From 516af2c0d9b35780243909ce5866d32191402d04 Mon Sep 17 00:00:00 2001 From: sidtohan Date: Sun, 24 Jul 2022 20:32:32 +0530 Subject: [PATCH 1/2] refactor(attendance): migrated the attendance module to typescript --- packages/attendance/.gitignore | 3 + packages/attendance/.storybook/main.js | 19 +- packages/attendance/jsconfig.json | 6 - packages/attendance/package.json | 8 +- .../composite/AttendanceComponent.tsx | 182 ++ .../src/components/composite/CalendarBar.tsx | 35 + .../composite/CalendarComponent.tsx | 150 ++ .../components/composite/CardListHolder.tsx | 26 + .../composite/MultipleAttendance.tsx | 320 +++ .../composite/PresentStudentsSummary.tsx | 67 + .../components/composite/ReportDetailData.tsx | 115 ++ .../src/components/composite/ReportFooter.tsx | 84 + .../components/composite/StudentCardsList.tsx | 94 + .../src/components/simple/AppShell.tsx | 6 + .../simple/AttendanceMessageComponent.tsx | 64 + .../components/simple/CalendarComponent.tsx | 189 ++ .../src/components/simple/Collapsible.tsx | 49 + .../simple/CompareAttendanceModal.tsx | 92 + .../simple/CompareReportHeading.tsx | 16 + .../src/components/simple/FourOFour.tsx | 23 + .../src/components/simple/GetIcon.tsx | 79 + .../src/components/simple/IconComponent.tsx | 21 + .../src/components/simple/Loader.tsx | 54 + .../components/simple/MarkAttendanceModal.tsx | 68 + .../src/components/simple/Message.tsx | 61 + .../simple/MultipleAttendanceFooter.tsx | 49 + .../components/simple/ReportDetailTitle.tsx | 22 + .../components/simple/ReportNavigation.tsx | 66 + .../src/components/simple/ReportSummary.tsx | 145 ++ .../src/components/simple/SmsModal.tsx | 44 + .../components/simple/TimeBar/Children.tsx | 54 + .../src/components/simple/TimeBar/Display.tsx | 87 + .../src/components/simple/TimeBar/TimeBar.tsx | 48 + packages/attendance/src/pages/Attendance.tsx | 340 ++++ .../src/pages/reports/CompareReport.tsx | 262 +++ .../attendance/src/pages/reports/Report.tsx | 125 ++ .../src/pages/reports/ReportDetail.tsx | 297 +++ .../src/pages/sms/MessageHistory/assets.tsx | 50 + .../src/pages/sms/MessageHistory/index.tsx | 251 +++ packages/attendance/src/pages/sms/SendSMS.tsx | 282 +++ .../src/services/calls/registryCalls.ts | 54 + packages/attendance/src/stories/Button.jsx | 54 - .../attendance/src/stories/Button.stories.jsx | 40 - packages/attendance/src/stories/Header.jsx | 67 - .../attendance/src/stories/Header.stories.jsx | 24 - .../src/stories/Introduction.stories.mdx | 219 --- packages/attendance/src/stories/Page.jsx | 87 - .../attendance/src/stories/Page.stories.jsx | 25 - .../stories/StudentCardsListstoriestemptsx | 61 + .../src/stories/assets/code-brackets.svg | 1 - .../attendance/src/stories/assets/colors.svg | 1 - .../src/stories/assets/comments.svg | 1 - .../src/stories/assets/direction.svg | 1 - .../attendance/src/stories/assets/flow.svg | 1 - .../attendance/src/stories/assets/plugin.svg | 1 - .../attendance/src/stories/assets/repo.svg | 1 - .../src/stories/assets/stackalt.svg | 1 - packages/attendance/src/stories/button.css | 30 - .../stories/composite/CalendarBar.stories.tsx | 30 + .../PresentStudentsSummary.stories.tsx | 35 + packages/attendance/src/stories/header.css | 32 - packages/attendance/src/stories/mockData.ts | 1009 ++++++++++ packages/attendance/src/stories/page.css | 69 - .../simple/CompareAttendanceModal.stories.tsx | 29 + .../simple/CompareReportHeading.stories.tsx | 15 + .../simple/ReportNavigation.stories.tsx | 7 + .../src/utils/customhooks/useAverage.ts | 11 + .../src/utils/customhooks/useDesignHook.ts | 60 + .../src/utils/customhooks/useGenderList.ts | 15 + .../src/utils/customhooks/usePAStudents.ts | 59 + .../src/utils/customhooks/useStudentIds.ts | 20 + .../utils/customhooks/useWithoutHolidays.ts | 40 + .../src/utils/functions/ColorTheme.ts | 5 + .../src/utils/functions/Constants.ts | 5 + .../src/utils/functions/CountReport.ts | 78 + .../src/utils/functions/FormatDate.ts | 33 + .../utils/functions/GetAttendanceReport.tsx | 25 + .../src/utils/functions/GetLastAttendance.ts | 6 + .../src/utils/functions/GetPercentage.ts | 44 + .../utils/functions/GetStatusFromManifest.ts | 11 + .../utils/functions/GetStudentsAttendance.ts | 38 + .../utils/functions/HandleAttendanceData.ts | 86 + .../src/utils/functions/HandleGenderList.ts | 13 + .../src/utils/functions/MarkAllAttendance.ts | 48 + .../src/utils/functions/StudentSorts.ts | 15 + .../utils/functions/TelemetryFactoryMapper.ts | 17 + .../attendance/src/utils/types/typeGuards.ts | 13 + packages/attendance/src/utils/types/types.ts | 9 + packages/attendance/tsconfig.json | 13 + packages/attendance/yarn.lock | 2 +- yarn.lock | 1734 ++++++++++++++++- 91 files changed, 7489 insertions(+), 759 deletions(-) delete mode 100644 packages/attendance/jsconfig.json create mode 100644 packages/attendance/src/components/composite/AttendanceComponent.tsx create mode 100644 packages/attendance/src/components/composite/CalendarBar.tsx create mode 100644 packages/attendance/src/components/composite/CalendarComponent.tsx create mode 100644 packages/attendance/src/components/composite/CardListHolder.tsx create mode 100644 packages/attendance/src/components/composite/MultipleAttendance.tsx create mode 100644 packages/attendance/src/components/composite/PresentStudentsSummary.tsx create mode 100644 packages/attendance/src/components/composite/ReportDetailData.tsx create mode 100644 packages/attendance/src/components/composite/ReportFooter.tsx create mode 100644 packages/attendance/src/components/composite/StudentCardsList.tsx create mode 100644 packages/attendance/src/components/simple/AppShell.tsx create mode 100644 packages/attendance/src/components/simple/AttendanceMessageComponent.tsx create mode 100644 packages/attendance/src/components/simple/CalendarComponent.tsx create mode 100644 packages/attendance/src/components/simple/Collapsible.tsx create mode 100644 packages/attendance/src/components/simple/CompareAttendanceModal.tsx create mode 100644 packages/attendance/src/components/simple/CompareReportHeading.tsx create mode 100644 packages/attendance/src/components/simple/FourOFour.tsx create mode 100644 packages/attendance/src/components/simple/GetIcon.tsx create mode 100644 packages/attendance/src/components/simple/IconComponent.tsx create mode 100644 packages/attendance/src/components/simple/Loader.tsx create mode 100644 packages/attendance/src/components/simple/MarkAttendanceModal.tsx create mode 100644 packages/attendance/src/components/simple/Message.tsx create mode 100644 packages/attendance/src/components/simple/MultipleAttendanceFooter.tsx create mode 100644 packages/attendance/src/components/simple/ReportDetailTitle.tsx create mode 100644 packages/attendance/src/components/simple/ReportNavigation.tsx create mode 100644 packages/attendance/src/components/simple/ReportSummary.tsx create mode 100644 packages/attendance/src/components/simple/SmsModal.tsx create mode 100644 packages/attendance/src/components/simple/TimeBar/Children.tsx create mode 100644 packages/attendance/src/components/simple/TimeBar/Display.tsx create mode 100644 packages/attendance/src/components/simple/TimeBar/TimeBar.tsx create mode 100644 packages/attendance/src/pages/Attendance.tsx create mode 100644 packages/attendance/src/pages/reports/CompareReport.tsx create mode 100644 packages/attendance/src/pages/reports/Report.tsx create mode 100644 packages/attendance/src/pages/reports/ReportDetail.tsx create mode 100644 packages/attendance/src/pages/sms/MessageHistory/assets.tsx create mode 100644 packages/attendance/src/pages/sms/MessageHistory/index.tsx create mode 100644 packages/attendance/src/pages/sms/SendSMS.tsx create mode 100644 packages/attendance/src/services/calls/registryCalls.ts delete mode 100644 packages/attendance/src/stories/Button.jsx delete mode 100644 packages/attendance/src/stories/Button.stories.jsx delete mode 100644 packages/attendance/src/stories/Header.jsx delete mode 100644 packages/attendance/src/stories/Header.stories.jsx delete mode 100644 packages/attendance/src/stories/Introduction.stories.mdx delete mode 100644 packages/attendance/src/stories/Page.jsx delete mode 100644 packages/attendance/src/stories/Page.stories.jsx create mode 100644 packages/attendance/src/stories/StudentCardsListstoriestemptsx delete mode 100644 packages/attendance/src/stories/assets/code-brackets.svg delete mode 100644 packages/attendance/src/stories/assets/colors.svg delete mode 100644 packages/attendance/src/stories/assets/comments.svg delete mode 100644 packages/attendance/src/stories/assets/direction.svg delete mode 100644 packages/attendance/src/stories/assets/flow.svg delete mode 100644 packages/attendance/src/stories/assets/plugin.svg delete mode 100644 packages/attendance/src/stories/assets/repo.svg delete mode 100644 packages/attendance/src/stories/assets/stackalt.svg delete mode 100644 packages/attendance/src/stories/button.css create mode 100644 packages/attendance/src/stories/composite/CalendarBar.stories.tsx create mode 100644 packages/attendance/src/stories/composite/PresentStudentsSummary.stories.tsx delete mode 100644 packages/attendance/src/stories/header.css create mode 100644 packages/attendance/src/stories/mockData.ts delete mode 100644 packages/attendance/src/stories/page.css create mode 100644 packages/attendance/src/stories/simple/CompareAttendanceModal.stories.tsx create mode 100644 packages/attendance/src/stories/simple/CompareReportHeading.stories.tsx create mode 100644 packages/attendance/src/stories/simple/ReportNavigation.stories.tsx create mode 100644 packages/attendance/src/utils/customhooks/useAverage.ts create mode 100644 packages/attendance/src/utils/customhooks/useDesignHook.ts create mode 100644 packages/attendance/src/utils/customhooks/useGenderList.ts create mode 100644 packages/attendance/src/utils/customhooks/usePAStudents.ts create mode 100644 packages/attendance/src/utils/customhooks/useStudentIds.ts create mode 100644 packages/attendance/src/utils/customhooks/useWithoutHolidays.ts create mode 100644 packages/attendance/src/utils/functions/ColorTheme.ts create mode 100644 packages/attendance/src/utils/functions/Constants.ts create mode 100644 packages/attendance/src/utils/functions/CountReport.ts create mode 100644 packages/attendance/src/utils/functions/FormatDate.ts create mode 100644 packages/attendance/src/utils/functions/GetAttendanceReport.tsx create mode 100644 packages/attendance/src/utils/functions/GetLastAttendance.ts create mode 100644 packages/attendance/src/utils/functions/GetPercentage.ts create mode 100644 packages/attendance/src/utils/functions/GetStatusFromManifest.ts create mode 100644 packages/attendance/src/utils/functions/GetStudentsAttendance.ts create mode 100644 packages/attendance/src/utils/functions/HandleAttendanceData.ts create mode 100644 packages/attendance/src/utils/functions/HandleGenderList.ts create mode 100644 packages/attendance/src/utils/functions/MarkAllAttendance.ts create mode 100644 packages/attendance/src/utils/functions/StudentSorts.ts create mode 100644 packages/attendance/src/utils/functions/TelemetryFactoryMapper.ts create mode 100644 packages/attendance/src/utils/types/typeGuards.ts create mode 100644 packages/attendance/src/utils/types/types.ts create mode 100644 packages/attendance/tsconfig.json diff --git a/packages/attendance/.gitignore b/packages/attendance/.gitignore index 4d29575d..a1bd39fa 100644 --- a/packages/attendance/.gitignore +++ b/packages/attendance/.gitignore @@ -21,3 +21,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +# environment +.env \ No newline at end of file diff --git a/packages/attendance/.storybook/main.js b/packages/attendance/.storybook/main.js index 4df7266c..8916d51d 100644 --- a/packages/attendance/.storybook/main.js +++ b/packages/attendance/.storybook/main.js @@ -1,5 +1,5 @@ module.exports = { - stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], + stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ "@storybook/addon-links", "@storybook/addon-essentials", @@ -10,4 +10,21 @@ module.exports = { core: { builder: "webpack5", }, + webpackFinal: async (config, { configType }) => { + config.resolve.alias = { + "react-native$": "react-native-web", + }; + + return config; + }, + typescript: { + check: true, + checkOptions: {}, + reactDocgen: "react-docgen-typescript", + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + propFilter: (prop) => + prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, + }, + }, }; diff --git a/packages/attendance/jsconfig.json b/packages/attendance/jsconfig.json deleted file mode 100644 index 5875dc5b..00000000 --- a/packages/attendance/jsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "src" - }, - "include": ["src"] -} diff --git a/packages/attendance/package.json b/packages/attendance/package.json index 785fa684..932b62f8 100644 --- a/packages/attendance/package.json +++ b/packages/attendance/package.json @@ -9,14 +9,17 @@ "@mui/material": "^5.4.0", "@react-navigation/drawer": "^6.1.8", "@shiksha/common-lib": "^1.0.0", + "@storybook/client-api": "^6.5.9", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", "axios": "^0.24.0", "expo-font": "^10.0.3", "i18next": "^21.6.7", + "ip": "^1.1.8", "moment": "^2.29.1", "native-base": "^3.2.2", + "prettier": "^2.6.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-i18next": "^11.15.3", @@ -81,15 +84,18 @@ "@storybook/addon-essentials": "^6.4.19", "@storybook/addon-interactions": "^6.4.19", "@storybook/addon-links": "^6.4.19", + "@storybook/addon-react-native-web": "^0.0.18", "@storybook/builder-webpack5": "^6.4.19", "@storybook/manager-webpack5": "^6.4.19", "@storybook/node-logger": "^6.4.19", "@storybook/preset-create-react-app": "^4.0.1", - "@storybook/react": "^6.4.19", + "@storybook/react": "^6.5.9", "@storybook/testing-library": "^0.0.9", "craco-module-federation": "^1.1.0", "external-remotes-plugin": "^1.0.0", "prettier": "^2.5.1", + "typescript": "^4.7.4", + "@types/react-native": "0.69.3", "webpack": "^5.69.1" } } diff --git a/packages/attendance/src/components/composite/AttendanceComponent.tsx b/packages/attendance/src/components/composite/AttendanceComponent.tsx new file mode 100644 index 00000000..b93b627b --- /dev/null +++ b/packages/attendance/src/components/composite/AttendanceComponent.tsx @@ -0,0 +1,182 @@ +import React, { useState, useEffect, Suspense } from "react"; +import { VStack, Box, Stack } from "native-base"; +import { useTranslation } from "react-i18next"; +import { calendar } from "@shiksha/common-lib"; + +import { colorTheme } from "utils/functions/ColorTheme"; + +// Components +// @ts-ignore +const Card = React.lazy(() => import("students/Card")); +import { CalendarComponent } from "components/simple/CalendarComponent"; +import { SmsModal } from "components/simple/SmsModal"; +import { MarkAttendanceModal } from "components/simple/MarkAttendanceModal"; +import { + CreateAttendance, + UpdateAttendance, +} from "services/calls/registryCalls"; + +export default function AttendanceComponent({ + type, + page, + student, + attendanceProp, + hidePopUpButton, + sms, + _card, + isEditDisabled, + _weekBox, + appName, + manifest, +}) { + const { t } = useTranslation(); + const teacherId = localStorage.getItem("id"); + const [attendance, setAttendance] = React.useState([]); + const [attendanceObject, setAttendanceObject] = React.useState({}); + const [days, setDays] = useState([]); + + const [showModal, setShowModal] = React.useState(false); + const [smsShowModal, setSmsShowModal] = React.useState(false); + const [loading, setLoading] = React.useState({}); + useEffect(() => { + if (typeof page === "object") { + setDays( + page.map((e) => + calendar( + e, + type, + manifest?.[ + "class_attendance.no_of_day_display_on_attendance_screen" + ] + ) + ) + ); + } else { + setDays([ + calendar( + page, + type, + manifest?.["class_attendance.no_of_day_display_on_attendance_screen"] + ), + ]); + } + async function getData() { + if (attendanceProp) { + setAttendance(attendanceProp); + } + setLoading({}); + } + getData(); + }, [page, attendanceProp, type]); + const markAttendance = async (dataObject) => { + setLoading({ + [dataObject.date + dataObject.id]: true, + }); + if (dataObject.attendanceId) { + await UpdateAttendance(dataObject); + const newData = attendance.filter( + (e) => + !(e.date === dataObject.date && e.studentId === dataObject.studentId) + ); + setAttendance([ + ...newData, + { ...dataObject, id: dataObject.attendanceId }, + ]); + setLoading({}); + setShowModal(false); + } else { + await CreateAttendance({ dataObject, student, teacherId }); + setAttendance([...attendance, dataObject]); + setLoading({}); + setShowModal(false); + } + }; + return ( + + + {!_card?.isHideStudentCard ? ( + + ( + // @ts-ignore + + )) + : false + } + /> + + ) : ( + "" + )} + {type !== "day" ? ( + + {days.map((day, index) => ( + // @ts-ignore + + ))} + + ) : ( + <> + )} + + + + <> + + ); +} diff --git a/packages/attendance/src/components/composite/CalendarBar.tsx b/packages/attendance/src/components/composite/CalendarBar.tsx new file mode 100644 index 00000000..c37b4fef --- /dev/null +++ b/packages/attendance/src/components/composite/CalendarBar.tsx @@ -0,0 +1,35 @@ +// Lib +import React, { FC } from "react"; +import { TimeBar } from "components/simple/TimeBar/TimeBar"; + +export interface ICalendarBar { + view?: string; + type?: string; + page?: number; + setPage?: Function; + activeColor?: string; + _box?: Object; +} + +export const CalendarBar: FC = ({ view, ...props }) => { + let CalendarBar = <>; + switch (view) { + case "month": + case "monthInDays": + props.type = "monthInDays"; + break; + case "week": + props.type = "week"; + break; + default: + props.type = "days"; + break; + } + // @ts-ignore + CalendarBar = ; + return CalendarBar; +}; + +CalendarBar.defaultProps = { + view: "days", +}; diff --git a/packages/attendance/src/components/composite/CalendarComponent.tsx b/packages/attendance/src/components/composite/CalendarComponent.tsx new file mode 100644 index 00000000..fe69412a --- /dev/null +++ b/packages/attendance/src/components/composite/CalendarComponent.tsx @@ -0,0 +1,150 @@ +import React from "react"; +import moment from "moment"; +import { Box, HStack, Pressable, Text, VStack } from "native-base"; +import { TouchableHighlight } from "react-native-web"; +import { GetIcon } from "components/simple/GetIcon"; +import Message from "../simple/Message"; +import { colorTheme, colors } from "utils/functions/ColorTheme"; +const CalendarComponent = ({ + monthDays, + type, + isIconSizeSmall, + sms, + setSmsObject, + student, + loading, + _weekBox, +}) => { + if (type === "month") { + return monthDays.map((week, index) => ( + 1 && monthDays.length - 1 !== index ? "1" : "0" + } + borderBottomColor={colorTheme.lightGray} + p={"2"} + {...(_weekBox?.[index] ? _weekBox[index] : {})} + > + {week.map((day, subIndex) => { + let isToday = moment().format("Y-MM-DD") === day.format("Y-MM-DD"); + let dateValue = day.format("Y-MM-DD"); + let smsItem = sms + .slice() + .reverse() + .find((e) => e.date === dateValue); + let smsIconProp = !isIconSizeSmall + ? { + _box: { py: 2, minW: "46px", alignItems: "center" }, + status: "CheckboxBlankCircleLineIcon", + } + : {}; + if (smsItem?.type && smsItem?.type === "Present") { + smsIconProp = { + ...smsIconProp, + status: smsItem?.type, + // @ts-ignore + type: smsItem?.status, + }; + } else if (smsItem?.type && smsItem?.type === "Absent") { + smsIconProp = { + ...smsIconProp, + status: smsItem?.type, + // @ts-ignore + type: smsItem?.status, + }; + } else if (day.day() === 0) { + // @ts-ignore + smsIconProp = { ...smsIconProp, status: "Holiday" }; + } else if (isToday) { + // @ts-ignore + smsIconProp = { ...smsIconProp, status: "Today" }; + } else if (moment().diff(day, "days") > 0) { + // @ts-ignore + smsIconProp = { ...smsIconProp, status: "Unmarked" }; + } + + return ( + + 1 && index ? 0 : !isIconSizeSmall ? 2 : 0 + } + textAlign="center" + > + {!isIconSizeSmall ? ( + + {index === 0 ? ( + + {day.format("ddd")} + + ) : ( + "" + )} + {day.format("DD")} + + ) : ( + + {day.format("dd")} + {day.format("D")} + + )} + + setSmsObject(smsItem)} + // onLongPress={(e) => { + // console.log({ e }); + // }} + > + + {loading && loading[dateValue + student.id] ? ( + + ) : ( + + )} + + + + ); + })} + + )); + } else { + return sms.map((item, index) => ( + // @ts-ignore + setSmsObject(item)}> + { + // @ts-ignore + + } + + )); + } +}; + +export default CalendarComponent; diff --git a/packages/attendance/src/components/composite/CardListHolder.tsx b/packages/attendance/src/components/composite/CardListHolder.tsx new file mode 100644 index 00000000..7aff2e97 --- /dev/null +++ b/packages/attendance/src/components/composite/CardListHolder.tsx @@ -0,0 +1,26 @@ +// Lib +import * as React from "react"; +import { Box, Stack } from "native-base"; +import { Collapsible } from "@shiksha/common-lib"; + +// Components +import { CompareReportHeading } from "components/simple/CompareReportHeading"; + +export const CardListHolder = ({ _textMed, _textSmall, ...props }) => { + return ( + + + + } + > + {props.children} + + + + ); +}; diff --git a/packages/attendance/src/components/composite/MultipleAttendance.tsx b/packages/attendance/src/components/composite/MultipleAttendance.tsx new file mode 100644 index 00000000..7b437e3b --- /dev/null +++ b/packages/attendance/src/components/composite/MultipleAttendance.tsx @@ -0,0 +1,320 @@ +// Lib +import React, { useState, useEffect, Suspense } from "react"; +import { + VStack, + Text, + HStack, + Box, + Actionsheet, + Stack, + Button, + ScrollView, +} from "native-base"; +import { useTranslation } from "react-i18next"; +import moment from "moment"; +import { + IconByName, + capture, + H2, + BodySmall, + Subtitle, + H1, + BodyLarge, + Caption, + Loading, +} from "@shiksha/common-lib"; +import { useNavigate } from "react-router-dom"; + +// Components +import { ReportSummary } from "components/simple/ReportSummary"; + +// Lazy Loading +// @ts-ignore +const Card = React.lazy(() => import("students/Card")); + +// Custom Hooks +import { usePAStudents } from "../../utils/customhooks/usePAStudents"; + +// Utils +import { GetLastAttendance } from "utils/functions/GetLastAttendance"; +import { colors, colorTheme } from "utils/functions/ColorTheme"; +import { MarkAllAttendance } from "utils/functions/MarkAllAttendance"; +import * as TelemetryFactoryMapper from "utils/functions/TelemetryFactoryMapper"; +import { AttendanceMessageComponent } from "components/simple/AttendanceMessageComponent"; +import { MultipleAttendanceFooter } from "components/simple/MultipleAttendanceFooter"; + +export const MultipleAttendance = ({ + students, + attendance, + getAttendance, + setLoading, + setAllAttendanceStatus, + allAttendanceStatus, + classObject, + isEditDisabled, + setIsEditDisabled, + isWithEditButton, + appName, + manifest, +}) => { + const { t } = useTranslation(); + const [showModal, setShowModal] = useState(false); + const teacherId = localStorage.getItem("id"); + const [presentStudents] = usePAStudents({ + students, + attendance, + type: "present", + }); + const navigate = useNavigate(); + const [startTime, setStartTime] = useState(); + const fullName = localStorage.getItem("fullName"); + useEffect(() => { + if (showModal) setStartTime(moment()); + }, [showModal]); + + const markAllAttendance = async () => { + setLoading(true); + if (typeof students === "object" && students.length > 0) { + await MarkAllAttendance({ + appName, + students, + getAttendance, + classObject, + }); + setLoading(false); + } else { + setLoading(false); + } + }; + + const modalClose = () => { + setShowModal(false); + setIsEditDisabled(true); + const telemetryData = TelemetryFactoryMapper.end({ + appName, + type: "Attendance-Summary-End", + groupID: classObject.id, + duration: moment().diff(startTime, "seconds"), + }); + capture("END", telemetryData); + setStartTime(moment()); + }; + + const saveViewReportHandler = () => { + setShowModal(true); + const telemetryData = TelemetryFactoryMapper.begin({ + appName, + type: "Attendance-Summary-Start", + groupID: classObject.id, + }); + capture("START", telemetryData); + }; + + return ( + <> + {isWithEditButton || !isEditDisabled ? ( + + + + + + {t("LAST_UPDATED_AT") + " " + GetLastAttendance(attendance)} + + + {t("ATTENDANCE_WILL_AUTOMATICALLY_SUBMIT")} + + + {!isEditDisabled ? ( + + {manifest?.[ + "class_attendance.mark_all_attendance_at_once" + ] === "true" ? ( + + ) : ( + + )} + + + ) : ( + + + + )} + + + modalClose()}> + + + + +

