Skip to content

Commit

Permalink
feat: add timestamp for last edit in UG tutor form (#51)
Browse files Browse the repository at this point in the history
* feat: add attributes to track last edit on application

* feat: add useremail and date to admin updates

* feat: add useremail and date to reviewer scoring update

* feat: display last user edition in UG tutor form

* feat: modify last edit on by last reviewed by in reviewer scoring dialog

* refactor: use constant for date format. new constants.ts file created

* feat: clarify Last edit message by saying Last overall edit

* refactor: refactor new Date() to a const currentTimestamp

* fix: update edit timestamp consistently in all the actions in internal review

* style: make admin edit message more specific

---------

Co-authored-by: Zaki <[email protected]>
  • Loading branch information
estifraca and zaki-amin authored Jan 22, 2025
1 parent 3b47787 commit 52fb79a
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 14 deletions.
5 changes: 3 additions & 2 deletions components/dialog/AdminScoringDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import TmuaGradeBox from '@/components/dialog/TmuaGradeBox'
import Dropdown from '@/components/general/Dropdown'
import LabelText from '@/components/general/LabelText'
import { adminAccess } from '@/lib/access'
import { dateFormatting } from '@/lib/constants'
import { upsertAdminScoring } from '@/lib/query/forms'
import { FormPassbackState } from '@/lib/types'
import { decimalToNumber } from '@/lib/utils'
Expand Down Expand Up @@ -41,8 +42,8 @@ const AdminScoringForm: FC<AdminScoringFormProps> = ({ data, readOnly }) => {
<Flex direction="column" gap="3">
{internalReview?.lastAdminEditOn && internalReview?.lastAdminEditBy && (
<Text size="2" className="italic text-gray-500">
Last edited by {internalReview.lastAdminEditBy} on{' '}
{format(internalReview.lastAdminEditOn, "dd/MM/yy 'at' HH:mm")}
Last admin scoring by {internalReview.lastAdminEditBy} on{' '}
{format(internalReview.lastAdminEditOn, dateFormatting)}
</Text>
)}

Expand Down
5 changes: 3 additions & 2 deletions components/dialog/ReviewerScoringDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import FormWrapper from '@/components/dialog/FormWrapper'
import GenericDialog from '@/components/dialog/GenericDialog'
import LabelText from '@/components/general/LabelText'
import { reviewerAccess } from '@/lib/access'
import { dateFormatting } from '@/lib/constants'
import { upsertReviewerScoring } from '@/lib/query/forms'
import { FormPassbackState } from '@/lib/types'
import { ord } from '@/lib/utils'
Expand Down Expand Up @@ -45,7 +46,7 @@ const ReviewerScoringForm: FC<ReviewerScoringFormProps> = ({ data, readOnly }) =
<Flex direction="column" gap="3">
{internalReview?.lastReviewerEditOn && (
<Text size="2" className="italic text-gray-500">
Last edited on {format(internalReview.lastReviewerEditOn, "dd/MM/yy 'at' HH:mm")}
Last reviewed on {format(internalReview.lastReviewerEditOn, dateFormatting)}
</Text>
)}

Expand Down Expand Up @@ -142,7 +143,7 @@ const ReviewerScoringDialog: FC<ReviewerScoringDialogProps> = ({ data, userEmail
const handleFormSuccess = () => setIsOpen(false)

const upsertReviewerScoringWithId = (prevState: FormPassbackState, formData: FormData) =>
upsertReviewerScoring(data.id, prevState, formData)
upsertReviewerScoring(data.id, userEmail, prevState, formData)

const readOnly = !reviewerAccess(data.reviewer?.login, userEmail)

Expand Down
10 changes: 10 additions & 0 deletions components/dialog/UgTutorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Dropdown from '@/components/general/Dropdown'
import LabelText from '@/components/general/LabelText'
import { ApplicationRow } from '@/components/table/ApplicationTable'
import { adminAccess } from '@/lib/access'
import { dateFormatting } from '@/lib/constants'
import { insertComment, updateOutcomes } from '@/lib/query/forms'
import { FormPassbackState } from '@/lib/types'
import { decimalToNumber } from '@/lib/utils'
Expand All @@ -32,6 +33,7 @@ import {
TextArea,
TextField
} from '@radix-ui/themes'
import { format } from 'date-fns'
import React, { FC, useEffect, useMemo, useState } from 'react'

type Tab = 'outcomes' | 'comments'
Expand Down Expand Up @@ -86,6 +88,13 @@ const UgTutorForm: FC<UgTutorFormProps> = ({

return (
<Flex direction="column" gap="3">
{internalReview?.lastUserEditOn && internalReview?.lastUserEditBy && (
<Text size="2" className="italic text-gray-500">
Last overall edit by {internalReview.lastUserEditBy} on{' '}
{format(internalReview.lastUserEditOn, dateFormatting)}
</Text>
)}

<CandidateCallout
firstName={applicant.firstName}
surname={applicant.surname}
Expand Down Expand Up @@ -225,6 +234,7 @@ const UgTutorDialog: FC<UgTutorDialogProps> = ({ data, reviewerLogin, user }) =>
const upsertOutcomeWithId = async (prevState: FormPassbackState, formData: FormData) => {
return await updateOutcomes(
id,
email,
data.outcomes.map((o) => ({
id: o.id,
degreeCode: o.degreeCode
Expand Down
12 changes: 9 additions & 3 deletions components/table/ApplicationTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,11 @@ const ApplicationTable: FC<ApplicationTableProps> = ({
}),
columnHelper.accessor('nextAction', {
cell: (info) => (
<NextActionCell nextAction={info.getValue()} applicationId={info.row.original.id} />
<NextActionCell
userEmail={email}
nextAction={info.getValue()}
applicationId={info.row.original.id}
/>
),
header: 'Next Action',
id: SEARCH_PARAM_NEXT_ACTION
Expand Down Expand Up @@ -258,9 +262,10 @@ const WPColourMap: Record<WP, 'green' | 'red' | 'yellow'> = {
[WP.NOT_CALCULATED]: 'yellow'
}

const NextActionCell: FC<{ nextAction: NextAction; applicationId: number }> = ({
const NextActionCell: FC<{ nextAction: NextAction; applicationId: number; userEmail: string }> = ({
nextAction,
applicationId
applicationId,
userEmail
}) => {
return (
<Flex align="center" justify="between" gap="2">
Expand All @@ -271,6 +276,7 @@ const NextActionCell: FC<{ nextAction: NextAction; applicationId: number }> = ({
color="grass"
onClick={async () => {
await updateNextAction(
userEmail,
nextAction === NextAction.INFORM_CANDIDATE
? NextAction.FINAL_CHECK
: NextAction.CANDIDATE_INFORMED,
Expand Down
1 change: 1 addition & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const dateFormatting = "dd/MM/yy 'at' HH:mm"
5 changes: 4 additions & 1 deletion lib/csv/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ function updateAdminScoring(
if (currentNextAction === NextAction.ADMIN_SCORING_WITH_TMUA)
nextNextAction = NextAction.REVIEWER_SCORING

const currentTimestamp = new Date()
return prisma.application.update({
where: {
admissionsCycle_cid: {
Expand All @@ -211,7 +212,9 @@ function updateAdminScoring(
extracurricularAdminScore: a.extracurricularAdminScore,
examComments: a.examComments,
lastAdminEditBy: userEmail,
lastAdminEditOn: new Date()
lastAdminEditOn: currentTimestamp,
lastUserEditBy: userEmail,
lastUserEditOn: currentTimestamp
}
}
}
Expand Down
31 changes: 25 additions & 6 deletions lib/query/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export async function upsertAdminScoring(
}
})

const currentTimestamp = new Date()
await prisma.internalReview.upsert({
where: {
applicationId
Expand All @@ -60,14 +61,18 @@ export async function upsertAdminScoring(
extracurricularAdminScore,
examComments,
lastAdminEditBy: adminLogin,
lastAdminEditOn: new Date()
lastAdminEditOn: currentTimestamp,
lastUserEditBy: adminLogin,
lastUserEditOn: currentTimestamp
},
update: {
motivationAdminScore,
extracurricularAdminScore,
examComments,
lastAdminEditBy: adminLogin,
lastAdminEditOn: new Date()
lastAdminEditOn: currentTimestamp,
lastUserEditBy: adminLogin,
lastUserEditOn: currentTimestamp
}
})

Expand All @@ -77,6 +82,7 @@ export async function upsertAdminScoring(

export async function upsertReviewerScoring(
applicationId: number,
userEmail: string,
_: FormPassbackState,
formData: FormData
): Promise<FormPassbackState> {
Expand All @@ -97,14 +103,17 @@ export async function upsertReviewerScoring(
}
})

const currentTimestamp = new Date()
await prisma.internalReview.update({
where: { applicationId },
data: {
motivationReviewerScore,
extracurricularReviewerScore,
referenceReviewerScore,
academicComments,
lastReviewerEditOn: new Date()
lastReviewerEditOn: currentTimestamp,
lastUserEditBy: userEmail,
lastUserEditOn: currentTimestamp
}
})

Expand All @@ -114,6 +123,7 @@ export async function upsertReviewerScoring(

export async function updateOutcomes(
applicationId: number,
userEmail: string,
partialOutcomes: { id: number; degreeCode: string }[],
_: FormPassbackState,
formData: FormData
Expand All @@ -126,7 +136,7 @@ export async function updateOutcomes(
return { id, ...parsedOutcome }
})

await updateNextAction(formData.get('nextAction'), applicationId)
await updateNextAction(userEmail, formData.get('nextAction'), applicationId)

for (const { id, offerCode, offerText, decision } of fullOutcomes) {
await prisma.outcome.update({
Expand Down Expand Up @@ -156,7 +166,7 @@ export async function insertComment(
const result = formCommentSchema.safeParse(Object.fromEntries(formData))
if (!result.success) return { status: 'error', message: result.error.issues[0].message }

await updateNextAction(formData.get('nextAction'), applicationId)
await updateNextAction(authorEmail, formData.get('nextAction'), applicationId)

await prisma.comment.create({
data: {
Expand All @@ -171,12 +181,21 @@ export async function insertComment(
}

export async function updateNextAction(
userEmail: string,
nextActionInput: FormDataEntryValue | string | null,
applicationId: number
) {
if (!nextActionInput || nextActionInput === 'Unchanged') return
await prisma.internalReview.update({
where: { applicationId },
data: {
lastUserEditBy: userEmail,
lastUserEditOn: new Date()
}
})

if (!nextActionInput || nextActionInput === 'Unchanged') return
const nextAction = nextActionField.parse(nextActionInput)

await prisma.application.update({
where: { id: applicationId },
data: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "InternalReview" ADD COLUMN "lastUserEditBy" TEXT,
ADD COLUMN "lastUserEditOn" TIMESTAMP(3);
2 changes: 2 additions & 0 deletions prisma/schema/application.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ model InternalReview {
examComments String?
lastAdminEditBy String?
lastAdminEditOn DateTime?
lastUserEditBy String?
lastUserEditOn DateTime?
motivationReviewerScore Decimal? @db.Decimal(3, 1)
extracurricularReviewerScore Decimal? @db.Decimal(3, 1)
referenceReviewerScore Decimal? @db.Decimal(3, 1)
Expand Down

0 comments on commit 52fb79a

Please sign in to comment.