Skip to content

Commit 92357a7

Browse files
authored
Merge pull request #38 from RoBorregos/debug
Debug problem consistency and UI fixes
2 parents 3f96fa3 + 2f93fbd commit 92357a7

File tree

15 files changed

+275
-86
lines changed

15 files changed

+275
-86
lines changed

prisma/schema.prisma

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ model User {
4040
email String? @unique
4141
emailVerified DateTime?
4242
image String?
43-
role String @default("MEMBER")
4443
leetcodeUser String?
44+
role String @default("MEMBER")
4545
accounts Account[]
4646
additionalContents AdditionalContent[] @relation("UserAdditionalContents")
4747
sessions Session[]
@@ -57,14 +57,23 @@ model VerificationToken {
5757
}
5858

5959
model Week {
60-
id String @id @default(cuid())
61-
number Int
62-
title String
63-
description String
64-
isBlocked Boolean
65-
color String
66-
resources String[]
67-
problems Problem[]
60+
id String @id @default(cuid())
61+
number Int
62+
title String
63+
description String
64+
isBlocked Boolean
65+
color String
66+
resources String[]
67+
detailResources Resource[]
68+
problems Problem[]
69+
}
70+
71+
model Resource {
72+
id String @id @default(cuid())
73+
title String
74+
url String
75+
weekId String
76+
week Week @relation(fields: [weekId], references: [id], onDelete: Cascade)
6877
}
6978

7079
model Problem {
@@ -73,9 +82,9 @@ model Problem {
7382
level Difficulty
7483
leetcodeUrl String
7584
weekId String
85+
recommended Boolean @default(false)
7686
week Week @relation(fields: [weekId], references: [id], onDelete: Cascade)
7787
solvedBy User[] @relation("SolvedProblems")
78-
7988
}
8089

8190
model AdditionalContent {
@@ -88,7 +97,8 @@ model AdditionalContent {
8897
}
8998

9099
enum Difficulty {
91-
EASY
100+
WARMUP
92101
MEDIUM
93-
HARD
102+
HARDER
103+
INSANE
94104
}

src/app/(pages)/admin/page.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const Admin = () => {
1313

1414
const [weeks, setWeeks] = useState<Week[]>([]);
1515
const [problems, setProblems] = useState<ProblemWithSolvedBy[]>([]);
16-
1716
const fetchWeeks = api.week.getWeeks.useQuery();
1817

1918
const fetchProblems = api.problem.getAll.useQuery();

src/app/(pages)/leaderboard/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const Leaderboard = () => {
9090
});
9191

9292
return (
93-
<div className="mx-auto mt-12 max-w-3xl">
93+
<div className="mx-auto mt-12 max-w-3xl pb-20">
9494
<h1 className="mb-8 text-center text-3xl font-extrabold tracking-tight text-white drop-shadow">
9595
Leaderboard
9696
</h1>

src/app/(pages)/weekly-problems/page.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,31 @@ import Title from "~/app/_components/title";
44
import { backgroundColors, textColors } from "utils/colors";
55
import { api } from "~/trpc/server";
66

7-
const WeeklyProblems = async() => {
7+
const WeeklyProblems = async () => {
88
const weeks = await api.week.getWeeksPublic();
99

1010
return (
1111
<div>
1212
<Title label="Weekly Problems" />
1313
<Subtitle label="Weeks" className="pb-4" />
1414

15-
<div className="grid grid-cols-3 text-main px-10 gap-10">
15+
<div className="text-main grid grid-cols-3 gap-10 px-10">
1616
{weeks.map((week, key) => (
1717
<div key={key}>
18-
<ProblemCard key={key} title={week.title} description={week.description} id={week.id} isBlocked={week.isBlocked} bgColor={backgroundColors.get(week.color) ?? "bg-white"} textColor={textColors.get(week.color) ?? "text-black"} />
18+
<ProblemCard
19+
key={key}
20+
title={`Week ${week.number}`}
21+
description={week.title}
22+
id={week.id}
23+
isBlocked={week.isBlocked}
24+
bgColor={backgroundColors.get(week.color) ?? "bg-white"}
25+
textColor={textColors.get(week.color) ?? "text-black"}
26+
/>
1927
</div>
2028
))}
2129
</div>
2230
</div>
23-
)
24-
}
31+
);
32+
};
2533

2634
export default WeeklyProblems;

src/app/_components/nav/navbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const Navbar = async () => {
99
const session = await auth();
1010

1111
return (
12-
<div className="w-full flex flex-row justify-between bg-primary-light py-5 px-10 border-b border-neutral-700">
12+
<div className="z-50 fixed w-full flex flex-row justify-between bg-primary-light py-5 px-10 border-b border-neutral-700">
1313
<div className="flex flex-row items-center gap-12">
1414
<Link href="/">
1515
<Image src={logo} alt="Logo" className="h-[2rem] w-fit cursor-pointer object-contain" />

src/app/_components/subtitle.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {cn} from "utils/merge"
1+
import { cn } from "utils/merge"
22

3-
const Subtitle = ({label, className} : {label:string, className?:string}) => {
3+
const Subtitle = ({ label, className }: { label: string, className?: string }) => {
44
return (
55
<div className={cn("text-white text-xl font-main font-semibold", className)}>
66
{label}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
const Unauthorized = () => {
3+
return (
4+
<div className="flex flex-col items-center justify-center py-20 bg-primary-light rounded-md">
5+
<h1 className="text-4xl font-bold text-accent">Unauthorized</h1>
6+
<p className="mt-4 text-lg text-neutral-400">Please Login</p>
7+
</div>
8+
);
9+
}
10+
11+
export default Unauthorized;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"use client";
2+
3+
import { api } from "~/trpc/react";
4+
import { useState } from "react";
5+
6+
interface SolvedToggleProps {
7+
problemId: string;
8+
initialSolved: boolean;
9+
userId: string;
10+
}
11+
12+
const SolvedToggle = ({ problemId, initialSolved, userId }: SolvedToggleProps) => {
13+
const [isSolved, setIsSolved] = useState(initialSolved);
14+
15+
const toggleSolved = api.problem.toggleSolved.useMutation({
16+
onSuccess: () => {
17+
setIsSolved(!isSolved);
18+
},
19+
});
20+
21+
const handleToggle = () => {
22+
// This is an option in case leetcode user doesnt work
23+
// For now we'll pass
24+
25+
// toggleSolved.mutate({ problemId, userId });
26+
};
27+
28+
return (
29+
<button
30+
onClick={handleToggle}
31+
disabled={toggleSolved.status === 'pending'}
32+
className={`px-3 py-1 rounded-full text-sm font-medium transition-colors ${
33+
isSolved
34+
? 'bg-green-600 text-white hover:bg-green-700'
35+
: 'bg-gray-600 text-gray-200 hover:bg-gray-700'
36+
} ${toggleSolved.isPending ? 'opacity-50 cursor-not-allowed' : ''}`}
37+
>
38+
{toggleSolved.isPending ? 'Loading...' : isSolved ? 'Solved' : 'Unsolved'}
39+
</button>
40+
);
41+
};
42+
43+
export default SolvedToggle;

src/app/_components/week/weekCard.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ const WeekCard = ({ title, description, id, isBlocked, bgColor, textColor }: Car
2626
<div className="text-white font-extralight pb-5">
2727
{description}
2828
</div>
29-
<Link href={`week/${id}`} className={`${textColor} bg-white text-sm rounded-full px-3 py-1 w-fit`}>
30-
Check it out
31-
</Link>
29+
{!isBlocked && (
30+
<Link href={`week/${id}`} className={`${textColor} bg-white text-sm rounded-full px-3 py-1 w-fit`}>
31+
Check it out
32+
</Link>
33+
)}
3234
</div>
3335
)
3436
}

src/app/_components/week/weekInfo.tsx

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,57 @@ import { api } from "~/trpc/server";
22
import Title from "../title";
33
import Subtitle from "../subtitle";
44
import { auth } from "~/server/auth";
5+
import Unauthorized from "../unauthorized";
6+
import SolvedToggle from "./solvedToggle";
7+
import { IoIosStar } from "react-icons/io";
8+
import Link from "next/link";
59

6-
const WeekInfo = async ({id}: { id: string }) => {
7-
const week = await api.week.getWeekPublic({id: id});
8-
console.log(week);
10+
const WeekInfo = async ({ id }: { id: string }) => {
911
const session = await auth();
1012
const userId = session?.user?.id;
1113
const leetcodeUser = session?.user?.leetcodeUser;
1214

13-
// Only call backend if needed information exists.
15+
if (!session?.user) {
16+
return <Unauthorized />;
17+
}
18+
1419
if (userId && leetcodeUser) {
1520
await api.leetcode.checkNewCompletions({
1621
userId: userId,
1722
leetcodeUser: leetcodeUser,
1823
});
1924
}
20-
25+
const week = await api.week.getWeekPublic({ id: id });
2126
return (
2227
<div>
2328
{week ? (
2429
<div>
2530
<Title label={"Week " + week.number + " - " + week.title} />
2631

2732
<div className="flex flex-col gap-10">
28-
<div className="flex flex-row justify-between">
29-
<div>
33+
<div className="flex flex-row justify-between gap-10">
34+
<div className="">
3035
<Subtitle label="Overview" />
3136
<div className="font-main text-primary-foreground">
3237
{week.description}
3338
</div>
39+
<ul className="text-white list-disc pl-4">
40+
{week.resources.map((resource, index) => (
41+
<li key={index} className="text-primary-foreground ">
42+
{resource}
43+
</li>
44+
))}
45+
</ul>
3446
</div>
3547

36-
<div className="w-1/3 rounded-xl bg-primary-light p-4">
48+
<div className="rounded-xl bg-primary-light p-4 w-max">
3749
<Subtitle label="Resources" />
38-
<div className="font-main text-sm text-primary-foreground">
39-
{/* {week.resources.map((resource, index) => (
40-
<div key={index} className="mb-2">
41-
<a
42-
href={resource}
43-
target="_blank"
44-
rel="noopener noreferrer"
45-
className="hover:underline"
46-
>
47-
{resource}
48-
</a>
49-
</div>
50-
))} */}
50+
<div className="font-main text-sm text-primary-foreground flex flex-col pr-5">
51+
{week.detailResources.map((resource, index) => (
52+
<Link href={resource.url} key={index} target="_blank" rel="noopener noreferrer" className="underline hover:text-gray-100 text-nowrap">
53+
{resource.title}
54+
</Link>
55+
))}
5156
</div>
5257
</div>
5358
</div>
@@ -64,12 +69,6 @@ const WeekInfo = async ({id}: { id: string }) => {
6469
</tr>
6570
</thead>
6671
<tbody>
67-
{/* <tr className="text-white ">
68-
<td> <a href="https://leetcode.com/" target="_blank" className="hover:underline"> Hello </a> </td>
69-
<td> Hi </td>
70-
<td> Hi </td>
71-
<td> Hi </td>
72-
</tr> */}
7372
{week.problems.map((problem) => (
7473
<tr key={problem.id} className="text-white">
7574
<td>
@@ -79,15 +78,26 @@ const WeekInfo = async ({id}: { id: string }) => {
7978
rel="noopener noreferrer"
8079
className="hover:underline"
8180
>
82-
{problem.name}
81+
<div className="flex flex-row items-center gap-2">
82+
{problem.recommended && (
83+
<IoIosStar />
84+
)}
85+
{problem.name}
86+
</div>
8387
</a>
8488
</td>
8589
<td>{problem.level}</td>
8690
<td>{problem.solvedBy?.length ?? 0}</td>
8791
<td>
88-
{userId && problem.solvedBy?.some((u) => u.id === userId)
89-
? "Solved"
90-
: "Unsolved"}
92+
{userId ? (
93+
<SolvedToggle
94+
problemId={problem.id}
95+
initialSolved={problem.solvedBy?.some((u) => u.id === userId) ?? false}
96+
userId={userId}
97+
/>
98+
) : (
99+
<span className="text-gray-400">Login required</span>
100+
)}
91101
</td>
92102
</tr>
93103
))}

0 commit comments

Comments
 (0)