+ {t("ATTENDANCE_SUMMARY_REPORT")} +

+ + {classObject?.title ?? ""} + +
+ { + // @ts-ignore + modalClose()} + /> + } +
+
+ + + + +

+ {t("ATTENDANCE_SUBMITTED")} +

+
+
+ + +

{t("ATTENDANCE_SUMMARY")}

+ {moment().format("DD MMM, Y")} +
+ e.date === moment().format("YYYY-MM-DD") + ), + ], + footer: ( + + {t("ATTENDANCE_TAKEN_BY")} + + {fullName ? fullName : ""} + {" at "} + {GetLastAttendance(attendance)} + + + ), + }} + /> +
+ + + + + + + + + 100% {t("ATTENDANCE") + " " + t("THIS_WEEK")} + + { + // @ts-ignore + + } + + + {presentStudents.map((student, index) => + index < 3 ? ( + + + + + + ) : ( +
+ ) + )} +
+ {presentStudents?.length <= 0 ? ( + + No Student Has Achieved 100% Attendance This Week + + ) : ( + "" + )} + {presentStudents?.length > 3 ? ( + + ) : ( + "" + )} +
+
+
+ + + +
+
+
+
+ ) : ( + <> + + + )} + + ); +}; diff --git a/packages/attendance/src/components/composite/PresentStudentsSummary.tsx b/packages/attendance/src/components/composite/PresentStudentsSummary.tsx new file mode 100644 index 00000000..3a1d26a9 --- /dev/null +++ b/packages/attendance/src/components/composite/PresentStudentsSummary.tsx @@ -0,0 +1,67 @@ +// Lib +import * as React from "react"; +import { Box, VStack, Text } from "native-base"; +import { H2, Caption, Collapsible, Subtitle } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; + +// Components +import { ReportSummary } from "components/simple/ReportSummary"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; + +export const PresentStudentsSummary = ({ + students, + attendance, + compareAttendance, + thisTitle, + lastTitle, + page, + compare, + presentCount, +}) => { + const { t } = useTranslation(); + return ( + + + +

