Skip to content

Paper -> Count Route #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
15 changes: 8 additions & 7 deletions src/app/api/papers/count/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ import Paper from "@/db/papers";

export const dynamic = "force-dynamic";

export async function GET() {
export async function GET(req: Request) {
try {
await connectToDatabase();

const count: number = await Paper.countDocuments();
const { searchParams } = new URL(req.url);
const subject = searchParams.get("subject");
Comment on lines +11 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

req.query.subject should work


return NextResponse.json(
{ count },
{ status: 200 }
);
const filter = subject ? { subject } : {};
const count = await Paper.countDocuments(filter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just maintain a counter in db
and increment those values on the fly


return NextResponse.json({ count }, { status: 200 });
} catch (error) {
return NextResponse.json(
{ message: "Failed to fetch papers", error },
{ status: 500 }
{ status: 500 },
);
}
}
2 changes: 1 addition & 1 deletion src/app/api/papers/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function GET(req: NextRequest) {
if (papers.length === 0) {
return NextResponse.json(
{ message: "No papers found for the specified subject" },
{ status: 404 },
{ status: 200 },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errors shouldn't be 200

);
}

Expand Down
9 changes: 7 additions & 2 deletions src/app/api/upcoming-papers/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ export async function GET() {
{ status: 404 },
);
}
const nextSlot = String.fromCharCode(slot.charCodeAt(0) + 1)
const correspondingSlots = [slot + "1", slot + "2", nextSlot + "1", nextSlot + "2"];
const nextSlot = String.fromCharCode(slot.charCodeAt(0) + 1);
const correspondingSlots = [
slot + "1",
slot + "2",
nextSlot + "1",
nextSlot + "2",
];
const selectedSubjects = await UpcomingSubject.find({
slots: { $in: correspondingSlots },
});
Expand Down
53 changes: 53 additions & 0 deletions src/app/api/user-papers/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NextResponse } from "next/server";
import { connectToDatabase } from "@/lib/mongoose";
import Paper from "@/db/papers";
import { StoredSubjects } from "@/interface";

export const dynamic = "force-dynamic";

export async function POST(req: Request) {
try {
await connectToDatabase();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will happen at startup u dont need to do this

const body = (await req.json()) as StoredSubjects;

const subjects = body;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant


const usersPapers = await Paper.find({
subject: { $in: subjects },
});

const transformedPapers = usersPapers.reduce(
(acc: { subject: string; slots: string[] }[], paper) => {
const existing = acc.find((item) => item.subject === paper.subject);

if (existing) {
existing.slots.push(paper.slot);
} else {
acc.push({ subject: paper.subject, slots: [paper.slot] });
}

return acc;
},
[],
);

const seenSubjects = new Set();
const uniquePapers = transformedPapers.filter((paper) => {
if (seenSubjects.has(paper.subject)) return false;
seenSubjects.add(paper.subject);
return true;
});

return NextResponse.json(uniquePapers, {
status: 200,
});
} catch (error) {
console.error("Error fetching papers:", error);
return NextResponse.json(
{
error: "Failed to fetch papers.",
},
{ status: 500 },
);
}
}
25 changes: 25 additions & 0 deletions src/app/pinned/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import SearchBar from "@/components/Searchbar/searchbar";
import PinnedPapersCarousel from "@/components/PinnedPapersCarousel";

const Pinned = () => {
return (
<div id="pinned" className="flex flex-col justify-between">
<h1 className="mx-auto my-8 text-center font-vipnabd text-3xl font-extrabold">
Pinned Papers
</h1>

<div className="flex items-center justify-center gap-4 px-6">
<div className="flex max-w-xl flex-1">
<SearchBar type="pinned" />
</div>
</div>
<PinnedPapersCarousel carouselType="users" />
<div className="mt-6 flex w-full items-center justify-center">
<p>You can pin upto 8 Subjects</p>
</div>
</div>
);
};

export default Pinned;
14 changes: 14 additions & 0 deletions src/components/AddPapers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const AddPapers = ({ onClick }: { onClick?: () => void }) => {
return (
<div
onClick={onClick}
className="flex h-full w-full cursor-pointer items-center justify-center rounded-md border-2 border-dashed border-purple-500 bg-transparent transition hover:border-gray-400 hover:bg-[#2a2a2a] hover:text-gray-300"
>
<span className="text-4xl text-purple-500 transition-colors duration-200 group-hover:text-gray-300">
+
</span>
</div>
);
};

export default AddPapers;
52 changes: 49 additions & 3 deletions src/components/CatalogueContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import SideBar from "../components/SideBar";
import Error from "./Error";
import { Filter } from "lucide-react";
import { Sheet, SheetContent, SheetTrigger } from "./ui/sheet";
import { StarIcon } from "lucide-react";
import { StoredSubjects } from "@/interface";

export async function downloadFile(url: string, filename: string) {
try {
Expand Down Expand Up @@ -48,25 +50,51 @@ const CatalogueContent = () => {
const [filterOptions, setFilterOptions] = useState<Filters>();
const [filtersPulled, setFiltersPulled] = useState<boolean>(false);
const [appliedFilters, setAppliedFilters] = useState<boolean>(false);
const [pinned, setPinned] = useState<boolean>(false);

// Set initial state from searchParams on client-side mount
useEffect(() => {
setIsMounted(true);
if (searchParams) {
setSubject(searchParams.get("subject"));
const currentPinnedSubjects = JSON.parse(
localStorage.getItem("userSubjects") ?? "[]",
) as StoredSubjects;
const subjectName = searchParams.get("subject");
setSubject(subjectName);
setSelectedExams(searchParams.get("exams")?.split(",") ?? []);
setSelectedSlots(searchParams.get("slots")?.split(",") ?? []);
setSelectedYears(searchParams.get("years")?.split(",") ?? []);
setSelectedCampuses(searchParams.get("campus")?.split(",") ?? []);
setSelectedSemesters(searchParams.get("semester")?.split(",") ?? []);
setSelectedAnswerKeyIncluded(searchParams.get("answerkey") === "true");
if (subjectName && Array.isArray(currentPinnedSubjects)) {
if (currentPinnedSubjects.includes(subjectName)) {
setPinned(true);
} else {
setPinned(false);
}
}
}
}, [searchParams]);
}, [searchParams, pinned]);

const filtersNotPulled = () => {
setFiltersPulled(false);
};

const handlePinToggle = () => {
const current = !pinned;
setPinned(current);

const saved = JSON.parse(
localStorage.getItem("userSubjects") ?? "[]",
) as string[];
const updated = current
? [...new Set([...saved, subject])]
: saved.filter((s) => s !== subject);

localStorage.setItem("userSubjects", JSON.stringify(updated));
};

// Fetch papers and apply filters
useEffect(() => {
if (!subject || !isMounted) return;
Expand Down Expand Up @@ -295,11 +323,29 @@ const CatalogueContent = () => {
</SheetContent>
</Sheet>

<div className="flex items-center gap-2 p-7">
<div>
<p className="text-s font-semibold text-white/80">
{subject?.split("[")[1]?.replace("]", "")}
</p>
<h2 className="text-2xl font-extrabold text-white md:text-3xl">
{subject?.split(" [")[0]}
</h2>
</div>
<div className="mt-7">
<button onClick={handlePinToggle}>
<StarIcon
className={`h-7 w-7 ${pinned ? "fill-[#A78BFA]" : ""} stroke-white`}
/>
</button>
</div>
</div>

{loading ? (
<Loader />
) : papers.length > 0 ? (
<div
className={`grid h-fit grid-cols-1 gap-8 px-[30px] py-[40px] md:grid-cols-2 lg:grid-cols-4 ${filtersPulled ? "blur-xl" : ""}`}
className={`grid h-fit grid-cols-1 gap-8 px-[30px] pb-[40px] md:grid-cols-2 lg:grid-cols-4 ${filtersPulled ? "blur-xl" : ""}`}
>
{appliedFilters ? (
filteredPapers.length > 0 ? (
Expand Down
9 changes: 8 additions & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ModeToggle from "@/components/toggle-theme";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { ArrowDownLeftIcon } from "lucide-react";
import { StarIcon } from "lucide-react";

function Navbar() {
const pathname = usePathname();
Expand All @@ -27,10 +28,16 @@ function Navbar() {
</a>
<Link
href="/"
className="font-jost bg-gradient-to-r from-[#562EE7] to-[rgba(116,128,255,0.8)] bg-clip-text text-left text-4xl font-bold tracking-wide text-transparent dark:from-[#562EE7] dark:to-[#FFC6E8] md:text-6xl"
className="bg-gradient-to-r from-[#562EE7] to-[rgba(116,128,255,0.8)] bg-clip-text text-left font-jost text-4xl font-bold tracking-wide text-transparent dark:from-[#562EE7] dark:to-[#FFC6E8] md:text-6xl"
>
Papers
</Link>
<Link href="/pinned">
<div className="ml-3 mt-4 flex items-center gap-1 rounded-full border border-white/20 bg-[#1A1921] px-4 py-[6px] text-xs font-semibold text-white shadow-sm transition hover:bg-[#2C2A36] dark:border-[#36266D] dark:bg-[#171720] dark:hover:bg-[#2A2635] sm:text-sm">
<StarIcon className="h-4 w-4" />
Pinned Subjects
</div>
</Link>
</div>
<div className="md:w/[20%] flex items-center justify-end gap-x-2">
<div className="scale-75 sm:scale-100">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import Autoplay from "embla-carousel-autoplay";
import { chunkArray } from "@/util/utils";

function StoredPapers() {
function PapersCarousel() {
const [displayPapers, setDisplayPapers] = useState<IUpcomingPaper[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [chunkSize, setChunkSize] = useState<number>(4);
Expand Down Expand Up @@ -107,4 +107,4 @@ function StoredPapers() {
);
}

export default StoredPapers;
export default PapersCarousel;
27 changes: 27 additions & 0 deletions src/components/PinButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { Star, StarOff } from "lucide-react";

export default function PinButton({
isPinned,
onToggle,
}: {
isPinned: boolean;
onToggle?: () => void;
}) {
return (
<button
onClick={onToggle}
className={`ml-2 flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium ${
isPinned ? "bg-purple-700 text-white" : "bg-[#2B2B30] text-white/80"
} transition hover:bg-purple-600`}
>
{isPinned ? (
<Star className="h-4 w-4" />
) : (
<StarOff className="h-4 w-4" />
)}
<span className="hidden sm:inline">{isPinned ? "Pinned" : "Pin"}</span>
</button>
);
}
Loading