Skip to content

Commit

Permalink
Link attempt to backend
Browse files Browse the repository at this point in the history
  • Loading branch information
chunweii committed Nov 1, 2023
1 parent 1d8e0eb commit 88a6ef0
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 11 deletions.
11 changes: 9 additions & 2 deletions frontend/src/hooks/useHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { AuthContext } from "@/contexts/AuthContext";
import {
getAttemptsOfUser,
createAttemptOfUser,
} from "./../pages/api/historyHandler";
getAttemptById,
} from "@/pages/api/historyHandler";

type AttemptData = {
uid: string;
Expand All @@ -27,5 +28,11 @@ export const useHistory = () => {
}
};

return { fetchAttempts, postAttempt };
const fetchAttempt = async (attemptId: string) => {
if (authIsReady) {
return getAttemptById(currentUser, attemptId);
}
}
;
return { fetchAttempts, fetchAttempt, postAttempt };
};
31 changes: 30 additions & 1 deletion frontend/src/pages/api/historyHandler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { userApiPathAddress } from "@/gateway-address/gateway-address";
import { Attempt } from "@/types/UserTypes";

export const getAttemptsOfUser = async (user: any, uid: string) => {
try {
Expand Down Expand Up @@ -30,9 +31,37 @@ export const getAttemptsOfUser = async (user: any, uid: string) => {
}
};

export const getAttemptById = async (user: any, attemptId: string) => {
try {
const url = `${userApiPathAddress}attempt/${attemptId}`;
const idToken = await user.getIdToken(true);

const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
"User-Id-Token": idToken,
},
});

if (!response.ok) {
throw new Error(`Unable to get attempt: ${await response.text()}`);
}
return response.json().then(val => {
val["time_saved_at"] = new Date(val["time_saved_at"]);
val["time_updated"] = new Date(val["time_updated"]);
val["time_created"] = new Date(val["time_created"]);
return val
});
} catch (error) {
console.error("There was an error fetching the attempt", error);
throw error;
}
};

export const createAttemptOfUser = async (user: any, data: any) => {
try {
const url = `${userApiPathAddress}api/attempt`;
const url = `${userApiPathAddress}attempt`;
const idToken = await user.getIdToken(true);

const response = await fetch(url, {
Expand Down
59 changes: 53 additions & 6 deletions frontend/src/pages/attempt/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,50 @@ import { Textarea } from "@/components/ui/textarea";
import { TypographyBody, TypographyCode, TypographyH2 } from "@/components/ui/typography";
import { ArrowLeft } from "lucide-react";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { Attempt } from "@/types/UserTypes";
import { useHistory } from "@/hooks/useHistory";
import { useQuestions } from "@/hooks/useQuestions";
import { Question } from "@/types/QuestionTypes";
import { DotWave } from "@uiball/loaders";

export default function Page() {
const router = useRouter();
const attemptId = router.query.id;
const { fetchAttempt } = useHistory();
const { fetchQuestion } = useQuestions();
const [attempt, setAttempt] = useState<Attempt>();
const [question, setQuestion] = useState<Question>();
const [loadingState, setLoadingState] = useState<"loading" | "error" | "success">("loading");

useEffect(() => {
if (attemptId === undefined || Array.isArray(attemptId)) {
router.push("/profile");
return;
}
fetchAttempt(attemptId).then((attempt) => {
if (attempt) {
setAttempt(attempt);
return fetchQuestion(attempt.question_id);
} else {
throw new Error("Attempt not found");
}
}).then((question) => {
if (question) {
setQuestion(question);
setLoadingState("success");
} else {
throw new Error("Question not found");
}
}).catch((err: any) => {
setLoadingState("error");
console.log(err);
});
}, [attemptId]);

if (attemptId === undefined || Array.isArray(attemptId)) {
return null;
}

return (
<div className="min-h-screen p-12 mx-auto max-w-3xl flex flex-col gap-8">
Expand All @@ -17,27 +58,33 @@ export default function Page() {
<TypographyH2>Attempt</TypographyH2>
</div>

{ loadingState === "loading" ? <div className="h-32 flex items-center justify-center">
<DotWave
size={47}
speed={1}
color="white"
/></div> : loadingState === "error" ? <TypographyBody>Error</TypographyBody> : <>
<div>
<Label className="text-primary">Question</Label>
<TypographyBody>Two Sum</TypographyBody>
<TypographyBody>{question?.title}</TypographyBody>
</div>

<div>
<Label className="text-primary">Attempted At</Label>
<TypographyBody>23/10/2023 15:00</TypographyBody>
<TypographyBody>{attempt?.time_updated.toLocaleString()}</TypographyBody>
</div>

<div>
<Label className="text-primary">Mode of Attempt</Label>
<TypographyBody>Interview • with Chun Wei</TypographyBody>
<TypographyBody>{attempt?.room_id ? "Interview" : "Solo"}</TypographyBody>
</div>

<div>
<Label className="text-primary">Solution</Label>
<TypographyBody>Solved</TypographyBody>
<Textarea disabled={true} className="my-4" defaultValue={'class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: numToIndex = {} for i in range(len(nums)): if target - nums[i] in numToIndex: return [numToIndex[target - nums[i]], i] numToIndex[nums[i]] = i return []'}>
<TypographyBody>{attempt?.solved ? "Solved": "Unsolved"}</TypographyBody>
<Textarea disabled={true} className="my-4" defaultValue={attempt?.answer || ""}>
</Textarea>
</div>
</div></>}
</div>
)
}
2 changes: 1 addition & 1 deletion frontend/src/pages/profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function Page() {
setAttempts(attempts);
setLoadingState("success");
}
}).catch(err => {
}).catch((err: any) => {
setLoadingState("error");
console.log(err);
});
Expand Down
1 change: 0 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ model Match {
chosenProgrammingLanguage String
questionId String?
createdAt DateTime @default(now())
questionId String?
}

model AppUser {
Expand Down
14 changes: 14 additions & 0 deletions services/user-service/src/db/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ const userDatabaseFunctions = {
}
},

async getAttemptById(attemptId: string) {
try {
const attempt = await prismaClient.attempt.findUnique({
where: {
id: attemptId,
},
});
return attempt;
} catch (error: any) {
console.error(`Error retrieving attempt: ${error.message}`);
throw error;
}
},

async createAttemptOfUser(data: {
uid: string;
question_id: string;
Expand Down
19 changes: 19 additions & 0 deletions services/user-service/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,25 @@ indexRouter.get(
}
);

indexRouter.get(
"/attempt/:attempt_id",
function (req: express.Request, res: express.Response) {
userDatabaseFunctions
.getAttemptById(req.params.attempt_id)
.then((result) => {
if (result === null) {
res.status(404).end();
} else {
res.status(200).json(result);
}
})
.catch(() => {
// Server side error such as database not being available
res.status(500).end();
});
}
);

indexRouter.post(
"/attempt",
function (req: express.Request, res: express.Response) {
Expand Down

0 comments on commit 88a6ef0

Please sign in to comment.