{t("SUMMARY")}

+ + {t("TOTAL")}: {students.length} {t("PRESENT")}:{presentCount} + + + } + > + + + + + {t("NOTES")} + {": "} + + {t("MONTHLY_REPORT_WILL_GENERATED_LAST_DAY_EVERY_MONTH")} + + +
+
+
+ ); +}; diff --git a/packages/attendance/src/components/composite/ReportDetailData.tsx b/packages/attendance/src/components/composite/ReportDetailData.tsx new file mode 100644 index 00000000..c1604fd2 --- /dev/null +++ b/packages/attendance/src/components/composite/ReportDetailData.tsx @@ -0,0 +1,115 @@ +// Lib +import * as React from "react"; +import { Suspense } from "react"; +import { Box, Stack, Text, VStack, FlatList, Button } from "native-base"; + +// Components +// @ts-ignore +const Card = React.lazy(() => import("students/Card")); +import { Collapsible } from "components/simple/Collapsible"; +import { useTranslation } from "react-i18next"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; +import { ReportDetailTitle } from "components/simple/ReportDetailTitle"; + +export const ReportDetailData = ({ data, calendarView, appName, type }) => { + const { t } = useTranslation(); + let _textMed: string; + let _textSmall: string; + let _borderColor: string; + let _bgColor: string; + let _textTitle; + let _color1: string; + let _color2: string; + if (type.toLowerCase() === "present") { + _textMed = `100% ${ + calendarView === "monthInDays" ? t("THIS_MONTH") : t("THIS_WEEK") + }`; + _textSmall = data.length + " " + t("STUDENTS"); + _borderColor = colorTheme.presentCardBorder; + _bgColor = colorTheme.presentCardBg; + _textTitle = "100%"; + _color1 = colorTheme.lightGray; + _color2 = colorTheme.presentCardText; + } else { + _textMed = t("ABSENT_CONSECUTIVE_3_DAYS"); + _textSmall = data.length + " " + t("STUDENTS"); + _borderColor = colorTheme.absentCardBorder; + _bgColor = colorTheme.absentCardBg; + _textTitle = `3 ${t("DAYS")}`; + _color1 = colorTheme.lightGray; + _color2 = colorTheme.absentCardText; + } + return ( + + + { + // @ts-ignore + + + + {_textMed} + + {_textSmall} + + + } + body={ + + + ( + + + + } + href={"/students/" + item.id} + hidePopUpButton + /> + + + )} + keyExtractor={(item) => item.id} + /> + + + + } + /> + } + + + ); +}; diff --git a/packages/attendance/src/components/composite/ReportFooter.tsx b/packages/attendance/src/components/composite/ReportFooter.tsx new file mode 100644 index 00000000..53c61d1f --- /dev/null +++ b/packages/attendance/src/components/composite/ReportFooter.tsx @@ -0,0 +1,84 @@ +// Lib +import * as React from "react"; +import { Box, VStack, Button, Text } from "native-base"; +import { Collapsible, H2, Caption, Subtitle } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; + +// Components +import { ReportSummary } from "components/simple/ReportSummary"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; + +export const ReportFooter = ({ + classes, + page, + calendarView, + students, + attendance, + navigate, + getAttendance, + makeDefaultCollapse, +}) => { + const { t } = useTranslation(); + return ( + + {classes.map((item, index) => ( + + getAttendance(item.id)} + // @ts-ignore + header={ + +

{item.name}

