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..49a5613e 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,17 @@ "@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", "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..673c189e --- /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..473d38b9 --- /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..72f5b630 --- /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) => 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/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) =>