Skip to content

Commit

Permalink
Frontend form error handling (#125)
Browse files Browse the repository at this point in the history
* Added popup and (flawed) toast popup

* buggy implementation

* fixed bug of page rerouting

* Descriptive submission failure form

* updated minor changes for organzation

* updated cycle review form logic

* reformatted with prettier

---------

Co-authored-by: Xiaole Su <[email protected]>
  • Loading branch information
Yasoop and suxls authored Feb 20, 2025
1 parent f1d139a commit dc8a790
Show file tree
Hide file tree
Showing 3 changed files with 12,548 additions and 6,788 deletions.
76 changes: 66 additions & 10 deletions apps/web/src/app/_components/form/review-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { WorkEnvironment, WorkTerm } from "@cooper/db/schema";
import { cn } from "@cooper/ui";
import { Button } from "@cooper/ui/button";
import { Form } from "@cooper/ui/form";
import { useToast } from "@cooper/ui/hooks/use-toast";
import { CheckIcon } from "@cooper/ui/icons";

import {
Expand All @@ -23,6 +24,7 @@ import {
} from "~/app/_components/form/sections";
import { SubmissionConfirmation } from "~/app/_components/form/submission-confirmation";
import { api } from "~/trpc/react";
import { SubmissionFailure } from "./submission-failure";

const formSchema = z.object({
workTerm: z.nativeEnum(WorkTerm, {
Expand Down Expand Up @@ -227,9 +229,40 @@ export function ReviewForm(props: ReviewFormProps) {
});

const [currentStep, setCurrentStep] = useState<number>(0);
const [validForm, setValidForm] = useState(true);
const [errorMessage, setErrorMessage] = useState<string | null>(null);

const { toast } = useToast();

type FieldName = keyof z.infer<typeof formSchema>;

const profile = api.profile.getCurrentUser.useQuery(undefined, {
refetchOnWindowFocus: false,
});
const profileId = profile.data?.id;

const reviews = api.review.getByProfile.useQuery(
{ id: profileId ?? "" },
{
enabled: !!profileId,
},
);

const canReviewForTerm = (): boolean => {
if (!reviews.data) return false;

const currentTerm = form.getValues("workTerm");
const currentYear = form.getValues("workYear");

const reviewsForCurrentTerm = reviews.data.filter(
(review) =>
String(review.workTerm) === currentTerm &&
review.workYear === Number(currentYear),
);

return reviewsForCurrentTerm.length < 2;
};

const next = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
const fields = steps[currentStep - 1]?.fields;
Expand All @@ -241,6 +274,11 @@ export function ReviewForm(props: ReviewFormProps) {
return;
}

if (currentStep === 1 && !canReviewForTerm()) {
alert("You have already submitted too many reviews for this term!");
return;
}

// FIXME: Fix the scrolling eslint issue

if (currentStep <= steps.length) {
Expand All @@ -262,20 +300,38 @@ export function ReviewForm(props: ReviewFormProps) {
scroll.scrollToTop({ duration: 250, smooth: true });
};

const mutation = api.review.create.useMutation();
const mutation = api.review.create.useMutation({
onError: (error) => {
setValidForm(false);
setErrorMessage(error.message || "An unknown error occurred.");

function onSubmit(values: z.infer<ReviewFormType>) {
mutation.mutate({
roleId: props.roleId,
profileId: props.profileId,
companyId: props.company.id,
...values,
});
toast({
title: "Submission Error",
description: error.message || "Something went wrong. Please try again.",
variant: "destructive",
});
},
});

async function onSubmit(values: z.infer<ReviewFormType>) {
try {
await mutation.mutateAsync({
roleId: props.roleId,
profileId: props.profileId,
companyId: props.company.id,
...values,
});
} catch (error) {
console.error("Mutation failed:", error);
}
}

if (currentStep === steps.length + 1) {
// Also check if the mutation is successful before displaying this. Otherwise, show a loading spinner.
return <SubmissionConfirmation />;
if (validForm) {
return <SubmissionConfirmation />;
} else {
return <SubmissionFailure message={errorMessage ?? undefined} />;
}
}

if (currentStep === 0) {
Expand Down
28 changes: 28 additions & 0 deletions apps/web/src/app/_components/form/submission-failure.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Link from "next/link";

import { Button } from "@cooper/ui/button";

interface SubmissionFailureProps {
message?: string; // Allow passing an optional error message
}

export function SubmissionFailure({ message }: SubmissionFailureProps) {
return (
<div className="flex flex-col border-2">
<div className="z-10 -mb-4 h-4 w-full rounded-t-xl bg-gradient-to-r from-rose-400 via-yellow-400 to-blue-600" />
<div className="flex h-[80vh] w-full flex-col items-center justify-center space-y-6 rounded-xl bg-white px-8 py-32 text-cooper-blue-600">
<div className="text-5xl font-bold">
Whoops, your review is invalid...
</div>
<div className="max-w-3xl text-center text-2xl">
{message ? message : "Something went wrong. Please try again."}
</div>
<div className="flex justify-between space-x-8">
<Link href="/">
<Button>Return to home </Button>
</Link>
</div>
</div>
</div>
);
}
Loading

0 comments on commit dc8a790

Please sign in to comment.