+ + {index % 2 === 0 ? t("MORNING") : t("MID_DAY_MEAL")} + +
+ } + > + + + + + {t("NOTES")} + {": "} + + {t("MONTHLY_REPORT_WILL_GENERATED_LAST_DAY_EVERY_MONTH")} + + + +
+
+ ))} +
+ ); +}; diff --git a/packages/attendance/src/components/composite/StudentCardsList.tsx b/packages/attendance/src/components/composite/StudentCardsList.tsx new file mode 100644 index 00000000..8ea914d6 --- /dev/null +++ b/packages/attendance/src/components/composite/StudentCardsList.tsx @@ -0,0 +1,94 @@ +// Lib +import * as React from "react"; +import { HStack, VStack, Box, FlatList, Button } from "native-base"; +import { BodyLarge, Caption } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; + +// Components +// @ts-ignore +const Card = React.lazy(() => import("students/Card")); + +// Utils +import { GetPercentage } from "utils/functions/GetPercentage"; +import { colorTheme } from "utils/functions/ColorTheme"; + +export const StudentCardsList = ({ + thisTitle, + lastTitle, + data, + appName, + attendance, + compareAttendance, + type, + _bgColor, + _textColor, + _textCompareColor, +}) => { + const { t } = useTranslation(); + return ( + + + { + let percentage, comparePercentage; + if (type === "absent") { + percentage = + GetPercentage(attendance, item, 6, "Absent", "count") + + " " + + t("DAYS"); + + comparePercentage = + GetPercentage(compareAttendance, item, 6, "Absent", "count") + + " " + + t("DAYS"); + } else { + percentage = GetPercentage(attendance, item) + "%"; + comparePercentage = GetPercentage(compareAttendance, item) + "%"; + } + return ( + + + + + {percentage} + {thisTitle} + + + + {comparePercentage} + + + {lastTitle} + + + + } + /> + + + ); + }} + keyExtractor={(item) => item.id} + /> + + + + ); +}; diff --git a/packages/attendance/src/components/simple/AppShell.tsx b/packages/attendance/src/components/simple/AppShell.tsx new file mode 100644 index 00000000..85dbfb07 --- /dev/null +++ b/packages/attendance/src/components/simple/AppShell.tsx @@ -0,0 +1,6 @@ +import * as React from "react"; + +function AppShell() { + return <>Render shell here ...; +} +export default AppShell; diff --git a/packages/attendance/src/components/simple/AttendanceMessageComponent.tsx b/packages/attendance/src/components/simple/AttendanceMessageComponent.tsx new file mode 100644 index 00000000..766ce808 --- /dev/null +++ b/packages/attendance/src/components/simple/AttendanceMessageComponent.tsx @@ -0,0 +1,64 @@ +// Lib +import * as React from "react"; +import { useTranslation } from "react-i18next"; +import { VStack, Button } from "native-base"; +import { BodyLarge, Caption } from "@shiksha/common-lib"; +import { capture } from "@shiksha/common-lib"; + +// Utils +import * as TelemetryFactoryMapper from "utils/functions/TelemetryFactoryMapper"; +import { colorTheme } from "utils/functions/ColorTheme"; + +export const AttendanceMessageComponent = ({ + navigate, + appName, + classObject, +}) => { + const { t } = useTranslation(); + return ( + + + {t("VIEW_SEND_ATTENDANCE_RELATED_MESSAGES_TO_STUDENTS")} + + {t("STUDENTS_ABSENT")} + + + + + + + ); +}; diff --git a/packages/attendance/src/components/simple/CalendarComponent.tsx b/packages/attendance/src/components/simple/CalendarComponent.tsx new file mode 100644 index 00000000..46958b46 --- /dev/null +++ b/packages/attendance/src/components/simple/CalendarComponent.tsx @@ -0,0 +1,189 @@ +// Lib +import colorTheme from "colorTheme"; +import * as React from "react"; +import moment from "moment"; +import { BodySmall, IconByName } from "@shiksha/common-lib"; +import { HStack, VStack, Box, Text, Badge } from "native-base"; +import { TouchableHighlight } from "react-native-web"; +import { GetIcon } from "components/simple/GetIcon"; + +// Utils +import { HandleAttendanceData } from "utils/functions/HandleAttendanceData"; + +export const CalendarComponent = ({ + monthDays, + type, + isIconSizeSmall, + isEditDisabled, + sms, + attendance, + student, + markAttendance, + setAttendanceObject, + setShowModal, + setSmsShowModal, + loading, + manifest, + _weekBox, +}) => { + let thisMonth = monthDays?.[1]?.[0]?.format("M"); + return monthDays.map((week, index) => ( + 1 && monthDays.length - 1 !== index ? "1" : "0" + } + borderBottomColor={colorTheme.lightGray} + {...(type === "day" ? { px: "2" } : { p: "2" })} + {..._weekBox} + > + {week.map((day, subIndex) => { + const { + isToday, + isAllowDay, + isHoliday, + dateValue, + smsDay, + attendanceItem, + attendanceIconProp, + attendanceType, + } = HandleAttendanceData({ + attendance, + day, + sms, + isIconSizeSmall, + student, + manifest, + }); + + return ( + + {smsDay?.type && isEditDisabled ? ( + + ) : ( + "" + )} + 1 && index ? 0 : !isIconSizeSmall ? 2 : 0} + textAlign="center" + > + {!isIconSizeSmall ? ( + + {index === 0 ? ( + + {day.format("ddd")} + + ) : ( + "" + )} + + {day.format("DD")} + + + ) : ( + + {day.format("dd")} + {day.format("D")} + + )} + + { + if (!isEditDisabled && isAllowDay && !isHoliday) { + const newAttendanceData = { + attendanceId: attendanceItem?.id ? attendanceItem.id : null, + id: attendanceItem?.id ? attendanceItem.id : null, + date: dateValue, + attendance: attendanceType, + studentId: student.id, + }; + markAttendance(newAttendanceData); + } + }} + onLongPress={(event) => { + if ( + !isEditDisabled && + day.format("M") === moment().format("M") && + day.day() !== 0 + ) { + setAttendanceObject({ + attendanceId: attendanceItem?.id ? attendanceItem.id : null, + date: dateValue, + attendance: attendanceItem?.attendance, + id: attendanceItem?.id, + studentId: student.id, + }); + setShowModal(true); + } + }} + > + + {loading[dateValue + student.id] ? ( + + ) : ( + + )} + + + {!isEditDisabled ? ( + smsDay?.type ? ( + setSmsShowModal(true)} + /> + ) : ( + + ) + ) : ( + "" + )} + + ); + })} + + )); +}; diff --git a/packages/attendance/src/components/simple/Collapsible.tsx b/packages/attendance/src/components/simple/Collapsible.tsx new file mode 100644 index 00000000..f30031c5 --- /dev/null +++ b/packages/attendance/src/components/simple/Collapsible.tsx @@ -0,0 +1,49 @@ +// Lib +import * as React from "react"; +import { Pressable, Box, HStack, Text, PresenceTransition } from "native-base"; +import { IconByName } from "@shiksha/common-lib"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; + +export const Collapsible = ({ + header, + body, + defaultCollapse, + isHeaderBold, + onPressFuction, +}) => { + const [collaps, setCollaps] = React.useState(defaultCollapse); + return ( + <> + { + setCollaps(!collaps); + onPressFuction(); + }} + > + + + + {header} + + + + + + {body} + + ); +}; diff --git a/packages/attendance/src/components/simple/CompareAttendanceModal.tsx b/packages/attendance/src/components/simple/CompareAttendanceModal.tsx new file mode 100644 index 00000000..0730c03d --- /dev/null +++ b/packages/attendance/src/components/simple/CompareAttendanceModal.tsx @@ -0,0 +1,92 @@ +// Lib +import * as React from "react"; +import { Box, Actionsheet, HStack, Stack, Pressable, Text } from "native-base"; +import { H2, IconByName, capture } from "@shiksha/common-lib"; + +// Utils +import * as TelemetryFactory from "utils/functions/TelemetryFactoryMapper"; +import { colorTheme } from "utils/functions/ColorTheme"; +import { useTranslation } from "react-i18next"; + +export const CompareAttendanceModal = ({ + showModal, + setShowModal, + appName, + navigate, + setCompare, + classId, +}) => { + const { t } = useTranslation(); + return ( + + + + +

+ {t("SELECT_CLASS_MARK_ATTENDANCE")} +

+
+ { + // @ts-ignore + setShowModal(false)} + _icon={{ size: "25" }} + /> + } +
+
+ + + {[ + { name: t("PREVIOUS_WEEK"), value: "week" }, + { name: t("PREVIOUS_MONTH"), value: "monthInDays" }, + // { name: t("BEST_PERFORMANCE"), value: "best-performance" }, + // { name: t("ANOTHER_SCHOOL"), value: "another-school" }, + { + name: t("DONT_SHOW_COMPARISON"), + value: "dont-show-comparison", + }, + ].map((item, index) => { + return ( + { + if (item?.value === "dont-show-comparison") { + navigate(-1); + } else { + setCompare(item.value); + setShowModal(false); + const telemetryData = TelemetryFactory.interact({ + appName, + type: "Attendance-Compare-Report", + groupID: classId, + typeOfComparison: item.name, + }); + capture("INTERACT", telemetryData); + } + }} + > + {item.name} + + ); + })} + +
+ ); +}; diff --git a/packages/attendance/src/components/simple/CompareReportHeading.tsx b/packages/attendance/src/components/simple/CompareReportHeading.tsx new file mode 100644 index 00000000..038b9302 --- /dev/null +++ b/packages/attendance/src/components/simple/CompareReportHeading.tsx @@ -0,0 +1,16 @@ +// Lib +import * as React from "react"; +import { VStack, Text } from "native-base"; + +export const CompareReportHeading = ({ _textMed, _textSmall }) => { + return ( + <> + + + {_textMed} + + {_textSmall} + + + ); +}; diff --git a/packages/attendance/src/components/simple/FourOFour.tsx b/packages/attendance/src/components/simple/FourOFour.tsx new file mode 100644 index 00000000..1909eb3c --- /dev/null +++ b/packages/attendance/src/components/simple/FourOFour.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Center } from "native-base"; + +export interface IFourOFour { + children?: React.ReactNode; +} +const FourOFour = () => { + return ( +
+
+ 404 +
+
+ ); +}; + +export default FourOFour; diff --git a/packages/attendance/src/components/simple/GetIcon.tsx b/packages/attendance/src/components/simple/GetIcon.tsx new file mode 100644 index 00000000..cb41e2d3 --- /dev/null +++ b/packages/attendance/src/components/simple/GetIcon.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { Box } from "native-base"; +import { IconByName } from "@shiksha/common-lib"; +import { colorTheme } from "utils/functions/ColorTheme"; + +export interface IGetIcon { + status?: string; + _box?: any; + color?: any; + _icon?: any; + type?: string; +} + +export const GetIcon: React.FC = ({ + status, + _box, + color, + _icon, + type, +}) => { + let icon = <>; + let iconProps = { fontSize: "xl", isDisabled: true, ..._icon }; + switch (status) { + case "Present": + icon = ( + + + + ); + break; + case "Absent": + icon = ( + + + + ); + break; + case "Late": + icon = ( + + + + ); + break; + case "Holiday": + icon = ( + + + + ); + break; + case "Unmarked": + icon = ( + + + + ); + break; + case "Today": + icon = ( + + + + ); + break; + default: + icon = ( + + + + ); + break; + } + return icon; +}; +export default GetIcon; diff --git a/packages/attendance/src/components/simple/IconComponent.tsx b/packages/attendance/src/components/simple/IconComponent.tsx new file mode 100644 index 00000000..10b6b411 --- /dev/null +++ b/packages/attendance/src/components/simple/IconComponent.tsx @@ -0,0 +1,21 @@ +// Lib +import * as React from "react"; +import { Box, HStack } from "native-base"; +import { BodyLarge, IconByName } from "@shiksha/common-lib"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; + +export const IconComponent = ({ lastTitle, iconName }) => { + return ( + + + {lastTitle} + { + // @ts-ignore + + } + + + ); +}; diff --git a/packages/attendance/src/components/simple/Loader.tsx b/packages/attendance/src/components/simple/Loader.tsx new file mode 100644 index 00000000..42794bcb --- /dev/null +++ b/packages/attendance/src/components/simple/Loader.tsx @@ -0,0 +1,54 @@ +import React, { FC } from "react"; +import { Center, Heading, Spinner, Text, VStack } from "native-base"; +import { useTranslation } from "react-i18next"; +import { useWindowSize } from "@shiksha/common-lib"; + +export interface IFourOFour { + children?: React.ReactNode; +} + +// Types +type LoaderType = { + success: string; + fail: string; +}; + +const Loader: FC = ({ success, fail }) => { + const { t } = useTranslation(); + const [width, height] = useWindowSize(); + return ( +
+
+ + + + + + {success ? success : ""} + + + {fail ? fail : ""} + + + {t("MARKING_ALL_STUDENTS_PRESENT") as string} + + + + +
+
+ ); +}; + +export default Loader; diff --git a/packages/attendance/src/components/simple/MarkAttendanceModal.tsx b/packages/attendance/src/components/simple/MarkAttendanceModal.tsx new file mode 100644 index 00000000..7a5c08f8 --- /dev/null +++ b/packages/attendance/src/components/simple/MarkAttendanceModal.tsx @@ -0,0 +1,68 @@ +// Lib +import * as React from "react"; +import { Actionsheet, HStack, Stack, Box, Pressable, Text } from "native-base"; +import { H2, IconByName } from "@shiksha/common-lib"; +import { GetIcon } from "./GetIcon"; + +// Utils +import { GetStatusFromManifest } from "utils/functions/GetStatusFromManifest"; +import { colorTheme } from "utils/functions/ColorTheme"; +import { useTranslation } from "react-i18next"; + +export const MarkAttendanceModal = ({ + manifest, + showModal, + setShowModal, + attendanceObject, + markAttendance, +}) => { + const status = GetStatusFromManifest(manifest); + const { t } = useTranslation(); + return ( + setShowModal(false)}> + + + +

