Skip to content

Commit

Permalink
Add READY_FOR_PICKUP UPR status and misc refactors/bugfixes (#180)
Browse files Browse the repository at this point in the history
- Changed copytext for SF emails
- Replaced placeholder text in SF confirmation modals
- Fixed UPR admin table PO num and Req num states not updating on ticket
change
- Fixed claim page crashing (toUpperCase in hooks.js)
  • Loading branch information
ansonjwhe authored Jan 3, 2024
1 parent aa02a6d commit a5b573e
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 67 deletions.
28 changes: 24 additions & 4 deletions backend/emails/emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ const sendEmailUPRPurchasedToReporter = async (upr) => {
`Your UW Finance Purchase Request has been purchased! When the item is ready to be picked up, we will let you know.`
) +
(await getUPRTicketInfoHTML(upr)) +
+getTicketLinkHTML(upr.path)
getTicketLinkHTML(upr.path)
const To = await getEmailToSection(upr.reporter_id, [
EMAIL_RECIPIENTS.reporter,
])
Expand Down Expand Up @@ -205,6 +205,25 @@ const sendEmailUPRPurchasedToCoordinator = async (upr) => {
})
}

const sendEmailUPRReadyForPickupToCoordinator = async (upr) => {
const Subject = `[Ready for pickup] ${upr.codename}`
const HTMLPart =
getMainMessageHTML(
'Your UW Finance Request is ready to be picked up! Please view the ticket below for pickup instructions and confirm when you have picked it up.'
) +
(await getUPRTicketInfoHTML(upr)) +
getTicketLinkHTML(upr.path)
const To = await getEmailToSection(upr.reporter_id, [
EMAIL_RECIPIENTS.reporter,
])

await sendEmail({
Subject,
HTMLPart,
To,
})
}

