Skip to content

Commit 6465064

Browse files
authored
Merge pull request #3 from lakshayg2005/errors
Major Features done
2 parents 9b38563 + 9df16a8 commit 6465064

37 files changed

+2785
-780
lines changed

package-lock.json

Lines changed: 696 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
"seed": "ts-node src/lib/syncData.ts"
1111
},
1212
"dependencies": {
13+
"@emotion/react": "^11.14.0",
14+
"@emotion/styled": "^11.14.1",
1315
"@headlessui/react": "^2.2.2",
1416
"@heroicons/react": "^2.2.0",
1517
"@hookform/resolvers": "^3.10.0",
18+
"@mui/material": "^7.2.0",
1619
"@next/swc-wasm-nodejs": "13.5.1",
1720
"@radix-ui/react-accordion": "^1.2.0",
1821
"@radix-ui/react-alert-dialog": "^1.1.1",
@@ -65,6 +68,7 @@
6568
"react-day-picker": "^8.10.1",
6669
"react-dom": "18.2.0",
6770
"react-hook-form": "^7.56.1",
71+
"react-hot-toast": "^2.5.2",
6872
"react-resizable-panels": "^2.1.3",
6973
"recharts": "^2.12.7",
7074
"sonner": "^1.5.0",

src/app/auth/callback/page.tsx

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,72 @@
33
import { useEffect, useState } from 'react';
44
import { useRouter } from 'next/navigation';
55
import { handleAuthCallback } from '@/lib/supabase-auth';
6+
import { useAuth } from "@/contexts/AuthContext";
67

78
export default function AuthCallback() {
89
const router = useRouter();
10+
const { refreshAnonymousId } = useAuth(); // ❌ don't wait for context `isReady` or `user`
911
const [message, setMessage] = useState("Processing login...");
1012
const [error, setError] = useState<string | null>(null);
13+
const [hasRun, setHasRun] = useState(false);
1114

1215
useEffect(() => {
16+
let mounted = true;
17+
1318
const processAuth = async () => {
19+
if (!mounted || hasRun) return;
20+
setHasRun(true);
21+
1422
try {
15-
// Process the auth callback and create anonymous identity
1623
const { user, anonymousId, error } = await handleAuthCallback();
17-
24+
1825
if (error) {
19-
console.error('Auth callback error:', error);
20-
setMessage('Authentication failed.');
21-
setError(error.message);
22-
return;
26+
if (error.code === '23505') {
27+
console.warn('User exists. Proceeding.');
28+
} else if (error.message?.includes('expired') || error.status === 401) {
29+
setMessage('Session expired. Try signing in again.');
30+
setError(error.message);
31+
return;
32+
} else {
33+
setMessage('Authentication failed.');
34+
setError(error.message);
35+
return;
36+
}
2337
}
24-
38+
2539
if (!user || !anonymousId) {
26-
setMessage('Authentication failed: User data incomplete.');
27-
setError('Unable to complete authentication process.');
40+
setMessage('Missing user or anonymousId.');
41+
setError('Could not complete authentication.');
2842
return;
2943
}
30-
31-
// Success! Redirect to verification page
44+
3245
setMessage('Authentication successful! Redirecting...');
33-
34-
// Short timeout to show success message before redirect
46+
await refreshAnonymousId();
47+
48+
window.history.replaceState({}, document.title, window.location.pathname);
49+
3550
setTimeout(() => {
3651
router.push('/auth/verify');
37-
}, 1500);
38-
52+
}, 1000);
3953
} catch (err) {
40-
console.error('Unexpected auth error:', err);
41-
setMessage('An unexpected error occurred.');
54+
setMessage('Unexpected error.');
4255
setError(err instanceof Error ? err.message : 'Unknown error');
4356
}
4457
};
4558

4659
processAuth();
47-
}, [router]);
60+
61+
return () => {
62+
mounted = false;
63+
};
64+
}, [router, refreshAnonymousId, hasRun]);
4865