{t("MARK_ATTENDANCE")}

+
+ { + // @ts-ignore + setShowModal(false)} + /> + } +
+
+ + {status.map((item) => { + return ( + { + if (attendanceObject.attendance !== item) { + markAttendance({ + ...attendanceObject, + attendance: item, + }); + } else { + setShowModal(false); + } + }} + > + + + + {t(item)} + + + + ); + })} + +
+ ); +}; diff --git a/packages/attendance/src/components/simple/Message.tsx b/packages/attendance/src/components/simple/Message.tsx new file mode 100644 index 00000000..3db930a7 --- /dev/null +++ b/packages/attendance/src/components/simple/Message.tsx @@ -0,0 +1,61 @@ +import React from "react"; +import moment from "moment"; +import { Box, Button, HStack, VStack } from "native-base"; +import { useTranslation } from "react-i18next"; +import { IconByName, H3, H4, capture } from "@shiksha/common-lib"; +// Utils +import { colors, colorTheme } from "utils/functions/ColorTheme"; +import * as TelemetryFactory from "utils/functions/TelemetryFactoryMapper"; + +const Message = ({ item, isDisableRetry }) => { + const { t } = useTranslation(); + return ( + + + + + {/*@ts-ignore*/} + +

+ {item.status === "Send" ? t("SENT") : t("FAILED")} +

+
+ {item.status !== "Send" && !isDisableRetry ? ( + + ) : ( + "" + )} +
+

+ {moment(item.date).format("Do MMM, hh:ssa")} +

+

{item.message}

+
+
+ ); +}; + +export default Message; diff --git a/packages/attendance/src/components/simple/MultipleAttendanceFooter.tsx b/packages/attendance/src/components/simple/MultipleAttendanceFooter.tsx new file mode 100644 index 00000000..2993ed37 --- /dev/null +++ b/packages/attendance/src/components/simple/MultipleAttendanceFooter.tsx @@ -0,0 +1,49 @@ +// Lib +import * as React from "react"; +import { VStack, Button } from "native-base"; +import { Caption } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; +import { colorTheme } from "utils/functions/ColorTheme"; + +export const MultipleAttendanceFooter = ({ + modalClose, + navigate, + classObject, +}) => { + const { t } = useTranslation(); + return ( + + + {t("ATTENDANCE_WILL_AUTOMATICALLY_SUBMIT")} + + + + + + + ); +}; diff --git a/packages/attendance/src/components/simple/ReportDetailTitle.tsx b/packages/attendance/src/components/simple/ReportDetailTitle.tsx new file mode 100644 index 00000000..9fb6a561 --- /dev/null +++ b/packages/attendance/src/components/simple/ReportDetailTitle.tsx @@ -0,0 +1,22 @@ +// Lib +import * as React from "react"; +import { VStack, Text } from "native-base"; +import { BodyLarge } from "@shiksha/common-lib"; + +export const ReportDetailTitle = ({ + _text1, + _text2, + _text3, + _color1, + _color2, +}) => { + return ( + + + {_text1} + {_text2} + {_text3} + + + ); +}; diff --git a/packages/attendance/src/components/simple/ReportNavigation.tsx b/packages/attendance/src/components/simple/ReportNavigation.tsx new file mode 100644 index 00000000..4a160cf2 --- /dev/null +++ b/packages/attendance/src/components/simple/ReportNavigation.tsx @@ -0,0 +1,66 @@ +// Lib +import * as React from "react"; +import { Menu, Button } from "native-base"; +import { IconByName } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; + +export const ReportNavigation = ({ calendarView, setCalendarView }) => { + const { t } = useTranslation(); + return ( + { + return ( + + ); + }} + > + setCalendarView("days")} + > + {t("TODAY_VIEW")} + + setCalendarView("week")} + > + {t("WEEK_VIEW")} + + setCalendarView("monthInDays")} + > + {t("MONTH_VIEW")} + + + ); +}; diff --git a/packages/attendance/src/components/simple/ReportSummary.tsx b/packages/attendance/src/components/simple/ReportSummary.tsx new file mode 100644 index 00000000..8afb9e87 --- /dev/null +++ b/packages/attendance/src/components/simple/ReportSummary.tsx @@ -0,0 +1,145 @@ +// Libs +import React from "react"; +import { Box, FlatList, HStack, Text, VStack } from "native-base"; +import { useTranslation } from "react-i18next"; + +// @ts-ignore +import manifest from "../../manifest.json"; +import { + ProgressBar, + calendar, + BodySmall, + Subtitle, + Caption, +} from "@shiksha/common-lib"; + +// Utilities +import { colorTheme } from "utils/functions/ColorTheme"; +import { PRESENT, ABSENT, UNMARKED } from "utils/functions/Constants"; +import { useStudentIds } from "utils/customhooks/useStudentIds"; +import { useDesignHook } from "utils/customhooks/useDesignHook"; +import { useWithoutHolidays } from "utils/customhooks/useWithoutHolidays"; +import { useGenderList } from "utils/customhooks/useGenderList"; +import { useAverage } from "utils/customhooks/useAverage"; +import { CountReport } from "utils/functions/CountReport"; + +export interface IReport { + students: Array; + attendance?: Array; + title?: Array<{ + name: string; + _text?: Object; + }>; + page?: object | number; + calendarView?: string; + footer?: React.ReactNode; +} +export const ReportSummary: React.FC = ({ + students, + attendance, + title, + page, + calendarView, + footer, +}) => { + const { t } = useTranslation(); + const { studentIds } = useStudentIds({ students }); + const { design } = useDesignHook({ attendance, page, calendarView, t }); + const { withoutHolidays } = useWithoutHolidays({ page, calendarView }); + const { genderList } = useGenderList({ students, t }); + const { isAverage } = useAverage({ calendarView }); + const fullName = localStorage.getItem("fullName"); + const status = manifest?.status ? manifest?.status : []; + + return ( + + + + {/* + + {design?.titleHeading} + */} + + + + {attendance && attendance.length ? ( + ( + + {attendance.length > 1 ? {item} : ""} + + {attendance.map((itemAttendance, index) => ( + + + {title && title.length && title[index] ? ( + title[index].name.split(" ").map((item, subIndex) => ( + + {item} + + )) + ) : ( + {item} + )} + + + { + // @ts-ignore + { + let statusCount = CountReport({ + isAverage, + gender: item, + attendanceType: subItem, + attendance: itemAttendance, + studentIds, + withoutHolidays: withoutHolidays[index], + students, + t, + }); + return { + name: subItem, + color: + subItem === PRESENT + ? colorTheme.attendancePresent + : subItem === ABSENT + ? colorTheme.attendanceAbsent + : subItem === UNMARKED + ? colorTheme.attendanceUnmarked + : colorTheme.gray, + value: statusCount, + }; + })} + /> + } + + + ))} + + + )} + keyExtractor={(item, index) => String(index)} + /> + ) : ( + "" + )} + + + {footer ? ( + footer + ) : ( + + {t("ATTENDANCE_TAKEN_BY")} + {fullName ? fullName : ""} + + )} + + + ); +}; diff --git a/packages/attendance/src/components/simple/SmsModal.tsx b/packages/attendance/src/components/simple/SmsModal.tsx new file mode 100644 index 00000000..d37c8a6f --- /dev/null +++ b/packages/attendance/src/components/simple/SmsModal.tsx @@ -0,0 +1,44 @@ +// Lib +import * as React from "react"; +import { Actionsheet, VStack, Button } from "native-base"; +import { Subtitle, BodyMedium } from "@shiksha/common-lib"; + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; + +export const SmsModal: React.FC = ({ + smsShowModal, + setSmsShowModal, + t, +}) => { + return ( + setSmsShowModal(false)}> + + {/* + setSmsShowModal(false)} + /> + */} + + + Message Sent to Parent + + Absent alert + + Hello Mr. B.K. Chaudhary, this is to inform you that your ward + Sheetal is absent in school on Wednesday, 12th of January 2022. + + + + + + ); +}; diff --git a/packages/attendance/src/components/simple/TimeBar/Children.tsx b/packages/attendance/src/components/simple/TimeBar/Children.tsx new file mode 100644 index 00000000..9eae7fdc --- /dev/null +++ b/packages/attendance/src/components/simple/TimeBar/Children.tsx @@ -0,0 +1,54 @@ +// Lib +import * as React from "react"; +import { H2, Caption } from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; +import { VStack } from "native-base"; +import moment from "moment"; + +// Utils +import { MomentUnionType } from "utils/types/types"; +import { FormatDate } from "utils/functions/FormatDate"; + +export const Children: React.FC<{ + type: string; + date: MomentUnionType; + page?: number; +}> = ({ type, date, page }) => { + const { t } = useTranslation(); + switch (true) { + case type === "monthInDays": + return ( + + + {/* + {t("THIS_MONTH")} + */} + + ); + case type === "week": + return ( + + + {t("THIS_WEEK")} + + ); + default: + return ( + +

