Skip to content

Commit 7979fd2

Browse files
committed
Implemented getRecommendedQuestions server-action. Refactored question retrieval to handle home filters criteria.
1 parent 336de82 commit 7979fd2

File tree

4 files changed

+133
-13
lines changed

4 files changed

+133
-13
lines changed

app/(root)/(home)/page.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import Pagination from "@/components/shared/Pagination";
1010
import HomeFilters from "@/components/home/HomeFilters";
1111
import QuestionCard from "@/components/cards/QuestionCard";
1212

13-
import { getQuestions } from "@/lib/actions/question.action";
13+
import {
14+
getQuestions,
15+
getRecommendedQuestions,
16+
} from "@/lib/actions/question.action";
1417

1518
import { HomePageFilters } from "@/constants/filters";
1619

@@ -24,11 +27,28 @@ export const metadata: Metadata = {
2427
export default async function Home({ searchParams }: SearchParamsProps) {
2528
const { userId: clerkId } = auth();
2629

27-
const result = await getQuestions({
28-
searchQuery: searchParams.q,
29-
filter: searchParams.filter,
30-
page: searchParams.page ? +searchParams.page : 1,
31-
});
30+
let result;
31+
32+
if (searchParams?.filter === "recommended") {
33+
if (clerkId) {
34+
result = await getRecommendedQuestions({
35+
userId: clerkId,
36+
searchQuery: searchParams.q,
37+
page: searchParams.page ? +searchParams.page : 1,
38+
});
39+
} else {
40+
result = {
41+
questions: [],
42+
isNext: false,
43+
};
44+
}
45+
} else {
46+
result = await getQuestions({
47+
searchQuery: searchParams.q,
48+
filter: searchParams.filter,
49+
page: searchParams.page ? +searchParams.page : 1,
50+
});
51+
}
3252

3353
return (
3454
<>

components/home/HomeFilters.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,50 @@
1-
/* eslint-disable no-unused-vars */
21
"use client";
32

43
import { useState } from "react";
4+
import { useRouter, useSearchParams } from "next/navigation";
55

66
import { Button } from "@/components/ui/button";
77

8+
import { formUrlQuery } from "@/lib/utils";
9+
810
import { HomePageFilters } from "@/constants/filters";
911

1012
const HomeFilters = () => {
11-
const [active, setActive] = useState<string>("");
13+
const searchParams = useSearchParams();
14+
const router = useRouter();
15+
16+
const [active, setActive] = useState("");
17+
18+
const handleTypeClick = (item: string) => {
19+
if (active === item) {
20+
setActive("");
21+
22+
const newUrl = formUrlQuery({
23+
params: searchParams.toString(),
24+
key: "filter",
25+
value: null,
26+
});
27+
28+
router.push(newUrl, { scroll: false });
29+
} else {
30+
setActive(item);
31+
32+
const newUrl = formUrlQuery({
33+
params: searchParams.toString(),
34+
key: "filter",
35+
value: item.toLowerCase(),
36+
});
1237

13-
// todo: implement home filters functionality
38+
router.push(newUrl, { scroll: false });
39+
}
40+
};
1441

1542
return (
1643
<div className="mt-10 flex-wrap gap-3 md:flex">
1744
{HomePageFilters.map((filter) => (
1845
<Button
1946
key={filter.value}
20-
onClick={() => {}}
47+
onClick={() => handleTypeClick(filter.value)}
2148
className={`body-medium rounded-lg px-6 py-3 capitalize shadow-none ${
2249
active === filter.value
2350
? "bg-primary-100 text-primary-500 dark:bg-dark-400 dark:hover:bg-dark-400"

components/ui/skeleton.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { cn } from "@/lib/utils"
1+
import React from "react";
2+
import { cn } from "@/lib/utils";
23

34
function Skeleton({
45
className,
@@ -9,7 +10,7 @@ function Skeleton({
910
className={cn("animate-pulse rounded-md bg-muted", className)}
1011
{...props}
1112
/>
12-
)
13+
);
1314
}
1415

15-
export { Skeleton }
16+
export { Skeleton };

lib/actions/question.action.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type {
2020
GetQuestionByIdParams,
2121
GetQuestionsParams,
2222
QuestionVoteParams,
23+
RecommendedParams,
2324
} from "./shared.types";
2425

2526
export async function createQuestion(params: CreateQuestionParams) {
@@ -335,3 +336,74 @@ export async function getHotQuestions() {
335336
throw error;
336337
}
337338
}
339+
340+
export async function getRecommendedQuestions(params: RecommendedParams) {
341+
try {
342+
connectToDatabase();
343+
344+
const { userId, page = 1, pageSize = 20, searchQuery } = params;
345+
346+
// find user
347+
const user = await User.findOne({ clerkId: userId });
348+
349+
if (!user) {
350+
throw new Error("user not found");
351+
}
352+
353+
const skipAmount = (page - 1) * pageSize;
354+
355+
// Find the user's interactions
356+
const userInteractions = await Interaction.find({ user: user._id })
357+
.populate("tags")
358+
.exec();
359+
360+
// Extract tags from user's interactions
361+
const userTags = userInteractions.reduce((tags, interaction) => {
362+
if (interaction.tags) {
363+
tags = tags.concat(interaction.tags);
364+
}
365+
return tags;
366+
}, []);
367+
368+
// Get distinct tag IDs from user's interactions
369+
const distinctUserTagIds = [
370+
// @ts-ignore
371+
...new Set(userTags.map((tag: any) => tag._id)),
372+
];
373+
374+
const query: FilterQuery<typeof Question> = {
375+
$and: [
376+
{ tags: { $in: distinctUserTagIds } }, // Questions with user's tags
377+
{ author: { $ne: user._id } }, // Exclude user's own questions
378+
],
379+
};
380+
381+
if (searchQuery) {
382+
query.$or = [
383+
{ title: { $regex: searchQuery, $options: "i" } },
384+
{ content: { $regex: searchQuery, $options: "i" } },
385+
];
386+
}
387+
388+
const totalQuestions = await Question.countDocuments(query);
389+
390+
const recommendedQuestions = await Question.find(query)
391+
.populate({
392+
path: "tags",
393+
model: Tag,
394+
})
395+
.populate({
396+
path: "author",
397+
model: User,
398+
})
399+
.skip(skipAmount)
400+
.limit(pageSize);
401+
402+
const isNext = totalQuestions > skipAmount + recommendedQuestions.length;
403+
404+
return { questions: recommendedQuestions, isNext };
405+
} catch (error) {
406+
console.error("Error getting recommended questions:", error);
407+
throw error;
408+
}
409+
}

0 commit comments

Comments
 (0)