-
Notifications
You must be signed in to change notification settings - Fork 4
[INTW26] Conflict Dialog #247
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
Changes from all commits
ac00b1b
2ab9f4f
e588a62
d99765b
f01aaa7
1109d38
a89d53b
c9b5840
14a8bea
5a1ec00
83a1309
2b1c934
8c77844
e8f59c1
188f7ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { fetchGraphql } from "@utils/makegqlrequest"; | ||
| import { mutations } from "graphql/queries"; | ||
| import BaseAPIClient from "./BaseAPIClient"; | ||
| import { ReviewedApplicantRecordDTO } from "types/review"; | ||
|
|
||
| export const reportReviewConflict = async ( | ||
| applicantRecordId: string, | ||
| reviewerId: number, | ||
| ): Promise<ReviewedApplicantRecordDTO> => { | ||
| BaseAPIClient.handleAuthRefresh(); | ||
| const rawResponse = await fetchGraphql(mutations.reportReviewConflict, { | ||
| applicantRecordId, | ||
| reviewerId, | ||
| }); | ||
| const response = rawResponse?.data?.reportReviewConflict; | ||
|
|
||
| if (!response) { | ||
| throw new Error("Conflict report request returned no data"); | ||
| } | ||
|
|
||
| return response; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { ReactNode } from "react"; | ||
| import Dialog from "@mui/material/Dialog"; | ||
| import { DialogActions } from "@mui/material"; | ||
| import { useTheme } from "@mui/material/styles"; | ||
|
|
||
| type DialogueProps = { | ||
| open: boolean; | ||
| onClose: () => void; | ||
| header: string; | ||
| text: string; | ||
| errorText?: string; | ||
| children?: ReactNode; | ||
| }; | ||
|
|
||
| export const Dialogue = ({ | ||
| open, | ||
| header, | ||
| text, | ||
| errorText, | ||
| children, | ||
| }: DialogueProps) => { | ||
| const theme = useTheme(); | ||
|
|
||
| return ( | ||
| <Dialog | ||
| open={open} | ||
| PaperProps={{ | ||
| sx: { | ||
| borderRadius: 2, | ||
| overflow: "hidden", | ||
| boxShadow: "none", | ||
| opacity: 1, | ||
| }, | ||
| }} | ||
| > | ||
| <div | ||
| className="flex flex-col justify-center items-center p-6 w-[310px] w-full" | ||
| style={{ | ||
| backgroundColor: theme.palette.background.paper, | ||
| }} | ||
| > | ||
| <div className="flex flex-col justify-center items-center gap-2"> | ||
| <h2 | ||
| className="font-poppins text-[20px] font-medium leading-[1.4] text-center" | ||
| style={{ color: theme.palette.primary.main }} | ||
| > | ||
| {header} | ||
| </h2> | ||
| <div className="font-source text-[14px] font-normal leading-[140%] text-center"> | ||
| <div style={{ color: theme.palette.text.primary }}> {text} </div> | ||
| {errorText && ( | ||
| <div style={{ color: theme.palette.error.main }}>{errorText}</div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| <DialogActions className="w-full h-[36px] !p-0 mt-9"> | ||
| {children} | ||
| </DialogActions> | ||
| </div> | ||
| </Dialog> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import Button from "@components/common/Button"; | ||
| import { Dialogue } from "@components/common/Dialogue"; | ||
|
|
||
| type ConflictDialogueProps = { | ||
| open: boolean; | ||
| hasError: boolean; | ||
| onClose: () => void; | ||
| onConfirm: () => void; | ||
| }; | ||
|
|
||
| export const ReportConflictDialogue = ({ | ||
| open, | ||
| hasError, | ||
| onClose, | ||
| onConfirm, | ||
| }: ConflictDialogueProps) => { | ||
| return ( | ||
| <Dialogue | ||
| open={open} | ||
| onClose={onClose} | ||
| header="Report as conflict of interest?" | ||
| text="Clicking yes will notify admins and cannot be undone." | ||
| errorText={ | ||
| hasError | ||
| ? "An error occurred while reporting. Please try again." | ||
| : undefined | ||
| } | ||
| > | ||
| <div className="flex gap-4 w-full"> | ||
| <Button | ||
| variant="secondary" | ||
| size="sm" | ||
| onClick={onClose} | ||
| className="flex-1 min-w-0 flex justify-center items-center whitespace-nowrap !m-0" | ||
| > | ||
| <span className="text-[16px] font-normal font-source">Cancel</span> | ||
| </Button> | ||
| <Button | ||
| variant="primary" | ||
| size="sm" | ||
| onClick={onConfirm} | ||
| className="flex-1 min-w-0 flex justify-center items-center whitespace-nowrap !m-0" | ||
| > | ||
| <span className="text-[16px] font-normal font-source"> | ||
| Yes, report | ||
| </span> | ||
| </Button> | ||
| </div> | ||
| </Dialogue> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import Button from "@components/common/Button"; | ||
| import { Dialogue } from "@components/common/Dialogue"; | ||
|
|
||
| type ReportConflictSuccessDialogueProps = { | ||
| open: boolean; | ||
| onClose: () => void; | ||
| }; | ||
|
|
||
| export const ReportConflictSuccessDialogue = ({ | ||
| open, | ||
| onClose, | ||
| }: ReportConflictSuccessDialogueProps) => { | ||
| return ( | ||
| <Dialogue | ||
| open={open} | ||
| onClose={onClose} | ||
| header="Conflict reported!" | ||
| text="This applicant has been reported as a conflict of interest and will be re-assigned to another reviewer." | ||
| > | ||
| <div className="flex gap-4 w-full"> | ||
| <Button | ||
| variant="primary" | ||
| size="sm" | ||
| onClick={onClose} | ||
| className="flex-1 min-w-0 flex justify-center items-center whitespace-nowrap !m-0" | ||
| > | ||
| <span className="text-[16px] font-normal font-source"> | ||
| Back to homepage | ||
| </span> | ||
| </Button> | ||
| </div> | ||
| </Dialogue> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { LongLeftIcon } from "@components/icons/long-left.icon"; | ||
| import Link from "next/link"; | ||
| import { ReactNode } from "react"; | ||
|
|
||
| interface ReviewStageHeaderProps { | ||
| backHref: string; | ||
| right?: ReactNode; | ||
| } | ||
|
|
||
| export const ReviewStageHeader = ({ | ||
| backHref, | ||
| right, | ||
| }: ReviewStageHeaderProps) => { | ||
| return ( | ||
| <> | ||
| <Link href={backHref} passHref> | ||
| <a className="font-source no-underline inline-flex justify-center items-center gap-2 w-fit cursor-pointer shrink-0 hover:opacity-90 rounded-full py-2 px-4 border-2 border-blue bg-white text-blue text-base font-normal leading-[1.4] hover:bg-sky-100 hover:border-blue hover:text-blue"> | ||
| <LongLeftIcon /> | ||
| Back to home | ||
| </a> | ||
| </Link> | ||
| {right != null ? ( | ||
| <div className="shrink-0 flex items-center">{right}</div> | ||
| ) : null} | ||
| </> | ||
| ); | ||
| }; |
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these changes are not really relevant tbh, because these endpoints are deprecated, changes are just made to avoid typing errors
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you thank you |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| import { ParsedUrlQuery } from "node:querystring"; | ||
|
|
||
| export const extractShortAnswerData = (shortAnswerJSON: any) => { | ||
| const extractedQuestions = shortAnswerJSON.map( | ||
| (dict: { [key: string]: string }) => dict.question, | ||
|
|
@@ -10,17 +12,17 @@ export const extractShortAnswerData = (shortAnswerJSON: any) => { | |
| return { extractedQuestions, extractedAnswers }; | ||
| }; | ||
|
|
||
| export const getReviewId = ( | ||
| query: Record<string, string | string[] | undefined>, | ||
| ): number => { | ||
| const reviewId = | ||
| typeof query["reviewId"] === "string" | ||
| ? parseInt(query["reviewId"]) | ||
| : (() => { | ||
| throw new Error("reviewId must be a String"); | ||
| })(); | ||
| if (Number.isNaN(reviewId)) | ||
| throw Error("reviewId must be parsable into an int"); | ||
|
Comment on lines
-13
to
-23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is still kinda janky tbh, idk if there is a good pattern to parse URLs
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the cleanest way to do something like this is to use a query validator that is typed like zod
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. apart from that tho it's fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oooh i kinda want to look in this and maybe some other native react options when we move to new repo. let's punt this for after migration? |
||
| export const getApplicantRecordId = (query: ParsedUrlQuery): string => { | ||
| const parsedQuery = query.applicantRecordId; | ||
| if (!parsedQuery) { | ||
| throw new Error("Applicant record ID is required to access review page."); | ||
| } | ||
|
|
||
| if (Array.isArray(parsedQuery)) { | ||
| throw new Error( | ||
| "Multiple applicant record IDs provided. Only one is expected.", | ||
| ); | ||
| } | ||
|
|
||
| return reviewId; | ||
| return parsedQuery; | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.