+ {page === 0 + ? t("TODAY") + : page === 1 + ? t("TOMORROW") + : page === -1 + ? t("YESTERDAY") + : // @ts-ignore + moment(date).format("dddd")} +

+ + + +
+ ); + } +}; diff --git a/packages/attendance/src/components/simple/TimeBar/Display.tsx b/packages/attendance/src/components/simple/TimeBar/Display.tsx new file mode 100644 index 00000000..5e266aca --- /dev/null +++ b/packages/attendance/src/components/simple/TimeBar/Display.tsx @@ -0,0 +1,87 @@ +// Lib +import * as React from "react"; +import { Box, HStack, Text, useToast } from "native-base"; +import { IconByName } from "@shiksha/common-lib"; + +// Misc +import { colors, colorTheme } from "utils/functions/ColorTheme"; + +// Display Helper +export const Display: React.FC = ({ + children, + activeColor, + page, + setPage, + nextDisabled, + previousDisabled, + rightErrorText, + leftErrorText, + _box, +}) => { + const toast = useToast(); + return ( + + + + { + // @ts-ignore + { + if (leftErrorText) { + toast.show(leftErrorText); + } else if ( + typeof previousDisabled === "undefined" || + previousDisabled === false + ) { + setPage(page - 1); + } + }} + /> + } + + + + {children} + + + + { + // @ts-ignore + { + if (rightErrorText) { + toast.show(rightErrorText); + } else if ( + typeof nextDisabled === "undefined" || + nextDisabled === false + ) { + setPage(page + 1); + } + }} + /> + } + + + + ); +}; diff --git a/packages/attendance/src/components/simple/TimeBar/TimeBar.tsx b/packages/attendance/src/components/simple/TimeBar/TimeBar.tsx new file mode 100644 index 00000000..d494e68d --- /dev/null +++ b/packages/attendance/src/components/simple/TimeBar/TimeBar.tsx @@ -0,0 +1,48 @@ +// Lib +import * as React from "react"; +import { useState, useEffect } from "react"; +import { calendar } from "@shiksha/common-lib"; +import moment from "moment"; + +// Utilities +import { MomentUnionType, UseStateFunction } from "utils/types/types"; + +// Helper Components +import { Display } from "./Display"; +import { Children } from "./Children"; + +// Misc +import { colors, colorTheme } from "utils/functions/ColorTheme"; + +// Interace +export interface ITimeBar { + type: string; + page: number; + setPage: UseStateFunction; + activeColor?: string; + setActiveColor?: UseStateFunction; + previousDisabled?: boolean; + nextDisabled?: boolean; + leftErrorText?: any; + rightErrorText?: any; + _box?: any; +} + +export const TimeBar: React.FC = (props) => { + // Type decides if array or not + // etc + const [date, setDate] = useState(); + useEffect(() => { + if (props.type === "days") setDate(moment().add(props.page, "days")); + else setDate(calendar(props.page, props.type)); + if (props.setActiveColor) { + props.setActiveColor(props.page === 0 ? colors.primary : colorTheme.gray); + } + }, [props.page, props.setActiveColor]); + + return ( + + + + ); +}; diff --git a/packages/attendance/src/pages/Attendance.tsx b/packages/attendance/src/pages/Attendance.tsx new file mode 100644 index 00000000..b2ffc567 --- /dev/null +++ b/packages/attendance/src/pages/Attendance.tsx @@ -0,0 +1,340 @@ +// Lib +import { + capture, + telemetryFactory, + IconByName, + Layout, + calendar, + H1, + classRegistryService, + studentRegistryService, + BodySmall, + getApiConfig, +} from "@shiksha/common-lib"; +import { useTranslation } from "react-i18next"; +import manifest1 from "../manifest.json"; +import React, { useState, useEffect } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Box, FlatList, HStack, Stack, VStack, Button } from "native-base"; +import moment, { Moment } from "moment"; + +// Components +import { TimeBar } from "components/simple/TimeBar/TimeBar"; +import { GetAttendance } from "../services/calls/registryCalls"; +import { MultipleAttendance } from "components/composite/MultipleAttendance"; +import AttendanceComponent from "components/composite/AttendanceComponent"; +import Loader from "../components/simple/Loader"; +import FourOFour from "../components/simple/FourOFour"; + +// Utils +import { sortByAdmissionNo, sortByName } from "utils/functions/StudentSorts"; +import { colors, colorTheme } from "utils/functions/ColorTheme"; +import { PRESENT, ABSENT } from "utils/functions/Constants"; +import { isMoment, isMoment2DArray } from "utils/types/typeGuards"; + +export default function Attendance({ footerLinks, appName }) { + const { t } = useTranslation(); + const [weekPage, setWeekPage] = useState(0); + const [allAttendanceStatus, setAllAttendanceStatus] = useState<{ + success?: string; + fail?: string; + }>({}); + const [students, setStudents] = useState([]); + const [searchStudents, setSearchStudents] = useState([]); + const [classObject, setClassObject] = useState({}); + let { classId } = useParams(); + if (!classId) classId = "9eae88b7-1f2d-4561-a64f-871cf7a6b3f2"; + const [loading, setLoading] = useState(false); + const [attendance, setAttendance] = useState([]); + const [search, setSearch] = useState(""); + const [isEditDisabled, setIsEditDisabled] = useState(true); + const [sms, setSms] = useState([]); + const teacherId = localStorage.getItem("id"); + const [attendanceStartTime, setAttendanceStartTime] = useState(); + const [unmarkStudents, setUnmarkStudents] = useState([]); + const navigate = useNavigate(); + const [manifest, setManifest] = React.useState(); + + useEffect(() => { + let studentIds = attendance + .filter((e) => e.attendance !== "Unmarked") + .map((e) => + e?.studentId?.startsWith("1-") + ? e?.studentId?.replace("1-", "") + : e?.studentId + ); + setUnmarkStudents( + students.filter( + (e) => + !studentIds.includes( + e.id.startsWith("1-") ? e.id.replace("1-", "") : e.id + ) + ) + ); + }, [attendance, students]); + + useEffect(() => { + const filterStudent = students.filter((e) => + e?.fullName?.toLowerCase().match(search?.toLowerCase()) + ); + setSearchStudents(filterStudent); + }, [search, students]); + + useEffect(() => { + let ignore = false; + async function getData() { + const newManifest = await getApiConfig(); + setManifest(newManifest); + const studentData = await studentRegistryService.getAll({ classId }); + if ( + newManifest?.["attendance_card.order_of_attendance_card"] === + '"Alphabetically"' + ) { + setStudents(sortByName(studentData)); + } else { + setStudents(sortByAdmissionNo(studentData)); + } + setSearchStudents(studentData); + if (!ignore) + setClassObject(await classRegistryService.getOne({ id: classId })); + + setSms( + studentData.map((e, index) => ({ + type: index % 2 === 0 ? ABSENT : PRESENT, + date: moment().add(-1, "days").format("Y-MM-DD"), + studentId: e.id, + })) + ); + } + getData(); + return () => { + ignore = true; + }; + }, [classId]); + + useEffect(() => { + let ignore = false; + async function getData() { + if (!ignore) await getAttendance(); + } + getData(); + return () => { + ignore = true; + }; + }, [weekPage]); + + const getAttendance = async () => { + let weekdays = calendar(weekPage, "week"); + // type guard + if (isMoment(weekdays) || isMoment2DArray(weekdays)) return; + let params = { + fromDate: weekdays?.[0]?.format("Y-MM-DD"), + toDate: weekdays?.[weekdays.length - 1]?.format("Y-MM-DD"), + }; + const attendanceData = await GetAttendance(params); + setAttendance(attendanceData); + }; + + const newSetIsEditDisabled = (isEditDisabled) => { + setIsEditDisabled(isEditDisabled); + if (!isEditDisabled) { + const telemetryData = telemetryFactory.start({ + appName, + type: "Attendance-Start", + groupID: classId, + }); + capture("START", telemetryData); + setAttendanceStartTime(moment()); + } else { + const telemetryData = telemetryFactory.end({ + appName, + type: "Attendance-End", + groupID: classId, + duration: attendanceStartTime + ? moment().diff(attendanceStartTime, "seconds") + : 0, + percentage: + ((students?.length - unmarkStudents.length) / students?.length) * 100, + }); + capture("END", telemetryData); + setAttendanceStartTime(moment()); + } + }; + + if (!classObject && !classObject?.name) { + return ; + } + + if (loading) { + return ( + + ); + } + + return ( + // @ts-ignore + } + // @ts-ignore + onPress={(e) => navigate("/attendance/report")} + > + {t("Report")} + + ), + }} + _appBar={{ + languages: manifest1.languages, + isEnableSearchBtn: true, + setSearch: setSearch, + }} + subHeader={ + + +

{classObject?.title ? classObject?.title : ""}

+ + {t("TOTAL") + " " + students.length + " " + t("STUDENTS")} + +
+ { + // @ts-ignore + navigate(`/students/class/${classId}`)} + /> + } +
+ } + _subHeader={{ bg: colorTheme.attendanceCardBg }} + _footer={footerLinks} + > + + + + = + moment() + .startOf("week") + .diff( + moment( + manifest?.[ + "class_attendance.date_till_previous_attendance_allow" + ] + ).startOf("week"), + "week" + ) + } + nextDisabled={weekPage >= 0} + leftErrorText={ + !isEditDisabled + ? { + title: + "Please click on save before moving to the previous page.", + status: "error", + placement: "top", + } + : false + } + rightErrorText={ + !isEditDisabled + ? { + title: + "Please click on save before moving to the next page.", + status: "error", + placement: "top", + } + : false + } + type="week" + /> + + + + + + + ( + e.studentId === item.id)} + // @ts-ignore + withDate={1} + attendanceProp={attendance} + isEditDisabled={isEditDisabled} + appName + /> + )} + keyExtractor={(item, index) => (item?.id ? item?.id : index)} + /> + + +
+ ); +} diff --git a/packages/attendance/src/pages/reports/CompareReport.tsx b/packages/attendance/src/pages/reports/CompareReport.tsx new file mode 100644 index 00000000..a99be4b1 --- /dev/null +++ b/packages/attendance/src/pages/reports/CompareReport.tsx @@ -0,0 +1,262 @@ +// Lib +import moment from "moment"; +import { Box, FlatList, HStack, Stack, VStack } from "native-base"; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { + IconByName, + Layout, + Collapsible, + getStudentsPresentAbsent, + getUniqAttendance, + calendar, + H2, + Caption, +} from "@shiksha/common-lib"; +import { useNavigate, useParams } from "react-router-dom"; + +// Components +// Lazy Loading +// @ts-ignore +import { CalendarBar } from "components/composite/CalendarBar"; +import AttendanceComponent from "components/composite/AttendanceComponent"; +import { IconComponent } from "components/simple/IconComponent"; +import { PresentStudentsSummary } from "components/composite/PresentStudentsSummary"; +import { StudentCardsList } from "components/composite/StudentCardsList"; +import { CardListHolder } from "components/composite/CardListHolder"; +import { CompareAttendanceModal } from "components/simple/CompareAttendanceModal"; +import { isMoment, isMoment2DArray } from "utils/types/typeGuards"; +import { CompareReportHeading } from "components/simple/CompareReportHeading"; + +// Services +import { + DefaultStudents, + GetAllStudents, + GetAttendance, + GetOneClass, +} from "../../services/calls/registryCalls"; +import manifest from "../../manifest.json"; + +// Utils +import { GetPercentage } from "utils/functions/GetPercentage"; +import { colorTheme } from "utils/functions/ColorTheme"; + +export default function ClassReportDetail({ footerLinks, appName }) { + const { t } = useTranslation(); + const [page, setPage] = useState(0); + const { classId } = useParams(); + const navigate = useNavigate(); + const [classObject, setClassObject] = useState({}); + const [students, setStudents] = useState([]); + const [attendance, setAttendance] = useState([]); + const [compare, setCompare] = useState(); + const [compareAttendance, setCompareAttendance] = useState([]); + const [showModal, setShowModal] = useState(true); + const [presentStudents, setPresentStudents] = useState([]); + const [absentStudents, setAbsentStudents] = useState([]); + const [presentCount, setPresentCount] = useState(0); + const [thisTitle, setThisTitle] = useState(""); + const [lastTitle, setLastTitle] = useState(""); + + const teacherId = localStorage.getItem("id"); + + useEffect(() => { + let ignore = false; + + const getData = async () => { + if (!ignore && compare) { + setThisTitle(compare === "week" ? t("THIS_WEEK") : t("THIS_MONTH")); + setLastTitle(compare === "week" ? t("LAST_WEEK") : t("LAST_MONTH")); + let classObj = await GetOneClass(classId); + const studentData = await GetAllStudents(classId); + setClassObject(classObj); + setStudents(studentData); + const { attendanceData, workingDaysCount } = await getAttendance( + 0, + true + ); + setAttendance(attendanceData); + setPresentCount( + getUniqAttendance(attendanceData, "Present", studentData).length + ); + const newCompareAttendance = await getAttendance(-1); + setCompareAttendance(newCompareAttendance); + + setPresentStudents( + await DefaultStudents( + getStudentsPresentAbsent( + attendanceData, + studentData, + workingDaysCount + ) + ) + ); + setAbsentStudents( + await DefaultStudents( + getStudentsPresentAbsent(attendanceData, studentData, 3, "Absent") + ) + ); + } + }; + getData(); + return () => { + ignore = true; + }; + }, [classId, compare, page, t]); + + const getAttendance = async (newPage = 0, withCount = false) => { + let weekdays = calendar( + newPage ? newPage + page : page, + ["days", "week"].includes(compare) ? "week" : compare + ); + if (isMoment(weekdays) || isMoment2DArray(weekdays)) return {}; + let workingDaysCount = weekdays.filter((e) => e.day())?.length; + let params = { + fromDate: weekdays?.[0]?.format("Y-MM-DD"), + toDate: weekdays?.[weekdays.length - 1]?.format("Y-MM-DD"), + }; + if (withCount) { + return { attendanceData: await GetAttendance(params), workingDaysCount }; + } else { + return await GetAttendance(params); + } + }; + + return ( + // @ts-ignore + + ), + }} + _appBar={{ languages: manifest.languages }} + subHeader={ + +

