Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stevenh/verify #21

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/sprintFiles/AlertComponent.tsx
Original file line number Diff line number Diff line change
@@ -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 !== "" &&
<AlertComponent message = {errorMessage} setErrorMessage = {setErrorMessage} />
}
* 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<string>) => void}) => {
const [open, setOpen] = useState(true);

const handleClose = () => {
setErrorMessage("");
setOpen(false);
}
return (
<Dialog
open = {open}
onClose = {handleClose}
>
<DialogContent>
<Alert severity="error">{message}</Alert>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Dismiss</Button>
</DialogActions>
</Dialog>
)
}

export default AlertComponent;
161 changes: 161 additions & 0 deletions src/sprintFiles/PinVerificationForm.tsx
Original file line number Diff line number Diff line change
@@ -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<ScheduledShift>, shiftEntityDictionary: Dictionary<Shift>, 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<any>) => {
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 (
<Container sx={{
height: '100vh',
backgroundColor: '#f3f5f6',
margin: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
alignSelf: 'center'
}}>
<Card
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
alignSelf: 'center',
paddingX: 5,
}}>
<Typography component="h5" variant="h5" sx={{ marginTop: 3 }}>
Pin Number
</Typography>
<Formik
validationSchema = {VerificationSchema}
initialValues = {{
pinNumber: ""
}}
onSubmit = {onSubmit}
>
{({ isSubmitting, values, setFieldValue }) => (
<Form>
<TextInput name="pinNumber" label="Pin Number" />
<Button type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
disabled={isSubmitting}
>Save
</Button>
</Form>
)}
</Formik>
</Card>
{
errorMessage !== "" &&
<AlertComponent message = {errorMessage} setErrorMessage = {setErrorMessage} />
}
</Container>
)
}

export default PinVerificationForm;
3 changes: 3 additions & 0 deletions src/types/schema.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -102,6 +104,7 @@ export type ScheduledShift = {
verifiedAt: string
unverifiedAt: string
penaltyHours: number
shiftCopy?: Shift
}

export type House = {
Expand Down
5 changes: 4 additions & 1 deletion src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,7 @@ export const DAYS = [
'thursday',
'friday',
'saturday',
]
]

export const VERIFIED = 'verified';
export const UNVERIFIED = 'unverified';