Skip to content

Commit

Permalink
Refactor/review comments (#58)
Browse files Browse the repository at this point in the history
* Review comments and half solution

* implemented version of the submission-utils in the rest of the code

* Add types for online status

---------

Co-authored-by: Nico Jansen <[email protected]>
  • Loading branch information
JurreBrandsenInfoSupport and nicojs authored Mar 28, 2024
1 parent cdd01cf commit 7e63523
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 250 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }]
}
52 changes: 24 additions & 28 deletions src/components/mobile/survey-questions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {

import { InfoCircledIcon } from "@radix-ui/react-icons";

import { type AnswerOption, type Question } from "~/models/types";
import {
type AnswerOption,
type Question,
type SurveyResponse,
} from "~/models/types";

import {
FormControl,
Expand All @@ -17,33 +21,29 @@ import {
FormMessage,
} from "~/components/ui/form";

import { type Dispatch, type SetStateAction } from "react";
import { type Session } from "next-auth";
import { idToMoreInfo, idToTextMap } from "~/utils/optionMapping";

import { RadioGroup, RadioGroupItem } from "~/components/ui/radio-group";

import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { HandleResponseSelection } from "~/utils/survey-utils";
import { type useForm } from "react-hook-form";
import { findAnswerId } from "~/utils/survey-utils";

export function MobileSurveyQuestionnaire({
session,
filteredQuestions,
answerOptions,
form,
responses,
setResponses,
submitResponse,
saveAnswer,
currentAnswers,
}: {
session: Session;
filteredQuestions: Question[];
answerOptions: AnswerOption[];
form: ReturnType<typeof useForm>;
responses: Record<string, string>;
setResponses: Dispatch<SetStateAction<Record<string, string>>>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
submitResponse: any;
saveAnswer: (answer: SurveyResponse) => void;
currentAnswers: SurveyResponse[];
}) {
return (
<div>
Expand Down Expand Up @@ -71,35 +71,31 @@ export function MobileSurveyQuestionnaire({
<RadioGroup
onValueChange={async (value) => {
field.onChange(value);
try {
await HandleResponseSelection({
questionId: question.id,
answerId: value,
responses,
setResponses,
session,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
submitResponse,
});
} catch (error) {
console.error(
"Error in handleResponseSelection:",
error,
);
}
saveAnswer({
userId: session.user.id,
questionId: question.id,
answerId: value,
});
}}
value={field.value as string}
className="flex flex-col space-y-1"
>
<label
className={`flex cursor-pointer items-center space-x-2 rounded-lg p-2 ${field.value === option.id || responses[question.id] === option.id ? "bg-custom-selectedLight dark:bg-custom-selected" : "hover:bg-gray-100 dark:hover:bg-slate-900"}`}
className={`flex cursor-pointer items-center space-x-2 rounded-lg p-2 ${
field.value === option.id ||
findAnswerId(currentAnswers, question.id) ===
option.id
? "bg-custom-selectedLight dark:bg-custom-selected"
: "hover:bg-gray-100 dark:hover:bg-slate-900"
}`}
>
<FormControl>
<RadioGroupItem
value={option.id}
checked={
field.value === option.id ||
responses[question.id] === option.id
findAnswerId(currentAnswers, question.id) ===
option.id
}
/>
</FormControl>
Expand Down
109 changes: 37 additions & 72 deletions src/components/survey-questionnaire.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ import { MobileSurveyQuestionnaire } from "./mobile/survey-questions";
import { SurveyQuestions } from "./survey-questions";
import { MobileProgressionBar } from "./mobile/progression-bar";
import {
GenerateFormAndSchema,
useGenerateFormAndSchema,
getInitialResponses,
getNextHref,
SaveResponsesToDatabase,
onSubmit,
} from "~/utils/survey-utils";
import { toast } from "./ui/use-toast";
import { useSubmission } from "~/utils/submission-utils";
import { useSubmitAnswers } from "~/utils/submission-utils";
import { SpinnerButton } from "./ui/button-spinner";
import { Form } from "./ui/form";
import renderNotFoundPage from "~/app/[...not_found]/page";
Expand Down Expand Up @@ -55,74 +53,61 @@ export function SurveyQuestionnaire({
(role) => slugify(role.role) === currentRole,
);

const { isSubmitting, setIsSubmitting, submitResponse } = useSubmission();

const [responses, setResponses] = useState(
const [responses] = useState(
getInitialResponses(userAnswersForRole, currentRole, userSelectedRoles),
);

const screenSize = useScreenSize();
const { saveAnswer, isSubmitting, currentAnswers } = useSubmitAnswers();

// Dynamically generate the slugToId mapping
const slugToId: Record<string, string> = {};
userSelectedRoles.forEach((role) => {
slugToId[slugify(role.role)] = role.id;
});

const filteredQuestions = questions.filter(
(question) =>
question.roleIds?.some(
(roleId) => roleId === slugToId[currentRole ?? ""],
) && selectedRoles.includes(slugToId[currentRole ?? ""] ?? ""),
);

const isOnline = useOnlineStatus();
const unansweredQuestions = filteredQuestions.filter(
(question) =>
!userAnswersForRole.some((answer) => answer.question.id === question.id),
);

const [previouslyOffline, setPreviouslyOffline] = useState(false);
const { form } = useGenerateFormAndSchema(
unansweredQuestions,
answerOptions,
responses,
);

useEffect(() => {
console.log("isOnline", isOnline);
const handleOnline = async () => {
if (previouslyOffline) {
try {
await SaveResponsesToDatabase(responses, session, submitResponse);
} catch (error) {
console.error("Error in online submission:", error);
}
setPreviouslyOffline(false);
}
};

if (!isOnline) {
setPreviouslyOffline(true);
} else {
handleOnline().catch(() => {
console.log("Failed to save responses");
});
}
}, [isOnline, previouslyOffline, responses, session, submitResponse]);
const screenSize = useScreenSize();

const onlineStatus = useOnlineStatus();

useEffect(() => {
if (isOnline && previouslyOffline) {
if (onlineStatus === "isBackOnline") {
toast({
title: "Back online!",
description:
"Your (intermediate) responses have been submitted successfully.",
});
} else if (!isOnline) {
} else if (onlineStatus === "isOffline") {
toast({
title: "Failed to save responses. Retrying...",
description:
"Data submission in progress... Your responses will be automatically submitted once you're back online. Feel free to continue filling out the survey.",
variant: "informative",
});
}
}, [isOnline, previouslyOffline]);
}, [onlineStatus]);

if (!roleExists) {
return renderNotFoundPage();
}

// Dynamically generate the slugToId mapping
const slugToId: Record<string, string> = {};
userSelectedRoles.forEach((role) => {
slugToId[slugify(role.role)] = role.id;
});

const filteredQuestions = questions.filter(
(question) =>
question.roleIds?.some(
(roleId) => roleId === slugToId[currentRole ?? ""],
) && selectedRoles.includes(slugToId[currentRole ?? ""] ?? ""),
);

// function that check if a user already has more than 1 response for a question
function hasAnsweredAllQuestionsForRole(
userAnswersForRole: QuestionResult[],
Expand All @@ -144,17 +129,6 @@ export function SurveyQuestionnaire({
return answeredQuestionsForRole.length >= totalQuestionsForRole;
}

const unansweredQuestions = filteredQuestions.filter(
(question) =>
!userAnswersForRole.some((answer) => answer.question.id === question.id),
);

const { form } = GenerateFormAndSchema(
unansweredQuestions,
answerOptions,
responses,
);

const selectedRolesForProgressBar = userSelectedRoles
.sort((a, b) => {
const roleA = a.role.toLowerCase();
Expand Down Expand Up @@ -192,15 +166,7 @@ export function SurveyQuestionnaire({
<form
onSubmit={form.handleSubmit(
async () => {
setIsSubmitting(true);
onSubmit(
form.getValues(),
session,
selectedRolesForProgressBar,
submitResponse,
).catch((error) => {
console.error("Error in form submission:", error);
});
// Do nothing
},
(errors) => {
// scroll to the first error
Expand All @@ -220,14 +186,13 @@ export function SurveyQuestionnaire({
filteredQuestions={filteredQuestions}
answerOptions={answerOptions}
form={form}
responses={responses}
setResponses={setResponses}
submitResponse={submitResponse}
saveAnswer={saveAnswer}
currentAnswers={currentAnswers}
/>
<SpinnerButton
type="submit"
state={isSubmitting || submitResponse.isLoading || !isOnline}
disabled={isSubmitting || submitResponse.isLoading || !isOnline}
state={isSubmitting || onlineStatus === "isOffline"}
disabled={isSubmitting || onlineStatus === "isOffline"}
name={getNextHref(selectedRolesForProgressBar) ? "Next" : "Submit"}
/>
</form>
Expand Down
48 changes: 18 additions & 30 deletions src/components/survey-questions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import {
HoverCardTrigger,
} from "~/components/ui/hover-card";

import { type AnswerOption, type Question } from "~/models/types";
import {
type AnswerOption,
type Question,
type SurveyResponse,
} from "~/models/types";

import {
FormControl,
Expand All @@ -27,28 +31,24 @@ import {
TableHeader,
TableRow,
} from "~/components/ui/table";
import { HandleResponseSelection } from "~/utils/survey-utils";
import { type Dispatch, type SetStateAction } from "react";
import { InfoCircledIcon } from "@radix-ui/react-icons";
import { type useForm } from "react-hook-form";
import { findAnswerId } from "~/utils/survey-utils";

export function SurveyQuestions({
session,
filteredQuestions,
answerOptions,
form,
responses,
setResponses,
submitResponse,
saveAnswer,
currentAnswers,
}: {
session: Session;
filteredQuestions: Question[];
answerOptions: AnswerOption[];
form: ReturnType<typeof useForm>;
responses: Record<string, string>;
setResponses: Dispatch<SetStateAction<Record<string, string>>>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
submitResponse: any;
saveAnswer: (answer: SurveyResponse) => void;
currentAnswers: SurveyResponse[];
}) {
return (
<Table divClassname="">
Expand Down Expand Up @@ -96,18 +96,16 @@ export function SurveyQuestions({
: ""
}
>
{/* add a dashed border of 1px in color red in case of validation error */}
<TableCell>
{question.questionText}
<FormMessage />
</TableCell>
{answerOptions.map((option) => (
<TableCell key={option.id} className="w-[300px]">
{/* add a dashed border of 1px in color red in case of validation error */}
<label
className={`${
field.value === option.id ||
responses[question.id] === option.id
findAnswerId(currentAnswers, question.id) === option.id
? "rounded-lg border-2 border-custom-selected "
: ""
}flex h-[40px] cursor-pointer items-center justify-center`}
Expand All @@ -117,22 +115,11 @@ export function SurveyQuestions({
<RadioGroup
onValueChange={async (value) => {
field.onChange(value);
try {
await HandleResponseSelection({
questionId: question.id,
answerId: value,
responses,
setResponses,
session,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
submitResponse,
});
} catch (error) {
console.error(
"Error in handleResponseSelection:",
error,
);
}
saveAnswer({
userId: session.user.id,
questionId: question.id,
answerId: value,
});
}}
value={field.value as string}
className="flex flex-col space-y-1"
Expand All @@ -142,7 +129,8 @@ export function SurveyQuestions({
value={option.id}
checked={
field.value === option.id ||
responses[question.id] === option.id
findAnswerId(currentAnswers, question.id) ===
option.id
}
/>
</FormControl>
Expand Down
Loading

0 comments on commit 7e63523

Please sign in to comment.