Commit
·
bae8ec6
1
Parent(s):
a774a10
Submitting.
Browse files
frontend/app/routes/jobs.$jid.assessments.$id.tsx
CHANGED
|
@@ -1,12 +1,15 @@
|
|
| 1 |
import { useParams } from "react-router";
|
| 2 |
import { Loader2Icon } from "lucide-react";
|
|
|
|
| 3 |
import { Label, RadioGroup } from "radix-ui";
|
|
|
|
| 4 |
import { Textarea } from "~/components/ui/textarea";
|
| 5 |
import { Checkbox } from "~/components/ui/checkbox";
|
| 6 |
import { RadioGroupItem } from "~/components/ui/radio-group";
|
| 7 |
import { AssessmentCard } from "~/components/assessment-card";
|
| 8 |
import type { Route } from "./+types/jobs.$jid.assessments.$id";
|
| 9 |
import { useGetJobAssessmentByID } from "~/services/useGetJobAssessmentByID";
|
|
|
|
| 10 |
|
| 11 |
export function meta({}: Route.MetaArgs) {
|
| 12 |
return [
|
|
@@ -21,6 +24,9 @@ export function meta({}: Route.MetaArgs) {
|
|
| 21 |
export default function AssessmentDetailRoute() {
|
| 22 |
const { jid, id } = useParams();
|
| 23 |
const { data: assessment, isLoading, isError, refetch } = useGetJobAssessmentByID({ jid: jid || "", id: id || "" });
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
if (isLoading) {
|
| 26 |
return (
|
|
@@ -49,8 +55,29 @@ export default function AssessmentDetailRoute() {
|
|
| 49 |
);
|
| 50 |
}
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
const totalWeights = assessment.questions.reduce((weights, question) => weights + question.weight, 0);
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
return (
|
| 55 |
<main className="container mx-auto p-4 flex flex-col gap-8">
|
| 56 |
<AssessmentCard jid={jid || ""} assessment={assessment} isStatic />
|
|
@@ -66,9 +93,9 @@ export default function AssessmentDetailRoute() {
|
|
| 66 |
</span>
|
| 67 |
</header>
|
| 68 |
{{
|
| 69 |
-
"text_based": <Textarea className="w-full resize-none" placeholder="Answer goes here..." />,
|
| 70 |
"choose_one": (
|
| 71 |
-
<RadioGroup.RadioGroup>
|
| 72 |
{question.options.map((option, i) => (
|
| 73 |
<div key={i} className="flex items-center gap-3">
|
| 74 |
<RadioGroupItem value={option.value} id={`${question.id}-option-${i}`} className="cursor-pointer" />
|
|
@@ -81,7 +108,18 @@ export default function AssessmentDetailRoute() {
|
|
| 81 |
<div>
|
| 82 |
{question.options.map((option, i) => (
|
| 83 |
<div key={i} className="flex items-center gap-3">
|
| 84 |
-
<Checkbox id={`${question.id}-option-${i}`} className="cursor-pointer"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
<Label.Label htmlFor={`${question.id}-option-${i}`} className="cursor-pointer">{option.text}</Label.Label>
|
| 86 |
</div>
|
| 87 |
))}
|
|
@@ -99,6 +137,14 @@ export default function AssessmentDetailRoute() {
|
|
| 99 |
))}
|
| 100 |
</div>
|
| 101 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
</main>
|
| 103 |
);
|
| 104 |
}
|
|
|
|
| 1 |
import { useParams } from "react-router";
|
| 2 |
import { Loader2Icon } from "lucide-react";
|
| 3 |
+
import { useEffect, useState } from "react";
|
| 4 |
import { Label, RadioGroup } from "radix-ui";
|
| 5 |
+
import { Button } from "~/components/ui/button";
|
| 6 |
import { Textarea } from "~/components/ui/textarea";
|
| 7 |
import { Checkbox } from "~/components/ui/checkbox";
|
| 8 |
import { RadioGroupItem } from "~/components/ui/radio-group";
|
| 9 |
import { AssessmentCard } from "~/components/assessment-card";
|
| 10 |
import type { Route } from "./+types/jobs.$jid.assessments.$id";
|
| 11 |
import { useGetJobAssessmentByID } from "~/services/useGetJobAssessmentByID";
|
| 12 |
+
import { usePostAssessmentApplication } from "~/services/usePostAssessmentApplication";
|
| 13 |
|
| 14 |
export function meta({}: Route.MetaArgs) {
|
| 15 |
return [
|
|
|
|
| 24 |
export default function AssessmentDetailRoute() {
|
| 25 |
const { jid, id } = useParams();
|
| 26 |
const { data: assessment, isLoading, isError, refetch } = useGetJobAssessmentByID({ jid: jid || "", id: id || "" });
|
| 27 |
+
const [answers, setAnswers] = useState({} as Record<string, any>);
|
| 28 |
+
|
| 29 |
+
const { mutate, isPending: isSubmittingLoading, isError: isSubmittingError } = usePostAssessmentApplication();
|
| 30 |
|
| 31 |
if (isLoading) {
|
| 32 |
return (
|
|
|
|
| 55 |
);
|
| 56 |
}
|
| 57 |
|
| 58 |
+
useEffect(() => {
|
| 59 |
+
if (assessment == null) { return }
|
| 60 |
+
|
| 61 |
+
setAnswers(assessment.questions.reduce((accumulator, question) => {
|
| 62 |
+
accumulator[question.id] = question.type === "choose_many" ? [] : "";
|
| 63 |
+
return accumulator;
|
| 64 |
+
}, {} as Record<string, any>));
|
| 65 |
+
}, [assessment]);
|
| 66 |
+
|
| 67 |
const totalWeights = assessment.questions.reduce((weights, question) => weights + question.weight, 0);
|
| 68 |
|
| 69 |
+
function handleSubmit() {
|
| 70 |
+
mutate({
|
| 71 |
+
job_id: jid || "",
|
| 72 |
+
assessment_id: id || "",
|
| 73 |
+
user_id: "",
|
| 74 |
+
answers: Object.entries(answers).map(([question_id, answer]) => ({
|
| 75 |
+
question_id,
|
| 76 |
+
[typeof answer == "string" ? "text" : "options"]: typeof answer == "string" ? answer : Array.isArray(answer) ? answer : [answer],
|
| 77 |
+
})),
|
| 78 |
+
})
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
return (
|
| 82 |
<main className="container mx-auto p-4 flex flex-col gap-8">
|
| 83 |
<AssessmentCard jid={jid || ""} assessment={assessment} isStatic />
|
|
|
|
| 93 |
</span>
|
| 94 |
</header>
|
| 95 |
{{
|
| 96 |
+
"text_based": <Textarea className="w-full resize-none" placeholder="Answer goes here..." value={answers[question.id]} onChange={e => setAnswers(prev => ({ ...prev, [question.id]: e.target.value }))} />,
|
| 97 |
"choose_one": (
|
| 98 |
+
<RadioGroup.RadioGroup value={answers[question.id]} onValueChange={value => setAnswers(prev => ({ ...prev, [question.id]: value }))}>
|
| 99 |
{question.options.map((option, i) => (
|
| 100 |
<div key={i} className="flex items-center gap-3">
|
| 101 |
<RadioGroupItem value={option.value} id={`${question.id}-option-${i}`} className="cursor-pointer" />
|
|
|
|
| 108 |
<div>
|
| 109 |
{question.options.map((option, i) => (
|
| 110 |
<div key={i} className="flex items-center gap-3">
|
| 111 |
+
<Checkbox id={`${question.id}-option-${i}`} className="cursor-pointer" value={answers[question.id]?.includes(option.value)}
|
| 112 |
+
onCheckedChange={checked => {
|
| 113 |
+
setAnswers(prev => {
|
| 114 |
+
const currentSelections = prev[question.id] || [];
|
| 115 |
+
if (checked) {
|
| 116 |
+
return { ...prev, [question.id]: [...currentSelections, option.value] };
|
| 117 |
+
} else {
|
| 118 |
+
return { ...prev, [question.id]: currentSelections.filter((v: string) => v !== option.value) };
|
| 119 |
+
}
|
| 120 |
+
});
|
| 121 |
+
}}
|
| 122 |
+
/>
|
| 123 |
<Label.Label htmlFor={`${question.id}-option-${i}`} className="cursor-pointer">{option.text}</Label.Label>
|
| 124 |
</div>
|
| 125 |
))}
|
|
|
|
| 137 |
))}
|
| 138 |
</div>
|
| 139 |
</section>
|
| 140 |
+
<footer className="mx-auto py-4">
|
| 141 |
+
<Button
|
| 142 |
+
className="bg-indigo-600 text-white hover:bg-indigo-700 dark:bg-indigo-500 dark:hover:bg-indigo-600"
|
| 143 |
+
onClick={handleSubmit}
|
| 144 |
+
>
|
| 145 |
+
Submit Answers
|
| 146 |
+
</Button>
|
| 147 |
+
</footer>
|
| 148 |
</main>
|
| 149 |
);
|
| 150 |
}
|
frontend/app/services/usePostAssessmentApplication.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useMutation } from "@tanstack/react-query";
|
| 2 |
+
import { HTTPManager } from "~/managers/HTTPManager";
|
| 3 |
+
|
| 4 |
+
export const POST_ASSESSMENT_APPLICATION_KEY = "post-assessment-application";
|
| 5 |
+
|
| 6 |
+
export type PostAssessmentApplicationPayload = {
|
| 7 |
+
job_id: string;
|
| 8 |
+
user_id: string;
|
| 9 |
+
assessment_id: string;
|
| 10 |
+
answers: Array<{
|
| 11 |
+
question_id: string;
|
| 12 |
+
text?: string;
|
| 13 |
+
options?: Array<string>;
|
| 14 |
+
}>;
|
| 15 |
+
};
|
| 16 |
+
|
| 17 |
+
export const usePostAssessmentApplication = () => useMutation({
|
| 18 |
+
mutationKey: [POST_ASSESSMENT_APPLICATION_KEY],
|
| 19 |
+
mutationFn: async (payload: PostAssessmentApplicationPayload) =>
|
| 20 |
+
HTTPManager.post(`/applications/jobs/${payload.job_id}/assessments/${payload.assessment_id}`, payload).then(response => response.data),
|
| 21 |
+
});
|