Skip to content

Commit ce1bd4a

Browse files
committed
fix: code cleanup, sitemap changes, explore page changes
1 parent 6c44bcf commit ce1bd4a

File tree

13 files changed

+116
-92
lines changed

13 files changed

+116
-92
lines changed

src/actions/ai/roadmap/generate.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { fetchRoadmapQuestions } from '@/utils/data/roadmap/questions/fetch-road
77
import { generateRoadmapResponse } from './utils/generate-roadmap';
88
import { revalidateTag } from 'next/cache';
99
import { QuestionDifficulty } from '@/types/Questions';
10+
import { getUser } from '@/actions/user/authed/get-user';
1011

1112
interface RoadmapQuestion {
1213
uid: string;
@@ -29,21 +30,29 @@ interface RoadmapQuestion {
2930

3031
export const roadmapGenerate = async (opts: {
3132
roadmapUid: string;
32-
// passed in the pass to generate data for ai + fetch questions
33-
userUid: string;
3433
generateMore?: boolean;
3534
}) => {
3635
const { roadmapUid } = opts;
3736
opts.generateMore = opts.generateMore ?? false;
3837

38+
// get the user
39+
const user = await getUser();
40+
if (!user || user?.userLevel === 'FREE') {
41+
throw new Error('User not found');
42+
}
43+
3944
// Retrieve and format the necessary data for AI
40-
const formattedData = await generateDataForAi(opts);
45+
const formattedData = await generateDataForAi({
46+
roadmapUid,
47+
userUid: user?.uid,
48+
generateMore: opts.generateMore,
49+
});
4150

4251
let existingQuestions = [];
4352
if (formattedData === 'generated' || formattedData === 'invalid') {
4453
existingQuestions = await fetchRoadmapQuestions({
4554
roadmapUid,
46-
userUid: opts.userUid,
55+
userUid: user?.uid,
4756
});
4857

4958
if (existingQuestions.length === 0) {

src/app/(app)/(roadmap)/roadmap/[roadmapUid]/onboarding/generate/page.tsx

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { fetchDefaultUserAnswers } from '@/utils/data/roadmap/questions/default/fetch-default-user-answers';
22
import LoadingSpinner from '@/components/ui/loading';
3-
import { useUserServer } from '@/hooks/use-user-server';
43
import { Check, Route, X } from 'lucide-react';
5-
import { redirect } from 'next/navigation';
64
import { Suspense } from 'react';
75
import RoadmapGenerateButton from '@/components/app/roadmaps/onboarding/onboarding-generate';
86

@@ -13,11 +11,6 @@ export default async function RoadmapGeneratingPage({
1311
}) {
1412
const { roadmapUid } = params;
1513

16-
const user = await useUserServer();
17-
if (!user || !user.uid) {
18-
return redirect('/login');
19-
}
20-
2114
// Fetch user answers
2215
const userAnswers = await fetchDefaultUserAnswers({
2316
roadmapUid,
@@ -27,39 +20,40 @@ export default async function RoadmapGeneratingPage({
2720
userAnswers.sort((a, b) => a.question.order - b.question.order);
2821

2922
return (
30-
<div className="h-screen flex items-center justify-center">
23+
<div className="min-h-screen flex items-center justify-center p-4 bg-gradient-to-br from-black-100 to-black">
3124
<div
32-
className="w-full max-w-4xl p-8 border border-black-50 shadow-lg rounded-md relative"
25+
className="w-full max-w-5xl p-8 border border-black-50 shadow-2xl rounded-xl relative overflow-hidden"
3326
style={{
3427
background:
35-
'radial-gradient(128% 107% at 0% 0%,#212121 0%,rgb(0,0,0) 77.61472409909909%)',
28+
'radial-gradient(128% 107% at 0% 0%, #212121 0%, rgb(0,0,0) 77.61472409909909%)',
3629
}}
3730
>
38-
<div className="flex flex-col lg:flex-row gap-8 lg:gap-16">
31+
<div className="absolute inset-0 bg-gradient-to-r from-accent/10 to-transparent opacity-20"></div>
32+
<div className="flex flex-col lg:flex-row gap-12 lg:gap-16 relative z-10">
3933
{/* User Answers Section */}
40-
<div className="flex-1 z-50">
41-
<h1 className="text-xl font-semibold mb-4 text-white">
34+
<div className="flex-1">
35+
<h2 className="text-2xl font-bold mb-6 text-white border-b border-black-50 pb-2">
4236
Your Answers
43-
</h1>
44-
<ul className="space-y-3">
37+
</h2>
38+
<ul className="space-y-4 max-h-[60vh] overflow-y-auto pr-4 custom-scrollbar">
4539
{userAnswers?.map((answer) => (
4640
<li
4741
key={answer.questionUid}
48-
className="flex items-center gap-4 p-2 bg-black-100 border border-black-50 rounded-md shadow-sm font-ubuntu"
42+
className="flex items-center gap-4 p-4 bg-black-100/50 border border-black-50 rounded-lg shadow-md transition-all duration-300 hover:shadow-lg hover:border-accent/50"
4943
>
50-
<span className="text-sm font-medium text-white">
44+
<span className="text-lg font-medium text-accent">
5145
{answer.question.order}.
5246
</span>
53-
<span className={`text-sm font-semibold`}>
47+
<span className={`text-sm font-semibold flex-grow`}>
5448
{answer.correct ? (
55-
<div className="flex items-center">
49+
<div className="flex items-center text-green-400">
5650
Correct
57-
<Check className="size-4 ml-2 text-green-500" />
51+
<Check className="size-5 ml-2" />
5852
</div>
5953
) : (
60-
<div className="flex items-center">
54+
<div className="flex items-center text-destructive">
6155
Incorrect
62-
<X className="size-4 ml-2 text-destructive" />
56+
<X className="size-5 ml-2" />
6357
</div>
6458
)}
6559
</span>
@@ -69,23 +63,22 @@ export default async function RoadmapGeneratingPage({
6963
</div>
7064

7165
{/* Roadmap Generating Section */}
72-
<div className="flex-1 flex flex-col items-center justify-center space-y-4 order-first lg:order-last">
73-
<div className="space flex flex-col gap-y-2 items-center">
74-
<Route />
75-
<h1 className="text-2xl font-semibold text-white">
66+
<div className="flex-1 flex flex-col items-center justify-center space-y-8 lg:border-l lg:border-black-50 lg:pl-12">
67+
<div className="text-center">
68+
<Route className="size-16 text-accent mb-4 mx-auto" />
69+
<h1 className="text-3xl font-bold text-white mb-2">
7670
Generating Your Roadmap
7771
</h1>
72+
<p className="text-sm text-gray-400 max-w-md">
73+
This process may take a few minutes. Please hold tight while we
74+
prepare your personalised learning journey.
75+
</p>
76+
</div>
77+
<div className="w-full max-w-xs">
78+
<Suspense fallback={<LoadingSpinner />}>
79+
<RoadmapGenerateButton roadmapUid={roadmapUid} />
80+
</Suspense>
7881
</div>
79-
<p className="text-xs text-gray-400 text-center">
80-
This process may take a few minutes. <br /> Please hold tight
81-
while we prepare your personalised roadmap.
82-
</p>
83-
<Suspense fallback={<LoadingSpinner />}>
84-
<RoadmapGenerateButton
85-
roadmapUid={roadmapUid}
86-
userUid={user.uid}
87-
/>
88-
</Suspense>
8982
</div>
9083
</div>
9184
</div>

src/app/(marketing)/page.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export const metadata: Metadata = {
3535
description,
3636
keywords: [
3737
'learn to code for free',
38-
'coding platform',
3938
'beginner-friendly coding lessons',
4039
'interactive coding challenges',
4140
'daily programming practice',

src/app/sitemap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
2424
}));
2525

2626
const questionsPosts = questions.questions.map((question) => ({
27-
url: `${baseUrl}/questions/${question.slug}`,
27+
url: `${baseUrl}/question/${question.slug}`,
2828
lastModified: new Date(question.createdAt),
2929
}));
3030

src/components/app/leaderboard/user-rank.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,16 @@ import { shortenText } from '@/utils';
1212
export default async function UserRank(opts: { questionUid: string }) {
1313
const { questionUid } = opts;
1414

15-
const userData = await useUserServer();
15+
// run this in parallel as they do not depend on each other
16+
const [userData, userRank, userAnswer] = await Promise.all([
17+
useUserServer(),
18+
getUserAnswerRank({ questionUid }),
19+
getUserAnswer({ questionUid }),
20+
]);
1621
if (!userData) return null;
1722

1823
const displayName = getUserDisplayName(userData);
1924

20-
// run this in parallel as they do not depend on each other
21-
const [userRank, userAnswer] = await Promise.all([
22-
getUserAnswerRank({
23-
questionUid,
24-
userUid: userData.uid,
25-
}),
26-
getUserAnswer({
27-
questionUid,
28-
userUid: userData.uid,
29-
}),
30-
]);
31-
3225
if (!userAnswer) {
3326
return (
3427
<div className="flex justify-between w-full items-center">

src/components/app/questions/layout/carousel/question-carousel-card.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,36 @@ import { QuestionWithTags } from '@/types/Questions';
22
import Link from 'next/link';
33
import Chip from '@/components/ui/chip';
44
import { capitalise, getQuestionDifficultyColor } from '@/utils';
5+
import { getUserAnswer } from '@/utils/data/answers/get-user-answer';
6+
import { CheckCircle, Circle } from 'lucide-react';
57

6-
export default function QuestionCarouselCard(opts: {
8+
export default async function QuestionCarouselCard(opts: {
79
questionData: QuestionWithTags;
810
}) {
911
const { questionData } = opts;
1012

13+
const userAnswered = await getUserAnswer({ questionUid: questionData.uid });
14+
1115
return (
1216
<Link
1317
href={`/question/${questionData?.slug}`}
14-
className="h-full bg-black-75"
18+
className="h-full bg-black-75 group"
1519
>
1620
<div className="flex flex-col justify-between space-y-5 items-start border border-black-50 hover:border-accent duration-300 p-6 rounded-lg group w-full h-full relative overflow-hidden">
1721
<h6 className="text-wrap text-start line-clamp-2">
1822
{questionData?.question}
1923
</h6>
20-
<div className="w-full flex justify-between items-end">
24+
<div className="flex w-full justify-between items-center">
25+
<div className="flex items-center gap-x-2">
26+
{userAnswered ? (
27+
<CheckCircle className="flex-shrink-0 size-5 text-green-500" />
28+
) : (
29+
<Circle className="flex-shrink-0 size-5 text-black-50" />
30+
)}
31+
<p className="text-sm font-medium">
32+
{userAnswered ? 'Answered' : 'Not Answered'}
33+
</p>
34+
</div>
2135
<Chip
2236
text={capitalise(questionData?.difficulty)}
2337
color={getQuestionDifficultyColor(questionData?.difficulty).bg}

src/components/app/questions/layout/carousel/question-carousel.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export default function QuestionCarousel(opts: {
6868
</div>
6969
</div>
7070
<div className="relative w-full">
71-
<div className="hidden md:block absolute left-0 top-0 h-full w-12 bg-gradient-to-r from-[#000] to-transparent z-10" />
7271
<div className="hidden md:block absolute right-0 top-0 h-full w-12 bg-gradient-to-l from-[#000000] to-transparent z-10" />
7372
<CarouselContent className="grid grid-flow-col auto-cols-[calc(100%-8px)] md:auto-cols-[calc(50%-8px)] lg:auto-cols-[calc(33.33%-8px)] gap-4">
7473
{questions.map((q) => (
@@ -78,11 +77,11 @@ export default function QuestionCarousel(opts: {
7877
))}
7978
</CarouselContent>
8079
<CarouselPrevious
81-
className="hidden md:block border-none text-white -top-12 left-0 z-10"
80+
className="hidden md:block border-none text-white -top-14 -left-6 z-10"
8281
variant="ghost"
8382
/>
8483
<CarouselNext
85-
className="hidden md:block border-none text-white -top-12"
84+
className="hidden md:block border-none text-white -top-14"
8685
variant="ghost"
8786
/>
8887
</div>

src/components/app/questions/layout/question-card.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import TagDisplay from '@/components/app/questions/previous/tag-display';
44
import { getQuestionStats } from '@/utils/data/questions/get-question-stats';
55
import Link from 'next/link';
66
import Chip from '@/components/ui/chip';
7+
import { getUserAnswer } from '@/utils/data/answers/get-user-answer';
8+
import { CheckCircle } from 'lucide-react';
79

810
export default async function QuestionCard(opts: {
911
questionData: QuestionWithoutAnswers;
@@ -24,6 +26,11 @@ export default async function QuestionCard(opts: {
2426
? await getQuestionStats(identifier, questionData[identifier] || '')
2527
: null;
2628

29+
// has the user answered this question?
30+
const userAnswered = Boolean(
31+
await getUserAnswer({ questionUid: questionData.uid })
32+
);
33+
2734
// if identifier is uid, this is a custom question
2835
const href =
2936
identifier === 'uid'
@@ -34,19 +41,24 @@ export default async function QuestionCard(opts: {
3441
<Link
3542
href={href}
3643
key={questionData.uid}
37-
className="flex flex-col space-y-5 items-start border border-black-50 hover:border-accent duration-300 p-5 rounded-lg group w-full relative overflow-hidden"
44+
className="flex flex-col space-y-5 items-start bg-black-75 border border-black-50 hover:border-accent duration-300 p-5 rounded-lg group w-full relative overflow-hidden"
3845
>
3946
<div className="flex flex-col gap-y-2 w-full">
40-
<div className="flex w-full justify-between">
41-
<h6 className="text-base text-wrap text-start line-clamp-3">
47+
<div className="flex w-full justify-between items-center">
48+
<h6 className="text-base text-wrap text-start line-clamp-2 flex-grow">
4249
{questionData?.question}
4350
</h6>
51+
{userAnswered && (
52+
<div className="flex-shrink-0 ml-2">
53+
<CheckCircle className="size-5 text-green-500" />
54+
</div>
55+
)}
4456
</div>
4557
{showSubmissions && (
4658
<div className="text-start text-[10px]">
4759
<p className="font-ubuntu text-sm">
4860
Submissions:{' '}
49-
<span className="font-medium underline">
61+
<span className="font-medium">
5062
{questionStats?.totalSubmissions}
5163
</span>
5264
</p>
@@ -55,19 +67,17 @@ export default async function QuestionCard(opts: {
5567
</div>
5668
<div className="mt-5 w-full flex justify-between items-end z-10 relative">
5769
<div className="flex gap-4 items-end">
58-
{questionData?.tags?.length
59-
? questionData?.tags?.length > 0 && (
60-
<div className="space-y-0.5 text-start">
61-
<div className="flex items-center gap-1">
62-
<TagDisplay
63-
tags={questionData?.tags || []}
64-
numberOfTags={numberOfTags}
65-
showcaseTag={showcaseTag}
66-
/>
67-
</div>
68-
</div>
69-
)
70-
: ''}
70+
{questionData?.tags?.length && questionData?.tags?.length > 0 && (
71+
<div className="space-y-0.5 text-start">
72+
<div className="flex items-center gap-1">
73+
<TagDisplay
74+
tags={questionData?.tags || []}
75+
numberOfTags={numberOfTags}
76+
showcaseTag={showcaseTag}
77+
/>
78+
</div>
79+
</div>
80+
)}
7181
</div>
7282
<div className="flex items-center gap-x-3">
7383
{questionData?.difficulty && (
@@ -83,7 +93,6 @@ export default async function QuestionCard(opts: {
8393
small
8494
/>
8595
)}
86-
{/** question date */}
8796
{questionData?.questionDate && questionData?.dailyQuestion && (
8897
<Chip
8998
color="bg-black-100"

src/components/app/roadmaps/[uid]/generate-more-questions.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const GenerateMoreQuestionsButton = forwardRef(
1818
try {
1919
await roadmapGenerate({
2020
roadmapUid: roadmap.uid,
21-
userUid: roadmap.userUid,
2221
generateMore: true,
2322
});
2423
} catch (error) {

src/components/app/roadmaps/onboarding/onboarding-generate.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ import Link from 'next/link';
33

44
export default async function RoadmapGenerateButton({
55
roadmapUid,
6-
userUid
76
}: {
87
roadmapUid: string;
9-
userUid: string;
108
}) {
119
const generate = await roadmapGenerate({
1210
roadmapUid,
13-
userUid
1411
});
1512

1613
if (generate.length || generate === 'generated') {

0 commit comments

Comments
 (0)