Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions frontend/app/[locale]/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getUserQuizStats } from '@/db/queries/quiz';
import { ProfileCard } from '@/components/dashboard/ProfileCard';
import { StatsCard } from '@/components/dashboard/StatsCard';
import { QuizSavedBanner } from '@/components/dashboard/QuizSavedBanner';
import { PostAuthQuizSync } from "@/components/auth/PostAuthQuizSync";

export const metadata = {
title: 'Dashboard | DevLovers',
Expand All @@ -28,9 +29,9 @@ export default async function DashboardPage({ params }: { params: Promise<{ loca
const averageScore =
totalAttempts > 0
? Math.round(
attempts.reduce((acc, curr) => acc + Number(curr.percentage), 0) /
totalAttempts
)
attempts.reduce((acc, curr) => acc + Number(curr.percentage), 0) /
totalAttempts
)
: 0;

const lastActiveDate =
Expand All @@ -57,6 +58,7 @@ export default async function DashboardPage({ params }: { params: Promise<{ loca

return (
<main className="relative min-h-[calc(100vh-80px)] overflow-hidden">
<PostAuthQuizSync />
<div
className="absolute inset-0 pointer-events-none -z-10"
aria-hidden="true"
Expand Down
48 changes: 0 additions & 48 deletions frontend/app/[locale]/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { useLocale } from "next-intl";
import { Link } from "@/i18n/routing";
import { useState } from "react";
import { useSearchParams } from "next/navigation";
import {
getPendingQuizResult,
clearPendingQuizResult,
} from "@/lib/quiz/guest-quiz";
import { Button } from "@/components/ui/button";
import { OAuthButtons } from "@/components/auth/OAuthButtons";

Expand Down Expand Up @@ -74,50 +70,6 @@ export default function LoginPage() {
return;
}

const pendingResult = getPendingQuizResult();

if (pendingResult && data?.userId) {
try {
const quizRes = await fetch("/api/quiz/guest-result", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
userId: data.userId,
quizId: pendingResult.quizId,
answers: pendingResult.answers,
violations: pendingResult.violations,
timeSpentSeconds:
pendingResult.timeSpentSeconds,
}),
});

if (!quizRes.ok) {
throw new Error(
`Failed to save quiz result: ${quizRes.status}`
);
}

const result = await quizRes.json();

if (result.success) {
sessionStorage.setItem(
"quiz_just_saved",
JSON.stringify({
score: result.score,
total: result.totalQuestions,
percentage: result.percentage,
pointsAwarded: result.pointsAwarded,
quizSlug: pendingResult.quizSlug,
})
);
}
} catch (err) {
console.error("Failed to save quiz result:", err);
} finally {
clearPendingQuizResult();
}
}

const redirectTarget =
returnTo && isSafeRedirectUrl(returnTo)
? returnTo
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/api/auth/google/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,5 @@ export async function GET(req: NextRequest) {

await setAuthCookie(token);

return NextResponse.redirect(new URL("/", req.url));
return NextResponse.redirect(new URL("/dashboard", req.url));
}
26 changes: 17 additions & 9 deletions frontend/app/api/quiz/guest-result/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { NextResponse } from "next/server";
import { getCurrentUser } from '@/lib/auth';
import { getCurrentUser } from "@/lib/auth";
import { db } from "@/db";
import { quizAttempts, quizAttemptAnswers, quizQuestions, quizAnswers } from "@/db/schema/quiz";
import {
quizAttempts,
quizAttemptAnswers,
quizQuestions,
quizAnswers,
} from "@/db/schema/quiz";
import { awardQuizPoints, calculateQuizPoints } from "@/db/queries/points";
import { eq, inArray } from "drizzle-orm";

Expand All @@ -17,23 +22,25 @@ export async function POST(req: Request) {
);
}

const { userId, quizId, answers, violations, timeSpentSeconds } = body;
const { quizId, answers, violations, timeSpentSeconds } = body;

if (!userId || !quizId || !Array.isArray(answers) || answers.length === 0) {
if (!quizId || !Array.isArray(answers) || answers.length === 0) {
return NextResponse.json(
{ success: false, error: "Invalid input" },
{ status: 400 }
);
}

const session = await getCurrentUser();
if (!session || session.id !== userId) {
if (!session) {
return NextResponse.json(
{ success: false, error: "Unauthorized" },
{ status: 401 }
);
}

const userId = session.id;

const questionRows = await db
.select({ id: quizQuestions.id })
.from(quizQuestions)
Expand Down Expand Up @@ -114,14 +121,13 @@ export async function POST(req: Request) {
);
}

const isCorrect = record.isCorrect;
if (isCorrect) correctAnswersCount++;
if (record.isCorrect) correctAnswersCount++;

attemptAnswers.push({
attemptId: "",
quizQuestionId: answer.questionId,
selectedAnswerId: answer.selectedAnswerId,
isCorrect,
isCorrect: record.isCorrect,
answeredAt: now,
});
}
Expand All @@ -132,10 +138,12 @@ export async function POST(req: Request) {
const integrityScore = Math.max(0, 100 - violationsArray.length * 10);
const safeTimeSpentSeconds = Math.max(0, Number(timeSpentSeconds) || 0);
const startedAt = new Date(now.getTime() - safeTimeSpentSeconds * 1000);

const pointsEarned = calculateQuizPoints({
score: correctAnswersCount,
integrityScore,
});

try {
const [attempt] = await db
.insert(quizAttempts)
Expand Down Expand Up @@ -185,4 +193,4 @@ export async function POST(req: Request) {
{ status: 500 }
);
}
}
}
63 changes: 63 additions & 0 deletions frontend/components/auth/PostAuthQuizSync.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"use client";

import { useEffect } from "react";
import { useRouter } from "next/navigation";
import {
getPendingQuizResult,
clearPendingQuizResult,
} from "@/lib/quiz/guest-quiz";


export function PostAuthQuizSync() {
const router = useRouter();

useEffect(() => {

queueMicrotask(() => {
const pendingResult = getPendingQuizResult();
if (!pendingResult) return;

(async () => {
try {
const res = await fetch("/api/quiz/guest-result", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
quizId: pendingResult.quizId,
answers: pendingResult.answers,
violations: pendingResult.violations,
timeSpentSeconds: pendingResult.timeSpentSeconds,
}),
});

if (!res.ok) {
throw new Error(`Failed to save quiz result (${res.status})`);
}

const result = await res.json();

if (!result?.success) {
throw new Error("Quiz save did not succeed");
}

sessionStorage.setItem(
"quiz_just_saved",
JSON.stringify({
score: result.score,
total: result.totalQuestions,
percentage: result.percentage,
pointsAwarded: result.pointsAwarded,
quizSlug: pendingResult.quizSlug,
})
);
clearPendingQuizResult();
router.refresh();
} catch (error) {
console.error("Failed to sync guest quiz result:", error);
}
})();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
}, [router]);

return null;
}
Loading