const sendEmailPPRApprovedToReporter = async (ppr) => {
const Subject = `[Ready to Buy] ${ppr.codename}`
const HTMLPart =
Expand Down Expand Up @@ -287,7 +306,7 @@ const sendEmailSFReimbursementRequestToCoordinator = async (sf) => {
const Subject = `[Action Needed] Submit Reimbursement Request ${sf.codename}`
const HTMLPart =
getMainMessageHTML(
`Claim has been submitted for ${sf.codename}! Please review it and submit a reimbursement request. Visit the ticket link below to confirm you have submitted the reimbursement request.`
`Please review the claim and submit a reimbursement request for ${sf.codename}. Once submitted, please visit the ticket link below to confirm that you have submitted it.`
) +
(await getSFTicketInfoHTML(sf)) +
getTicketLinkHTML(sf.path)
Expand All @@ -305,7 +324,7 @@ const sendEmailSFConfirmReimbursementSubmitToCoordinator = async (sf) => {
const Subject = `[Action Needed] Confirm Reimbursement Received ${sf.codename}`
const HTMLPart =
getMainMessageHTML(
`Please visit the ticket link below to confirm you have received the reimbursement for ${sf.codename}.`
`Once the sponsorship fund for ${sf.codename} has reimbursed us, please visit the ticket link below to confirm it.`
) +
(await getSFTicketInfoHTML(sf)) +
getTicketLinkHTML(sf.path)
Expand All @@ -322,7 +341,7 @@ const sendEmailSFConfirmReimbursementSubmitToCoordinator = async (sf) => {
const sendEmailSFReimbursementReceivedToTeam = async (sf) => {
const Subject = `[Reimbursed] ${sf.codename}`
const HTMLPart =
getMainMessageHTML(`${sf.codename} has been reimbursed.`) +
getMainMessageHTML(`${sf.codename} has been reimbursed!`) +
(await getSFTicketInfoHTML(sf)) +
getTicketLinkHTML(sf.path)
const To = await getEmailToSection(sf.reporter_id, [
Expand Down Expand Up @@ -662,6 +681,7 @@ module.exports = {
sendEmailUPRApprovedToCoordinator,
sendEmailUPRPurchasedToReporter,
sendEmailUPRPurchasedToCoordinator,
sendEmailUPRReadyForPickupToCoordinator,
sendEmailPPRApprovedToReporter,
sendEmailPPRCreatedToApprovers,
sendEmailPPRPurchasedAndReceiptsSubmittedToCoordinator,
Expand Down
4 changes: 2 additions & 2 deletions backend/models/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ const UPR_STATUS = [
'SEEKING_APPROVAL',
'SENT_TO_COORDINATOR',
'ORDERED',
'READY_FOR_PICKUP',
'PICKED_UP',
]

const UPR_STATUS_FUNDING_SPENT = ['ORDERED', 'PICKED_UP']
const UPR_STATUS_FUNDING_SPENT = ['ORDERED', 'READY_FOR_PICKUP', 'PICKED_UP']

const APPROVAL_LEVELS = Object.freeze({
director_approval: 'director_approval',
Expand Down
4 changes: 3 additions & 1 deletion backend/service/personalpurchases.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@ const createPersonalPurchase = async (body) => {
}

const updatePersonalPurchase = async (id, body) => {
// READY_TO_BUY -> PURCHASED_AND_RECEIPTS_SUBMITTED
const newPurchaseTicket = PersonalPurchase.findByIdAndUpdate(id, body, {
new: true,
})
const annotatedPPR = await getPersonalPurchase(id)

// READY_TO_BUY -> PURCHASED_AND_RECEIPTS_SUBMITTED
if (body?.status === 'PURCHASED_AND_RECEIPTS_SUBMITTED') {
await sendEmailPPRPurchasedAndReceiptsSubmittedToCoordinator(
annotatedPPR
)
// PURCHASED_AND_RECEIPTS_SUBMITTED -> REPORTER_PAID
} else if (body?.status === 'REPORTER_PAID') {
await sendEmailPPRReimbursedToReporter(annotatedPPR)
}
Expand Down
8 changes: 5 additions & 3 deletions backend/service/sponsorshipfunds.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,19 @@ const updateSponsorshipFund = async (id, body) => {
const status = annotatedSponsorshipFund.status

if (status === 'CLAIM_SUBMITTED') {
sendEmailSFReimbursementRequestToCoordinator(annotatedSponsorshipFund)
await sendEmailSFReimbursementRequestToCoordinator(
annotatedSponsorshipFund
)
}

if (status === 'SUBMITTED_TO_SF') {
sendEmailSFConfirmReimbursementSubmitToCoordinator(
await sendEmailSFConfirmReimbursementSubmitToCoordinator(
annotatedSponsorshipFund
)
}

if (status === 'REIMBURSED') {
sendEmailSFReimbursementReceivedToTeam(annotatedSponsorshipFund)
await sendEmailSFReimbursementReceivedToTeam(annotatedSponsorshipFund)
}

return newSponsorshipFund
Expand Down
14 changes: 8 additions & 6 deletions backend/service/uwfinancepurchases.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
sendEmailUPRApprovedToCoordinator,
sendEmailUPRPurchasedToCoordinator,
sendEmailUPRPurchasedToReporter,
sendEmailUPRReadyForPickupToCoordinator,
} = require('../emails/emails')

const getAllUWFinancePurchases = () => {
Expand All @@ -33,26 +34,27 @@ const createNewUWFinancePurchase = async (body) => {
}

const updateUWFinancePurchase = async (id, body) => {
const existingPurchaseTicket = await getUWFinancePurchase(id)
// SENT_TO_COORDINATOR -> ORDERED
const newPurchaseTicket = await UWFinancePurchase.findByIdAndUpdate(
id,
body,
{
new: true,
}
)
if (
existingPurchaseTicket.status === 'SENT_TO_COORDINATOR' &&
body.status === 'ORDERED'
) {
// SENT_TO_COORDINATOR -> ORDERED
if (body.status === 'ORDERED') {
const annotatedUPR = await getUWFinancePurchase(id)
const emails = [
sendEmailUPRPurchasedToCoordinator(annotatedUPR),
sendEmailUPRPurchasedToReporter(annotatedUPR),
]
await Promise.all(emails)
}
// ORDERED -> READY_FOR_PICKUP
if (body.status === 'READY_FOR_PICKUP') {
const annotatedUPR = await getUWFinancePurchase(id)
await sendEmailUPRReadyForPickupToCoordinator(annotatedUPR)
}
return newPurchaseTicket
}

Expand Down
11 changes: 3 additions & 8 deletions frontend/src/components/ConfirmationModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,14 @@ import {
Button,
} from '@chakra-ui/react'

const ConfirmationModal = ({ onConfirm, isOpen, onClose }) => {
const ConfirmationModal = ({ title, body, onConfirm, isOpen, onClose }) => {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Submission (Placeholder)</ModalHeader>
<ModalHeader>{title}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<div>
Are you sure you want to submit this, you cant undo bla
bla bla (PLACEHOLDER)
</div>
</ModalBody>
<ModalBody>{body}</ModalBody>

<ModalFooter>
<Button colorScheme="red" mr={3} onClick={onClose}>
Expand Down
76 changes: 38 additions & 38 deletions frontend/src/components/TicketContent/SFAdminContentTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Link,
useDisclosure,
} from '@chakra-ui/react'
import React from 'react'
import React, { useState } from 'react'
import { useGetPreserveParamsHref } from '../../hooks/hooks'
import { useGetCurrentTicket } from '../../hooks/hooks'
import { TICKET_ENDPOINTS } from '../../constants'
Expand All @@ -25,35 +25,45 @@ const SFAdminContentTable = () => {
onOpen: onOpenConfirmation,
onClose: onCloseConfirmation,
} = useDisclosure()
const [confirmationModalTitleText, setConfirmationModalTitleText] =
useState('')
const [confirmationModalBodyText, setConfirmationModalBodyText] =
useState('')
const transitionStatusButtonText = {
ALLOCATED: 'Submit Claim',
CLAIM_SUBMITTED: 'Confirm Reimbursement Request Submitted',
SUBMITTED_TO_SF: 'Confirm SF Reimbursed',
}

const transitionStatusText = (status) => {
if (status === 'ALLOCATED') {
return 'Submit Claim'
}
if (status === 'CLAIM_SUBMITTED') {
return 'Confirm Reimbursement Submission'
const handleOpenConfirmation = () => {
setConfirmationModalTitleText(
transitionStatusButtonText[currentTicket.status]
)
if (currentTicket.status === 'ALLOCATED') {
setConfirmationModalBodyText(
`Are you sure you want to submit this claim? This will send an email to the Finance Coordinator and prompt them to submit a reimbursement request for ${currentTicket.codename}`
)
}
if (status === 'SUBMITTED_TO_SF') {
return 'Confirm Reimbursement'
if (currentTicket.status === 'CLAIM_SUBMITTED') {
setConfirmationModalBodyText(
`Please confirm that you have submitted a reimbursement request for ${currentTicket.codename}.`
)
}
if (status === 'REIMBURSED') {
return 'Reimbursement Confirmed'
if (currentTicket.status === 'SUBMITTED_TO_SF') {
setConfirmationModalBodyText(
`Please confirm that the sponsorship fund for ${currentTicket.codename} has successfully reimbursed WATonomous.`
)
}
onOpenConfirmation()
}
const nextStatus = (status) => {
if (status === 'ALLOCATED') {
return 'CLAIM_SUBMITTED'
const handleUpdateStatus = async () => {
const nextStatus = {
ALLOCATED: 'CLAIM_SUBMITTED',
CLAIM_SUBMITTED: 'SUBMITTED_TO_SF',
SUBMITTED_TO_SF: 'REIMBURSED',
}
if (status === 'CLAIM_SUBMITTED') {
return 'SUBMITTED_TO_SF'
}
if (status === 'SUBMITTED_TO_SF') {
return 'REIMBURSED'
}
}
const handleUpdateStatus = async (nextStatus) => {
const payload = {
status: nextStatus,
status: nextStatus[currentTicket.status],
}
await axiosPreset.patch(
`${TICKET_ENDPOINTS.SF}/${currentTicket._id}`,
Expand All @@ -73,30 +83,20 @@ const SFAdminContentTable = () => {
<Center pb="7px" gap="10px">
{isConfirmationOpen && (
<ConfirmationModal
title={confirmationModalTitleText}
body={confirmationModalBodyText}
onClose={onCloseConfirmation}
isOpen={isConfirmationOpen}
onConfirm={() =>
handleUpdateStatus(
nextStatus(currentTicket.status)
)
}
onConfirm={handleUpdateStatus}
/>
)}
{currentTicket.status !== 'REIMBURSED' && (
<Button
colorScheme="blue"
size="sm"
disabled={
currentTicket?.po_number?.length +
currentTicket?.requisition_number
?.length ===
0
}
onClick={() => {
onOpenConfirmation()
}}
onClick={handleOpenConfirmation}
>
{transitionStatusText(currentTicket.status)}
{transitionStatusButtonText[currentTicket.status]}
</Button>
)}
{/* can remove getPreserveParamsHref if it does not make sense to preserve params */}
Expand Down
35 changes: 34 additions & 1 deletion frontend/src/components/TicketContent/UPRAdminContentTable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Button, Center, Heading, Table, Tbody, VStack } from '@chakra-ui/react'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useSetRecoilState } from 'recoil'
import TicketContentTableRow from './TicketContentTableRow'
import { allTicketsState } from '../../state/atoms'
Expand All @@ -9,6 +10,7 @@ import { getAllTickets } from '../../utils/globalSetters'
import { useGetCurrentTicket } from '../../hooks/hooks'

const UPRAdminContentTable = () => {
const location = useLocation()
const currentTicket = useGetCurrentTicket()
const [reqNum, setReqNum] = useState(currentTicket.requisition_number)
const [poNum, setPoNum] = useState(currentTicket.po_number)
Expand All @@ -23,6 +25,15 @@ const UPRAdminContentTable = () => {
setChanged(true)
}

useEffect(() => {
setReqNum(currentTicket.requisition_number ?? '')
setPoNum(currentTicket.po_number ?? '')
}, [
location.pathname,
currentTicket.requisition_number,
currentTicket.po_number,
])

const saveFields = async () => {
const payload = {
requisition_number: reqNum,
Expand Down Expand Up @@ -50,6 +61,18 @@ const UPRAdminContentTable = () => {
setChanged(false)
}

const transitionToReadyForPickup = async () => {
const payload = {
status: 'READY_FOR_PICKUP',
}
await axiosPreset.patch(
`${TICKET_ENDPOINTS.UPR}/${currentTicket._id}`,
payload
)
await getAllTickets(setAllTickets)
setChanged(false)
}

const transitionToPickedUp = async () => {
const payload = {
status: 'PICKED_UP',
Expand Down Expand Up @@ -98,6 +121,16 @@ const UPRAdminContentTable = () => {
</Button>
)}
{currentTicket.status === 'ORDERED' && (
<Button
colorScheme="blue"
size="sm"
mr="20px"
onClick={transitionToReadyForPickup}
>
Transition To Ready for Pickup
</Button>
)}
{currentTicket.status === 'READY_FOR_PICKUP' && (
<Button
colorScheme="blue"
size="sm"
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/hooks/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ export const useGetCurrentTicket = () => {
const getCurrentTicket = useCallback(() => {
const splitPath = location.pathname.split('/')
if (splitPath.length !== 3) return {}
let ticketType = splitPath[1].toUpperCase()
let ticketType = splitPath[1]
const ticketId = parseInt(splitPath[2])
if (ticketType === 'claim') {
ticketType = TICKET_TYPES.SF
}
// in case of claim view for instance
// return {} in case that allTickets is empty, means its still loading
if (Object.keys(allTickets).length === 0) return {}
const currentTicketData = allTickets[TICKET_TYPES[ticketType]].find(
(ticket) => ticket._id === ticketId
)
const currentTicketData = allTickets[
TICKET_TYPES[ticketType.toUpperCase()]
].find((ticket) => ticket._id === ticketId)
// if not found, will return null
return currentTicketData
}, [allTickets, location.pathname])
Expand Down

0 comments on commit a5b573e

Please sign in to comment.