4966
return (
5067
<div className="flex min-h-screen flex-col items-center justify-center p-4">
5168
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
5269
<h1 className="mb-6 text-2xl font-bold text-gray-800">Authentication</h1>
53-
54-
<div className="mb-4 text-lg">A {message}</div>
55-
70+
<div className="mb-4 text-lg">{message}</div>
71+
5672
{error && (
5773
<div className="rounded-md bg-red-50 p-4 text-sm text-red-700">
5874
<p>Error: {error}</p>
@@ -69,4 +85,4 @@ export default function AuthCallback() {
6985
</div>
7086
</div>
7187
);
72-
}
88+
}

src/app/auth/signin/page.tsx

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,43 @@ import Link from 'next/link';
55
import { useAuth } from '@/contexts/AuthContext';
66

77
export default function SignIn() {
8-
const { signIn,signInWithGoogle, isLoading } = useAuth();
8+
const { signIn, signInWithGoogle, isLoading } = useAuth();
99
const [email, setEmail] = useState('');
10-
const [message, setMessage] = useState<{text: string; type: 'success' | 'error'} | null>(null);
10+
const [message, setMessage] = useState<{ text: string; type: 'success' | 'error' } | null>(null);
1111

1212
const handleSubmit = async (e: React.FormEvent) => {
1313
e.preventDefault();
14-
14+
1515
if (!email.trim()) {
1616
setMessage({
1717
text: 'Please enter your email address.',
18-
type: 'error'
18+
type: 'error',
1919
});
2020
return;
2121
}
2222

2323
try {
2424
const { error } = await signIn(email);
25-
25+
2626
if (error) {
2727
console.error('Sign in error:', error);
2828
setMessage({
2929
text: `Failed to send magic link: ${error.message}`,
30-
type: 'error'
30+
type: 'error',
3131
});
3232
return;
3333
}
34-
34+
3535
setMessage({
3636
text: 'Magic link sent! Check your email for a login link.',
37-
type: 'success'
37+
type: 'success',
3838
});
3939
setEmail('');
40-
4140
} catch (err) {
4241
console.error('Unexpected sign in error:', err);
4342
setMessage({
4443
text: 'An unexpected error occurred. Please try again.',
45-
type: 'error'
44+
type: 'error',
4645
});
4746
}
4847
};
@@ -51,21 +50,24 @@ export default function SignIn() {
5150
<div className="flex min-h-screen flex-col items-center justify-center p-4">
5251
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
5352
<h1 className="mb-6 text-2xl font-bold text-gray-800">Sign In</h1>
53+
54+
{/* ✅ FIX: Replaced nested button */}
5455
<button
5556
onClick={signInWithGoogle}
5657
disabled={isLoading}
57-
className="mb-4 w-full rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50"
58+
className="mb-4 flex w-full items-center justify-center gap-2 rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50"
5859
>
59-
<button className="flex items-center justify-center gap-2 ...">
60-
<img src="/google-tile.svg" alt="Google" className="w-5 h-5" />
60+
<img src="/google-tile.svg" alt="Google" className="h-5 w-5" />
6161
Continue with Google
62-
</button>
6362
</button>
6463

64+
{/* Magic link form (optional) */}
65+
{/*
6566
<div className="relative mb-4 text-center text-sm text-gray-500">
66-
<span className="px-2 bg-white relative z-10">or</span>
67-
<div className="absolute left-0 top-1/2 w-full border-t border-gray-300 -translate-y-1/2"></div>
67+
<span className="relative z-10 bg-white px-2">or</span>
68+
<div className="absolute left-0 top-1/2 w-full -translate-y-1/2 border-t border-gray-300"></div>
6869
</div>
70+
6971
<form onSubmit={handleSubmit} className="flex flex-col space-y-4">
7072
<div>
7173
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
@@ -82,7 +84,7 @@ export default function SignIn() {
8284
required
8385
/>
8486
</div>
85-
87+
8688
<div className="pt-2">
8789
<button
8890
type="submit"
@@ -93,7 +95,7 @@ export default function SignIn() {
9395
</button>
9496
</div>
9597
</form>
96-
98+
9799
{message && (
98100
<div
99101
className={`mt-4 rounded-md p-4 text-sm ${
@@ -105,16 +107,8 @@ export default function SignIn() {
105107
{message.text}
106108
</div>
107109
)}
108-
109-
<div className="mt-6 text-center text-sm text-gray-600">
110-
<p>
111-
No account needed. We'll send you a secure magic link to sign in.
112-
</p>
113-
<p className="mt-2">
114-
Your email is only used for authentication and is never stored with your ratings.
115-
</p>
116-
</div>
117-
110+
*/}
111+
118112
<div className="mt-8 border-t border-gray-200 pt-6 text-center text-sm text-gray-600">
119113
<Link href="/" className="font-medium text-indigo-600 hover:text-indigo-500">
120114
Return to Home
@@ -123,4 +117,4 @@ export default function SignIn() {
123117
</div>
124118
</div>
125119
);
126-
}
120+
}

src/app/auth/verify/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default function VerifyAuth() {
5555
</div>
5656

5757
<div className="mb-6">
58-
<h2 className="mb-2 text-lg font-medium text-gray-700">{anonymousId}</h2>
58+
{/* <h2 className="mb-2 text-lg font-medium text-gray-700">{anonymousId}</h2> */}
5959
<p className="mb-2 text-sm text-gray-600">
6060
This is your secure anonymous ID. It cannot be traced back to your email:
6161
</p>
Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,81 @@
1-
"use client"
1+
"use client";
22
import { notFound } from "next/navigation";
3-
import { Progress } from "@/components/ui/progress";
4-
import { DifficultyBadge } from "@/components/common/DifficultyBadge";
5-
import AddReviewButton from "@/components/courses/AddReviewButton";
6-
import { ChevronRight, Star, Award, BookOpen, Clock, Users, ThumbsUp, Check } from "lucide-react";
7-
import Link from "next/link";
8-
import PainOMeter from "@/components/courses/PainOMeter";
3+
import { useEffect, useState } from "react";
4+
import { supabase } from "@/lib/supabase";
95
import { useCourses } from "@/hooks/useCourses";
106
import CoursePageHeader from "@/components/courses/course_page/CoursePageHeader";
117
import CoursePageStats from "@/components/courses/course_page/CoursePageStats";
12-
import CoursePageProfessors from "@/components/courses/course_page/CoursePageProfessors";
13-
import GradeDistribution from "@/components/courses/GradeDistribution";
14-
import RateThisCourse from "@/components/courses/course_page/RateThisCourse";
158
import CoursePageReviews from "@/components/courses/course_page/CoursePageReviews";
16-
9+
import RateThisCourse from "@/components/courses/course_page/RateThisCourse";
1710

1811
export default function CoursePage({ params }: { params: { courseId: string } }) {
19-
const {courses, isLoading, error}= useCourses()
12+
const { courses, isLoading } = useCourses();
13+
const [averageRating, setAverageRating] = useState(0);
14+
const [reviewCount, setReviewCount] = useState(0);
15+
16+
const course = courses.find((course) => course.id === params.courseId);
17+
18+
/* ---------- Fetch Ratings to Compute Avg + Count ---------- */
19+
useEffect(() => {
20+
if (!course?.id) return;
21+
22+
const fetchRatings = async () => {
23+
const { data, error } = await supabase
24+
.from("ratings")
25+
.select("overall_rating")
26+
.eq("target_id", course.id)
27+
.eq("target_type", "course");
28+
29+
if (error) {
30+
console.error("Error fetching ratings:", error.message);
31+
return;
32+
}
33+
34+
if (data && data.length > 0) {
35+
const total = data.reduce((sum, r) => sum + (r.overall_rating || 0), 0);
36+
const avg = total / data.length;
37+
setAverageRating(parseFloat(avg.toFixed(1)));
38+
setReviewCount(data.length);
39+
} else {
40+
setAverageRating(0);
41+
setReviewCount(0);
42+
}
43+
};
44+
45+
fetchRatings();
46+
}, [course?.id]);
47+
2048
if (isLoading) {
2149
return <div>Loading...</div>;
2250
}
2351

24-
const course = courses.find(course => course.id === params.courseId);
25-
2652
if (!course) {
2753
notFound();
2854
}
29-
return (
30-
<div className="container px-4 md:px-6 py-4 max-h-screen mx-auto">
3155

56+
return (
57+
<div className="container px-4 md:px-6 py-4 min-h-[calc(100vh-6rem)] mx-auto">
3258
<div className="grid grid-cols-12 gap-4">
33-
59+
{/* Left Section */}
3460
<div className="col-span-12 lg:col-span-8 space-y-4">
35-
<CoursePageHeader course={course} />
61+
<CoursePageHeader
62+
course={course}
63+
averageRating={averageRating}
64+
reviewCount={reviewCount}
65+
/>
66+
3667
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
37-
<CoursePageStats reviewCount={course.review_count ?? 0} />
38-
{/* TODO */}
39-
{/* <CoursePageProfessors professors={course.professors} /> */}
68+
<CoursePageStats reviewCount={reviewCount} />
4069
</div>
41-
<GradeDistribution />
42-
<CoursePageReviews id={course.id} reviewCount={course.review_count ?? 0} />
70+
71+
<CoursePageReviews id={course.id} reviewCount={reviewCount} />
4372
</div>
44-
73+
74+
{/* Right Section */}
4575
<div className="col-span-12 lg:col-span-4 space-y-4">
46-
<RateThisCourse />
47-
<PainOMeter
48-
difficulty={8}
49-
workload={7}
50-
rating={3}
51-
/>
76+
<RateThisCourse courseId={course.id} />
5277
</div>
5378
</div>
5479
</div>
5580
);
56-
}
81+
}

0 commit comments

Comments
 (0)