{classObject.name}

+ + {t("TOTAL")}: {students.length} {t("PRESENT")}:{presentCount} + +
+ } + _subHeader={{ bg: colorTheme.bgReportCard, mb: 1 }} + _footer={footerLinks} + > + {compare ? ( + + + + + { + // @ts-ignore + + } + + + + + + + + + + + + + } + > + ( + + )} + keyExtractor={(item) => item.id} + /> + + + + + ) : ( + + )} +
+ ); +} diff --git a/packages/attendance/src/pages/reports/Report.tsx b/packages/attendance/src/pages/reports/Report.tsx new file mode 100644 index 00000000..e04b7d95 --- /dev/null +++ b/packages/attendance/src/pages/reports/Report.tsx @@ -0,0 +1,125 @@ +// Libs +import { VStack } from "native-base"; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { + Layout, + capture, + calendar, + classRegistryService, + H1, +} from "@shiksha/common-lib"; +import { useNavigate } from "react-router-dom"; +import manifest from "../../manifest.json"; + +// Components +import { CalendarBar } from "../../components/composite/CalendarBar"; +// Utils +import { GetAllStudents } from "../../services/calls/registryCalls"; +import { colorTheme } from "utils/functions/ColorTheme"; +import { ReportNavigation } from "components/simple/ReportNavigation"; +import { GetAttendanceReport } from "utils/functions/GetAttendanceReport"; +import { ReportFooter } from "components/composite/ReportFooter"; + +export default function Report({ footerLinks }) { + const { t } = useTranslation(); + const [page, setPage] = useState(0); + const [classes, setClasses] = useState([]); + const teacherId = localStorage.getItem("id"); + const [students, setStudents] = useState([]); + const [attendance, setAttendance] = useState({}); + const [calendarView, setCalendarView] = useState("days"); + const [makeDefaultCollapse, setMakeDefaultCollapse] = useState(false); + const titleName = t("ATTENDANCE_REPORTS"); + const navigate = useNavigate(); + + useEffect(() => { + let ignore = false; + + const getData = async () => { + let responseClass = await classRegistryService.getAll({ + teacherId: teacherId, + type: "class", + role: "teacher", + }); + if (!ignore) { + if (responseClass[0].id) getAttendance(responseClass[0].id); + setClasses(responseClass); + } + }; + getData(); + return () => { + ignore = true; + }; + }, [teacherId]); + + useEffect(() => { + let ignore = false; + if (!ignore) { + if (classes[0]?.id) getAttendance(classes[0].id); + setMakeDefaultCollapse(makeDefaultCollapse); + } + return () => { + ignore = true; + }; + }, [page, calendarView]); + + useEffect(() => { + // @ts-ignore + capture("PAGE"); + }, []); + + const getAttendance = async (classId) => { + const attendanceData = await GetAttendanceReport( + page, + calendar, + calendarView + ); + setAttendance({ ...attendance, [classId]: attendanceData }); + const studentData = await GetAllStudents(classId); + setStudents({ ...students, [classId]: studentData }); + }; + + return ( + // @ts-ignore + + {titleName.split(" ").map((item, subIndex) => ( +

{item}

+ ))} + + ), + iconComponent: ( + + ), + }} + _appBar={{ languages: manifest.languages }} + subHeader={ + + } + _subHeader={{ bg: colorTheme.reportCardBackg }} + _footer={footerLinks} + > + +
+ ); +} diff --git a/packages/attendance/src/pages/reports/ReportDetail.tsx b/packages/attendance/src/pages/reports/ReportDetail.tsx new file mode 100644 index 00000000..1b1ffa82 --- /dev/null +++ b/packages/attendance/src/pages/reports/ReportDetail.tsx @@ -0,0 +1,297 @@ +// Lib +import moment, { Moment } from "moment"; +import { Box, FlatList, HStack, Stack, Text, VStack } from "native-base"; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { CalendarBar } from "components/composite/CalendarBar"; +import AttendanceComponent from "../../components/composite/AttendanceComponent"; +import { ReportSummary } from "../../components/simple/ReportSummary"; +import { Link, useNavigate, useParams } from "react-router-dom"; +import { Collapsible } from "components/simple/Collapsible"; + +import { + IconByName, + Layout, + getStudentsPresentAbsent, + getUniqAttendance, + capture, + telemetryFactory, + calendar, + classRegistryService, + studentRegistryService, + overrideColorTheme, + BodyLarge, + H2, + Caption, + Subtitle, +} from "@shiksha/common-lib"; +import colorTheme from "../../colorTheme"; +import { GetAttendanceReport } from "utils/functions/GetAttendanceReport"; +import { ReportDetailData } from "components/composite/ReportDetailData"; +import { CompareReportHeading } from "components/simple/CompareReportHeading"; +import { usePAStudents } from "utils/customhooks/usePAStudents"; + +const colors = overrideColorTheme(colorTheme); + +export default function ReportDetail({ footerLinks, appName }) { + const { t } = useTranslation(); + const [page, setPage] = useState(0); + const { classId, view } = useParams(); + const [classObject, setClassObject] = useState({}); + const [students, setStudents] = useState([]); + const [attendance, setAttendance] = useState([]); + const [attendanceForReport, setAttendanceForReport] = useState([]); + const [presentStudents] = usePAStudents({ + students, + attendance, + type: "present", + }); + const [absentStudents] = useState({ + students, + attendance, + type: "absent", + }); + const [calendarView] = useState( + view + ? ["month", "monthInDays"].includes(view) + ? "monthInDays" + : ["week", "weeks"].includes(view) + ? "week" + : "days" + : "days" + ); + const teacherId = localStorage.getItem("id"); + const [attendanceStartTime, setAttendanceStartTime] = useState(); + const navigate = useNavigate(); + + useEffect(() => { + const telemetryData = telemetryFactory.start({ + appName, + type: "Attendance-Full-Report-Start", + groupID: classId, + }); + capture("START", telemetryData); + setAttendanceStartTime(moment()); + }, []); + + useEffect(() => { + let ignore = false; + const getData = async () => { + let classObj = await classRegistryService.getOne({ id: classId }); + if (!ignore) setClassObject(classObj); + const studentData = await studentRegistryService.getAll({ classId }); + setStudents(studentData); + await getAttendance(); + await getAttendanceForReport(); + }; + getData(); + return () => { + ignore = true; + }; + }, [classId, page]); + + const getAttendance = async () => { + const attendanceData = await GetAttendanceReport( + page, + calendar, + calendarView, + "getAttendance" + ); + setAttendance(attendanceData); + }; + + const getAttendanceForReport = async () => { + const attendanceData = await GetAttendanceReport( + page, + calendar, + calendarView, + "getAttendanceForReport" + ); + setAttendanceForReport(attendanceData); + }; + + const handleBackButton = () => { + const telemetryData = telemetryFactory.end({ + appName, + type: "Attendance-Full-Report-End", + groupID: classId, + duration: attendanceStartTime + ? moment().diff(attendanceStartTime, "seconds") + : 0, + }); + capture("END", telemetryData); + navigate(-1); + }; + + return ( + // @ts-ignore + + + + {t("COMPARE")} + { + //@ts-ignore + + } + + + + ), + }} + subHeader={ + +

+ {(classObject?.name ? "Class " + classObject?.name : "") + + (classObject?.section ? " Sec " + classObject?.section : "")} +

