Skip to content
Merged
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
82 changes: 82 additions & 0 deletions src/components/AbsenceClaimThanks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
Button,
Flex,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useTheme,
} from '@chakra-ui/react';
import { FiCheckCircle } from 'react-icons/fi';

const AbsenceClaimThanks = ({ isOpen, onClose, event, absenceDate }) => {
const theme = useTheme();

return (
<Modal isOpen={isOpen} onClose={onClose} isCentered>
<ModalOverlay />
<ModalContent
width="319px"
padding="25px"
sx={{
alignItems: 'center',
}}
>
<ModalHeader
textStyle="h3"
fontSize="16px"
padding="0"
textAlign="center"
>
<Flex align="center" justify="center" gap={2}>
<FiCheckCircle
size="20px"
color={theme.colors.positiveGreen[200]}
/>
<Text>Thank you!</Text>
</Flex>
</ModalHeader>

<ModalBody
textStyle="subtitle"
color="text"
padding="0"
my="30px"
textAlign="center"
>
<Text as="span">You have successfully claimed </Text>
<Text as="span" fontWeight="bold">
{event?.absentTeacher?.firstName + "'s"}
</Text>
{' absence on '}
<Text as="span" fontWeight="bold">
{absenceDate + '.'}
</Text>

<Text my="12px">Make sure to review the lesson plan!</Text>

<Text as="span" fontWeight="bold">
Note:
</Text>
<Text as="span"> Please contact admin for any modifications.</Text>
</ModalBody>

<ModalFooter padding="0" width="full" justifyContent="center">
<Button
onClick={onClose}
textStyle="button"
fontWeight="500"
width="235px"
>
Got it!
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};

export default AbsenceClaimThanks;
155 changes: 148 additions & 7 deletions src/components/AbsenceDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { IoEyeOutline } from 'react-icons/io5';
import AbsenceStatusTag from './AbsenceStatusTag';
import EditAbsenceForm from './EditAbsenceForm';
import LessonPlanView from './LessonPlanView';
import AbsenceClaimThanks from './AbsenceClaimThanks';

interface AbsenceDetailsProps {
isOpen: boolean;
Expand All @@ -47,6 +48,9 @@ const AbsenceDetails: React.FC<AbsenceDetailsProps> = ({
const [isDeleting, setIsDeleting] = useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isClaiming, setIsClaiming] = useState(false);
const [isClaimDialogOpen, setIsClaimDialogOpen] = useState(false);
const [isClaimThanksOpen, setIsClaimThanksOpen] = useState(false);

const toast = useToast();

Expand All @@ -57,6 +61,94 @@ const AbsenceDetails: React.FC<AbsenceDetailsProps> = ({
const isUserSubstituteTeacher = userId === event.substituteTeacher?.id;
const isUserAdmin = userData.role === Role.ADMIN;

const getOrdinalNum = (number) => {
let selector;

if (number <= 0) {
selector = 4;
} else if ((number > 3 && number < 21) || number % 10 > 3) {
selector = 0;
} else {
selector = number % 10;
}

return number + ['th', 'st', 'nd', 'rd', ''][selector];
};

const formatDate = (date: Date) => {
const parsedDate = new Date(date);
const weekday = parsedDate.toLocaleDateString('en-CA', { weekday: 'long' });
const month = parsedDate.toLocaleDateString('en-CA', { month: 'long' });
const day = parsedDate.getDate();

return `${weekday}, ${month} ${getOrdinalNum(day)}`;
};

const absenceDate = formatDate(event.start!!);

const handleClaimThanksDone = () => {
setIsClaimThanksOpen(false);
};

const handleClaimAbsenceClick = () => {
setIsClaimDialogOpen(true);
};

const handleClaimCancel = () => {
setIsClaimDialogOpen(false);
};

const handleClaimConfirm = async () => {
setIsClaiming(true);

try {
const response = await fetch('/api/editAbsence', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: event.absenceId,
lessonDate: event.start,
reasonOfAbsence: event.reasonOfAbsence,
notes: event.notes,
absentTeacherId: event.absentTeacher.id,
substituteTeacherId: userData.id,
locationId: event.locationId,
subjectId: event.subjectId,
roomNumber: event.roomNumber,
}),
});

if (!response.ok) {
throw new Error('Failed to claim absence');
}

toast({
title: 'Absence claimed',
description: 'You have successfully claimed this absence.',
status: 'success',
duration: 5000,
isClosable: true,
});

await fetchAbsences();
setIsClaimDialogOpen(false);
setIsClaimThanksOpen(true);
} catch (error) {
toast({
title: 'Error',
description: error.message || 'Failed to claim absence',
status: 'error',
duration: 5000,
isClosable: true,
});
} finally {
setIsClaiming(false);
onClose();
}
};

const handleEditClick = () => {
setIsEditModalOpen(true);
};
Expand Down Expand Up @@ -180,13 +272,7 @@ const AbsenceDetails: React.FC<AbsenceDetailsProps> = ({
<Flex gap="13px" mt="-8px">
<Calendar size="20px" color={theme.colors.primaryBlue[300]} />
<Text textStyle="subtitle" color={theme.colors.text.body}>
{event.start
? new Date(event.start).toLocaleDateString('en-CA', {
weekday: 'long',
month: 'long',
day: 'numeric',
})
: 'N/A'}
{absenceDate}
</Text>
</Flex>
<Flex gap="13px" mt="-8px">
Expand Down Expand Up @@ -335,6 +421,7 @@ const AbsenceDetails: React.FC<AbsenceDetailsProps> = ({
height="44px"
fontSize="16px"
fontWeight="500"
onClick={handleClaimAbsenceClick}
>
Fill this Absence
</Button>
Expand All @@ -343,6 +430,60 @@ const AbsenceDetails: React.FC<AbsenceDetailsProps> = ({
</ModalBody>
</ModalContent>
</Modal>
<Modal isOpen={isClaimDialogOpen} onClose={handleClaimCancel} isCentered>
<ModalOverlay />
<ModalContent
width="300px"
padding="25px"
sx={{
alignItems: 'center',
}}
>
<ModalHeader
textStyle="h3"
fontSize="16px"
padding="0"
textAlign="center"
>
Are you sure you want to fill this absence?
</ModalHeader>
<ModalBody
textStyle="subtitle"
color="text"
padding="0"
mt="12px"
mb="16px"
>
<Text>{"You won't be able to undo."}</Text>
</ModalBody>
<ModalFooter padding="0">
<Button
onClick={handleClaimCancel}
variant="outline"
textStyle="button"
fontWeight="500"
mr="10px"
>
Cancel
</Button>
<Button
onClick={handleClaimConfirm}
textStyle="button"
fontWeight="500"
isLoading={isClaiming}
ml="10px"
>
Confirm
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<AbsenceClaimThanks
isOpen={isClaimThanksOpen}
onClose={handleClaimThanksDone}
event={event}
absenceDate={absenceDate}
/>
<Modal
isOpen={isDeleteDialogOpen}
onClose={handleDeleteCancel}
Expand Down