From 35f731f0ab499df25b241039a709d1ed49a064c3 Mon Sep 17 00:00:00 2001 From: Steven Huang Date: Sun, 23 Apr 2023 15:41:02 -0700 Subject: [PATCH 1/2] d --- src/sprintFiles/AlertComponent.tsx | 41 ++++++ src/sprintFiles/PinVerificationForm.tsx | 161 ++++++++++++++++++++++++ src/types/schema.ts | 3 + src/utils/constants.ts | 5 +- 4 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/sprintFiles/AlertComponent.tsx create mode 100644 src/sprintFiles/PinVerificationForm.tsx diff --git a/src/sprintFiles/AlertComponent.tsx b/src/sprintFiles/AlertComponent.tsx new file mode 100644 index 0000000..5738747 --- /dev/null +++ b/src/sprintFiles/AlertComponent.tsx @@ -0,0 +1,41 @@ +import { Dialog, DialogContent, Alert, Button, DialogActions } from "@mui/material"; +import { useState } from "react"; + +/** + * Pieces of code necessary to use this AlertComponent within a parent component: + * PARENT: + * const [errorMessage, setErrorMessage] = useState(""); + * In the returned div, + * { + errorMessage !== "" && + + } + * When the error message is set in the parent, this alert component will now automatically render. + + * @param message - The message for the popup to display + * @param setErrorMessage - The state function that is used to manage the message that's displayed + * @returns A dismissable Alert Component + */ +const AlertComponent = ({ message, setErrorMessage } : { message: string, setErrorMessage: (value: React.SetStateAction) => void}) => { + const [open, setOpen] = useState(true); + + const handleClose = () => { + setErrorMessage(""); + setOpen(false); + } + return ( + + + {message} + + + + + + ) +} + +export default AlertComponent; \ No newline at end of file diff --git a/src/sprintFiles/PinVerificationForm.tsx b/src/sprintFiles/PinVerificationForm.tsx new file mode 100644 index 0000000..5f4d81a --- /dev/null +++ b/src/sprintFiles/PinVerificationForm.tsx @@ -0,0 +1,161 @@ +import { TextInput } from "@/components/shared/forms/CustomFormikFields"; +import { selectCurrentHouse, selectCurrentUser } from "@/features/auth/authSlice"; +import { useGetScheduledShiftsQuery, useUpdateScheduledShiftMutation } from "@/features/scheduledShift/scheduledShiftApiSlice"; +import { House, ScheduledShift, Shift, User } from "@/types/schema"; +import { Container, Card, Button, Typography } from "@mui/material"; +import { Dictionary, EntityId } from "@reduxjs/toolkit"; +import { FormikHelpers, Formik, Form } from "formik"; +import { useState } from "react"; +import { useSelector } from "react-redux"; +import * as Yup from 'yup' +import AlertComponent from "./AlertComponent"; +import { useGetShiftsQuery } from "@/features/shift/shiftApiSlice"; +import dayjs from "dayjs"; +import { VERIFIED } from "@/utils/constants"; + +const VerificationSchema = Yup.object({ + pinNumber: Yup.string().required('Pin Number is required'), +}) + +const PinVerificationForm = ({scheduledShiftID}: {scheduledShiftID: string}) => { + const currentHouse: House = useSelector(selectCurrentHouse) as House + const currentUser: User = useSelector(selectCurrentUser) as User + const [errorMessage, setErrorMessage] = useState(""); + + const { + data: scheduledShifts + } = useGetScheduledShiftsQuery(currentHouse.houseID) + + const { + data: shifts + } = useGetShiftsQuery(currentHouse.houseID); + + const [ + updateScheduledShift + ] = useUpdateScheduledShiftMutation(); + + /** + * + * @param pinNumber The pin number that's entered + * @returns A boolean indicating if the pin number is valid for the given house and that the pin number doesn't belong to the current user + */ + const verifyPinCode = (pinNumber: string) => { + if (!(pinNumber in currentHouse.userPINs)) { + setErrorMessage("Unidentified User PIN"); + return; + } + if (currentHouse.userPINs[pinNumber] === currentUser.id) { + setErrorMessage("User PIN belongs to current user"); + return; + } + return true; + } + + /** + * + * @param scheduledEntityDictionary The entity dictionary for scheduledShifts + * @param shiftEntityDictionary The entity dictionary for shifts + * @param verifiedBy A user id for the person that verified this shift (not the current user) + * @returns Updates the Firebase with status = verified, verifiedBy = userID, verifiedAt = currentTime, shiftCopy = copy of the shift that this scheduledShifts points to + */ + + const updateScheduledShiftObject = async (scheduledEntityDictionary: Dictionary, shiftEntityDictionary: Dictionary, verifiedBy: string) => { + // Copy the shift into the scheduledShift object + let scheduledShiftObject = scheduledEntityDictionary[scheduledShiftID]; + if (scheduledShiftObject === undefined) { + console.log("Given scheduled shift id is invalid"); + return; + } + if (scheduledShiftObject.status === VERIFIED) { + setErrorMessage("Shift has already been verified"); + return; + } + let innerShiftID = scheduledShiftObject.shiftID; + let shiftObject = shiftEntityDictionary[innerShiftID]; + if (shiftObject === undefined) { + console.log("Inner shift id in the scheduled shfit is invalid"); + return; + } + const data = { + houseId: currentHouse.houseID, + shiftId: scheduledShiftObject.id, + data: { + shiftCopy: JSON.parse(JSON.stringify(shiftObject)) as Shift, + status: VERIFIED, + verifiedBy: verifiedBy, + verifiedAt: dayjs().toString() + } + } + await updateScheduledShift(data); + console.log("success"); + } + + /** + * + * @param pinNumber - The pin number that's entered + * @returns Checks the the pin code is valid and then updates the firebase. + */ + const onSubmit = async (values: {pinNumber: string}, formikBag: FormikHelpers) => { + const { pinNumber } = values; + if (scheduledShifts === undefined || shifts === undefined) { + console.log("Scheduled Shifts / Shifts not loaded"); + return; + } + if (!verifyPinCode(pinNumber)) { + return; + } + let verifiedBy = currentHouse.userPINs[pinNumber]; + await updateScheduledShiftObject(scheduledShifts.entities, shifts.entities, verifiedBy); + } + + return ( + + + + Pin Number + + + {({ isSubmitting, values, setFieldValue }) => ( +
+ + + + )} +
+
+ { + errorMessage !== "" && + + } +
+ ) +} + +export default PinVerificationForm; \ No newline at end of file diff --git a/src/types/schema.ts b/src/types/schema.ts index 3e1e63f..3736290 100644 --- a/src/types/schema.ts +++ b/src/types/schema.ts @@ -1,3 +1,5 @@ +import dayjs, {Dayjs} from "dayjs" + export type User = { // this id is to help the standard table generalize the id attribute id: string @@ -102,6 +104,7 @@ export type ScheduledShift = { verifiedAt: string unverifiedAt: string penaltyHours: number + shiftCopy: Shift } export type House = { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index bf1cf4e..afdb1f1 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -109,4 +109,7 @@ export const DAYS = [ 'thursday', 'friday', 'saturday', -] \ No newline at end of file +] + +export const VERIFIED = 'verified'; +export const UNVERIFIED = 'unverified'; \ No newline at end of file From a67bf137097788e1de6ab034593fdd9ce438ebab Mon Sep 17 00:00:00 2001 From: Steven Huang Date: Wed, 26 Apr 2023 20:04:37 -0700 Subject: [PATCH 2/2] d --- src/types/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/schema.ts b/src/types/schema.ts index 3736290..9cb5584 100644 --- a/src/types/schema.ts +++ b/src/types/schema.ts @@ -104,7 +104,7 @@ export type ScheduledShift = { verifiedAt: string unverifiedAt: string penaltyHours: number - shiftCopy: Shift + shiftCopy?: Shift } export type House = {