+ + {t("TOTAL")}: {students.length} {t("PRESENT")}: + { + attendanceForReport.filter((e) => e.attendance === "Present") + .length + } + +
+ } + _subHeader={{ bg: colorTheme.reportCardBackg, mb: 1 }} + _footer={footerLinks} + > + + + + + { + // @ts-ignore + + } + + + + + { + // @ts-ignore + +

{t("SUMMARY")}

+ + {t("TOTAL")}: {students.length} {t("PRESENT")}: + { + getUniqAttendance( + attendanceForReport, + "Present", + students + ).length + } + +
+ } + body={ + + + + {/* @ts-ignore*/} + + {t("NOTES")} + {": "} + + {t("MONTHLY_REPORT_WILL_GENRRATED_LAST_DAY_EVERY_MONTH")} + + + } + /> + } + + + + + + + { + // @ts-ignore + + } + body={ + ( + + )} + keyExtractor={(item) => item.id} + /> + } + /> + } + + + +
+ ); +} diff --git a/packages/attendance/src/pages/sms/MessageHistory/assets.tsx b/packages/attendance/src/pages/sms/MessageHistory/assets.tsx new file mode 100644 index 00000000..c2eff798 --- /dev/null +++ b/packages/attendance/src/pages/sms/MessageHistory/assets.tsx @@ -0,0 +1,50 @@ +import moment from "moment"; + +export const sms = (student) => { + return [ + { + status: "Send", + type: "Present", + date: moment().add(-1, "days").format("Y-MM-DD"), + message: + "Hello Mr. " + + student.fathersName + + ", this is to inform you that your ward " + + student.firstName + + " is present in school on Wednesday, 12th of January 2022.", + }, + { + status: "Failed", + type: "Present", + date: moment().add(-2, "days").format("Y-MM-DD"), + message: + "Hello Mr. " + + student.fathersName + + ", this is to inform you that your ward " + + student.firstName + + " is absent in school on Wednesday, 12th of January 2022.", + }, + { + status: "Failed", + type: "Absent", + date: moment().add(-3, "days").format("Y-MM-DD"), + message: + "Hello Mr. " + + student.fathersName + + ", this is to inform you that your ward " + + student.firstName + + " is absent in school on Wednesday, 12th of January 2022.", + }, + { + status: "Send", + type: "Absent", + date: moment().add(-6, "days").format("Y-MM-DD"), + message: + "Hello Mr. " + + student.fathersName + + ", this is to inform you that your ward " + + student.firstName + + " is present in school on Wednesday, 12th of January 2022.", + }, + ]; +}; diff --git a/packages/attendance/src/pages/sms/MessageHistory/index.tsx b/packages/attendance/src/pages/sms/MessageHistory/index.tsx new file mode 100644 index 00000000..01029ce3 --- /dev/null +++ b/packages/attendance/src/pages/sms/MessageHistory/index.tsx @@ -0,0 +1,251 @@ +import { + Actionsheet, + Box, + Button, + HStack, + Pressable, + Stack, + Text, + VStack, +} from "native-base"; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { useParams } from "react-router-dom"; +import { + IconByName, + capture, + Layout, + H3, + H2, + H4, + calendar, + studentRegistryService, + overrideColorTheme, +} from "@shiksha/common-lib"; +import { CalendarBar } from "components/composite/CalendarBar"; +import manifest from "../../../manifest.json"; +import { sms } from "./assets"; +import CalendarComponent from "../../../components/composite/CalendarComponent"; +import Message from "../../../components/simple/Message"; +import colorTheme from "../../../colorTheme"; + +const colors = overrideColorTheme(colorTheme); + +export default function MessageHistory({ footerLinks }) { + const { t } = useTranslation(); + const [weekPage, setWeekPage] = useState(0); + const [calendarView, setCalendarView] = useState(); + const { studentId } = useParams(); + const [studentObject, setStudentObject] = useState({}); + const [search, setSearch] = useState(); + const [searchSms, setSearchSms] = useState([]); + const [smsObject, setSmsObject] = useState({}); + const [showModal, setShowModal] = useState(false); + const [weekDays, setWeekDays] = useState([]); + + useEffect(() => { + let ignore = false; + const getData = async () => { + if (!ignore) { + //@ts-ignore + setWeekDays(calendar(weekPage, calendarView)); + } + }; + getData(); + }, [weekPage, calendarView]); + + useEffect(() => { + let ignore = false; + const getData = async () => { + let student = await studentRegistryService.getOne({ id: studentId }); + if (!ignore) { + setStudentObject(student); + setSearchSms(sms(student)); + } + }; + getData(); + }, [studentId]); + + useEffect(() => { + //@ts-ignore + capture("PAGE"); + }, []); + + return ( + // @ts-ignore + + + + + + + + +

{t("SELECT_VIEW")}

+
+ {/*@ts-ignore*/} + setShowModal(false)} + /> +
+
+ + + {[ + { name: t("TODAY_VIEW"), value: "day" }, + { name: t("WEEK_VIEW"), value: "week" }, + { name: t("MONTH_VIEW"), value: "monthInDays" }, + { name: t("CHOOSE_DATE"), value: "date" }, + ].map((item, index) => { + return ( + { + // @ts-ignore + setCalendarView(item.value); + setShowModal(false); + }} + > + {item.name} + + ); + })} + +
+
+ + } + _subHeader={{ bg: colorTheme.studentCardBg }} + _footer={footerLinks} + > + + + +

{studentObject.fullName}

+
+
+ + + +

{t("SEND_MESSAGE")}

+
+
+ + {/*@ts-ignore*/} + + + + + + +

+ { + //@ts-ignore + smsObject?.status === "Send" + ? t("MESSAGE_SENT") + : t("MESSAGE_FAILED") + } +

+

{smsObject?.date}

+
+ {/*@ts-ignore*/} + setSmsObject({})} + /> +
+
+ + + + + + + +
+
+
+
+ ); +} diff --git a/packages/attendance/src/pages/sms/SendSMS.tsx b/packages/attendance/src/pages/sms/SendSMS.tsx new file mode 100644 index 00000000..0a5e968c --- /dev/null +++ b/packages/attendance/src/pages/sms/SendSMS.tsx @@ -0,0 +1,282 @@ +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate, useParams } from "react-router-dom"; +import { + Box, + Button, + FlatList, + HStack, + Stack, + Text, + VStack, +} from "native-base"; +import { + GetAllStudents, + GetAttendance, + GetOneClass, +} from "../../services/calls/registryCalls"; +import { TimeBar } from "components/simple/TimeBar/TimeBar"; +import { + IconByName, + Layout, + Collapsible, + telemetryFactory, + H2, + H3, + capture, + calendar, + Caption, + Subtitle, + BodyLarge, +} from "@shiksha/common-lib"; + +// Components +// @ts-ignore +const Card = React.lazy(() => import("students/Card")); + +// Utils +import { colorTheme } from "utils/functions/ColorTheme"; +import { PRESENT, ABSENT } from "utils/functions/Constants"; +import { usePAStudents } from "utils/customhooks/usePAStudents"; +import { isMoment, isMoment2DArray } from "utils/types/typeGuards"; + +export default function SendSMS({ footerLinks, appName }) { + const { t } = useTranslation(); + const [datePage, setDatePage] = useState(0); + const { classId } = useParams(); + const [classObject, setClassObject] = useState({}); + const teacherId = localStorage.getItem("id"); + const [students, setStudents] = useState([]); + const navigate = useNavigate(); + const [attendance, setAttendance] = useState([]); + const [presentStudents] = usePAStudents({ + students, + attendance, + type: "present", + }); + const [absentStudents] = usePAStudents({ + students, + attendance, + type: "absent", + }); + + useEffect(() => { + let ignore = false; + + const getData = async () => { + let classObj = await GetOneClass(classId); + if (!ignore) setClassObject(classObj); + const studentData = await GetAllStudents(classId); + setStudents(studentData); + await getAttendance(); + }; + getData(); + return () => { + ignore = true; + }; + }, [classId]); + + const getAttendance = async () => { + let weekdays = calendar(datePage, "week"); + if (isMoment(weekdays) || isMoment2DArray(weekdays)) return; + let params = { + fromDate: weekdays?.[0]?.format("YYYY-MM-DD"), + toDate: weekdays?.[weekdays.length - 1]?.format("YYYY-MM-DD"), + attendance: PRESENT, + }; + const attendanceData = await GetAttendance(params); + setAttendance(attendanceData); + }; + + return ( + //@ts-ignore + + {(classObject?.name ? "Class " + classObject?.name : "") + + " • " + + (classObject?.section ? " Sec " + classObject?.section : "")} + + ), + _subHeading: { fontWeight: 500 }, + }} + subHeader={ + + + { + //@ts-ignore + + } + + } + _subHeader={{ bg: colorTheme.attendanceCardBg, mb: 1 }} + _footer={footerLinks} + > + + + + {(classObject?.name ? "Class " + classObject?.name : "") + + " • " + + (classObject?.section ? " Sec " + classObject?.section : "")} + + + {t("TOTAL")}: {students.length} • {t("PRESENT")}: + {attendance?.length} + + + + + + +

+ 100% {t("THIS_WEEK")} +

+ + {presentStudents?.length + " " + t("STUDENTS")} + +
+ + } + > + + + ( + + +

+ {item.fullName} + + + 100% + +

+
+ } + /> +
+ )} + keyExtractor={(item, index) => String(index)} + /> + +
+ + + + + + + + +

{t("ABSENT_CONSECUTIVE_3_DAYS")}

+ + {absentStudents?.length + " " + t("STUDENTS")} + +
+ + } + > + + + ( + + +

+ {item.fullName} + + + 3 {t("DAYS")} + +

+
+ } + /> +
+ )} + keyExtractor={(item, index) => String(index)} + /> + + + + + + + + + + {t("NOTES") + ": "} + + {t("SMS_WILL_AUTOMATICALLY_SENT")} + + + + + + + + +
+ ); +} diff --git a/packages/attendance/src/services/calls/registryCalls.ts b/packages/attendance/src/services/calls/registryCalls.ts new file mode 100644 index 00000000..f0356441 --- /dev/null +++ b/packages/attendance/src/services/calls/registryCalls.ts @@ -0,0 +1,54 @@ +// Lib +import { + classRegistryService, + studentRegistryService, + attendanceRegistryService, +} from "@shiksha/common-lib"; + +export const GetAttendance = async (params): Promise => { + return await attendanceRegistryService.getAll(params); +}; + +export const DefaultStudents = async (data): Promise => { + return await studentRegistryService.setDefaultValue(data); +}; + +export const MultipleAttendance = async (data): Promise => { + return await attendanceRegistryService.multipal(data); +}; + +export const UpdateAttendance = async (data): Promise => { + return await attendanceRegistryService.update( + { + id: data.attendanceId, + attendance: data.attendance, + }, + { + onlyParameter: ["attendance", "id", "date", "classId"], + } + ); +}; + +export const CreateAttendance = async ({ + dataObject, + student, + teacherId, +}): Promise => { + return await attendanceRegistryService.create({ + studentId: student.id, + date: dataObject.date, + attendance: dataObject.attendance, + attendanceNote: "Test", + classId: student.currentClassID, + subjectId: "History", + teacherId: teacherId, + }); +}; + +export const GetOneClass = async (id) => { + return await classRegistryService.getOne({ id }); +}; + +export const GetAllStudents = async (id) => { + return await studentRegistryService.getAll({ classId: id }); +}; diff --git a/packages/attendance/src/stories/Button.jsx b/packages/attendance/src/stories/Button.jsx deleted file mode 100644 index 19c96568..00000000 --- a/packages/attendance/src/stories/Button.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import "./button.css"; - -/** - * Primary UI component for user interaction - */ -export const Button = ({ primary, backgroundColor, size, label, ...props }) => { - const mode = primary - ? "storybook-button--primary" - : "storybook-button--secondary"; - return ( - - ); -}; - -Button.propTypes = { - /** - * Is this the principal call to action on the page? - */ - primary: PropTypes.bool, - /** - * What background color to use - */ - backgroundColor: PropTypes.string, - /** - * How large should the button be? - */ - size: PropTypes.oneOf(["small", "medium", "large"]), - /** - * Button contents - */ - label: PropTypes.string.isRequired, - /** - * Optional click handler - */ - onClick: PropTypes.func, -}; - -Button.defaultProps = { - backgroundColor: null, - primary: false, - size: "medium", - onClick: undefined, -}; diff --git a/packages/attendance/src/stories/Button.stories.jsx b/packages/attendance/src/stories/Button.stories.jsx deleted file mode 100644 index a391b602..00000000 --- a/packages/attendance/src/stories/Button.stories.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from "react"; - -import { Button } from "./Button"; - -// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export -export default { - title: "Example/Button", - component: Button, - // More on argTypes: https://storybook.js.org/docs/react/api/argtypes - argTypes: { - backgroundColor: { control: "color" }, - }, -}; - -// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args -const Template = (args) =>