Skip to content

Commit 041cec9

Browse files
Merge pull request #281 from karannfr/prod
fixed: #264, navbar responsiveness, footer responsiveness
2 parents 71be45b + f4a56e4 commit 041cec9

10 files changed

Lines changed: 267 additions & 76 deletions

File tree

src/app/layout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type { Metadata } from "next";
77
import Navbar from "@/components/Navbar";
88
import Footer from "@/components/Footer";
99
import ChildrenWrapper from "@/components/ChildrenWrapper";
10-
1110
export const metadata: Metadata = {
1211
metadataBase: new URL("https://papers.codechefvit.com/"),
1312
title: "Papers by CodeChef-VIT | Explore VIT Previous Year Question Papers",

src/components/CatalogueContent.tsx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,24 @@ import Error from "./Error";
1717
import { Filter } from "lucide-react";
1818
import { Sheet, SheetContent, SheetTrigger } from "./ui/sheet";
1919
import { Pin } from "lucide-react";
20-
20+
import SearchBarChild from "./Searchbar/searchbar-child"
2121
import Link from "next/link";
22+
import { usePathname } from "next/navigation";
2223
import {
2324
getSecureUrl,
2425
generateFileName,
2526
downloadFile,
2627
} from "@/util/download_paper";
28+
import type { ICourses } from "@/interface";
2729

2830
const CatalogueContent = () => {
2931
const router = useRouter();
3032
const searchParams = useSearchParams();
3133
const [isMounted, setIsMounted] = useState(false);
32-
34+
const pathname: string = usePathname() ?? "/";
3335
// Initialize state with defaults, set later in useEffect
3436
const [subject, setSubject] = useState<string | null>(null);
37+
const [subjects, setSubjects] = useState<string[]>([]);
3538
const [selectedExams, setSelectedExams] = useState<string[]>([]);
3639
const [selectedSlots, setSelectedSlots] = useState<string[]>([]);
3740
const [selectedYears, setSelectedYears] = useState<string[]>([]);
@@ -75,6 +78,31 @@ const CatalogueContent = () => {
7578
void fetchRelatedSubjects();
7679
}, [subject]);
7780

81+
useEffect(() => {
82+
if (pathname !== "/catalogue") return;
83+
84+
const getSubjects = async () => {
85+
try {
86+
const res = await fetch("/api/course-list");
87+
if (!res.ok) return;
88+
89+
const json: unknown = await res.json();
90+
91+
if (Array.isArray(json)) {
92+
const filtered = json
93+
.filter((item): item is ICourses => typeof item === "object" && item !== null && "name" in item)
94+
.map(item => item.name);
95+
setSubjects(filtered);
96+
} else {
97+
console.error("Invalid data returned from API:", json);
98+
}
99+
} catch (err) {
100+
console.error("Failed to fetch courses", err);
101+
}
102+
};
103+
104+
void getSubjects();
105+
}, [pathname]);
78106
// Set initial state from searchParams on client-side mount
79107
useEffect(() => {
80108
setIsMounted(true);
@@ -352,7 +380,10 @@ const CatalogueContent = () => {
352380
</SheetContent>
353381
</Sheet>}
354382

355-
<div className="p-7">
383+
<div className="p-7 flex flex-col items-start">
384+
<div className="md:hidden flex flex-col items-start w-full mb-8">
385+
<SearchBarChild initialSubjects={subjects} />
386+
</div>
356387
<div className="flex items-center gap-2">
357388
<div>
358389
<p className="text-s font-semibold text-gray-700 dark:text-white/80">

src/components/Error.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const Error = ({
1313
<div
1414
className={`flex h-[80%] flex-1 items-center justify-center ${filtersPulled ? "blur-xl" : ""}`}
1515
>
16-
<div className="-mt-48 text-center text-lg">{message}</div>
16+
<div className="-mt-48 text-center md:text-lg text-sm">{message}</div>
1717
</div>
1818
);
1919
};

src/components/FloatingNavbar.tsx

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,93 @@
11
"use client";
22

3-
import { useState } from "react";
43
import { usePathname } from "next/navigation";
54
import Link from "next/link";
6-
import { ArrowUpRight, Pin, UploadIcon, ChevronDown } from "lucide-react";
5+
import { useState } from "react";
6+
import {
7+
ArrowUpRight,
8+
ChevronDown,
9+
Pin,
10+
UploadIcon,
11+
} from "lucide-react";
712
import ModeToggle from "./toggle-theme";
813

14+
import {
15+
DropdownMenu,
16+
DropdownMenuTrigger,
17+
DropdownMenuContent,
18+
DropdownMenuItem,
19+
} from "@/components/ui/dropdown-menu";
20+
921
interface Props {
1022
onNavigate: () => void;
1123
}
1224

1325
export default function FloatingNavbar({ onNavigate }: Props) {
1426
const pathname = usePathname();
15-
const [isOpen, setIsOpen] = useState(false);
27+
const [dropdownOpen, setDropdownOpen] = useState(false);
1628

1729
return (
18-
<div className="fixed right-6 top-0 z-50 flex flex-col items-end h-full pointer-events-none">
19-
{}
20-
<button
21-
className="mt-[1.25rem] flex h-10 w-10 items-center justify-center rounded-full bg-[#4B22D1] text-white shadow-lg transition-transform duration-200 hover:scale-105 active:scale-95 pointer-events-auto"
22-
onClick={() => setIsOpen(prev => !prev)}
23-
aria-label={isOpen ? "Close menu" : "Open menu"}
24-
>
25-
<ChevronDown
26-
className={`h-5 w-5 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`}
27-
/>
28-
</button>
29-
30-
{}
31-
{isOpen && (
32-
<div className="mt-2 flex flex-col items-center gap-4 rounded-3xl bg-[#110F18] px-6 py-6 shadow-xl ring-1 ring-white/5 pointer-events-auto">
33-
<Link
34-
href={pathname === "/upload" ? "/" : "/upload"}
35-
onClick={() => {
36-
setIsOpen(false);
37-
onNavigate();
38-
}}
30+
<div className="flex flex-col items-end h-full space-y-4 pointer-events-none">
31+
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
32+
<DropdownMenuTrigger asChild>
33+
<button
34+
className="flex h-10 w-10 items-center justify-center rounded-full bg-[#4B22D1] text-white shadow-lg transition-transform duration-200 hover:scale-105 active:scale-95 pointer-events-auto"
35+
aria-label="Toggle dropdown"
3936
>
40-
<div className="flex items-center gap-2 rounded-full border border-[#3A3745] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#1A1823]">
37+
<ChevronDown
38+
className={`h-5 w-5 transition-transform duration-200 ${dropdownOpen ? "rotate-180" : ""}`}
39+
/>
40+
</button>
41+
</DropdownMenuTrigger>
42+
43+
<DropdownMenuContent
44+
className="xl:hidden mt-2 w-72 space-y-1 rounded-3xl border border-white/10 bg-[#110F18] px-4 py-4 text-white shadow-xl backdrop-blur-sm pointer-events-auto"
45+
align="end"
46+
>
47+
<DropdownMenuItem asChild>
48+
<Link
49+
href={pathname === "/upload" ? "/" : "/upload"}
50+
onClick={() => onNavigate()}
51+
className="flex w-full items-center gap-3 rounded-lg px-3 py-3 hover:bg-[#1A1823] transition"
52+
>
4153
<UploadIcon className="h-4 w-4" />
42-
<span>{pathname === "/upload" ? "Search Papers" : "Upload Papers"}</span>
43-
</div>
44-
</Link>
54+
<span className="text-sm font-medium">
55+
{pathname === "/upload" ? "Search Papers" : "Upload Papers"}
56+
</span>
57+
</Link>
58+
</DropdownMenuItem>
4559

46-
<Link href="/pinned" onClick={() => { setIsOpen(false); onNavigate(); }}>
47-
<div className="flex items-center gap-2 rounded-full border border-[#3A3745] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#1A1823]">
60+
<DropdownMenuItem asChild>
61+
<Link
62+
href="/pinned"
63+
onClick={() => onNavigate()}
64+
className="flex w-full items-center gap-3 rounded-lg px-3 py-3 hover:bg-[#1A1823] transition"
65+
>
4866
<Pin className="h-4 w-4" />
49-
<span>Pinned Subjects</span>
50-
</div>
51-
</Link>
67+
<span className="text-sm font-medium">Pinned Subjects</span>
68+
</Link>
69+
</DropdownMenuItem>
5270

53-
<Link href="/request" onClick={() => { setIsOpen(false); onNavigate(); }}>
54-
<div className="flex items-center gap-2 rounded-full border border-[#3A3745] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#1A1823] w-full justify-center">
71+
<DropdownMenuItem asChild>
72+
<Link
73+
href="/request"
74+
onClick={() => onNavigate()}
75+
className="flex w-full items-center gap-3 rounded-lg px-3 py-3 hover:bg-[#1A1823] transition"
76+
>
5577
<ArrowUpRight className="h-4 w-4" />
56-
<span>Paper Request</span>
57-
</div>
58-
</Link>
78+
<span className="text-sm font-medium">Paper Request</span>
79+
</Link>
80+
</DropdownMenuItem>
5981

60-
<div className="rounded-full border border-[#3A3745] p-1">
61-
<ModeToggle />
82+
<div className="pt-2 border-t border-[#3A3745] mt-2">
83+
<div className="flex justify-center pt-2">
84+
<div className="rounded-full border border-[#3A3745] p-1">
85+
<ModeToggle />
86+
</div>
87+
</div>
6288
</div>
63-
</div>
64-
)}
89+
</DropdownMenuContent>
90+
</DropdownMenu>
6591
</div>
6692
);
6793
}

src/components/Footer.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ export default function Footer() {
5050
};
5151

5252
return (
53-
<footer className="w-full overflow-hidden bg-gradient-to-b from-[#F3F5FF] to-[#A599CE] px-6 py-10 sm:pt-28 md:pt-36 text-white dark:from-[#070114] dark:to-[#1F0234]">
54-
<div className="mx-auto flex max-w-7xl flex-wrap justify-between gap-y-10 text-center sm:text-left mb-16">
53+
<footer className="w-full overflow-hidden bg-gradient-to-b from-[#F3F5FF] to-[#A599CE] px-6 py-10 pt-16 md:pt-20 lg:pt-36 text-white dark:from-[#070114] dark:to-[#1F0234]">
54+
<div className="mx-auto flex max-w-7xl flex-col lg:flex-row justify-between gap-y-10 text-center lg:text-left mb-16">
5555
{/* Branding & Socials */}
56-
<div className="flex w-full flex-col gap-4 sm:w-[45%] lg:w-[30%]">
56+
<div className="flex w-full flex-col gap-4 lg:w-[30%]">
5757
<h1 className="bg-gradient-to-r from-[#562EE7] to-[rgba(116,128,255,0.8)] bg-clip-text font-jost text-5xl font-bold text-transparent dark:to-[#FFC6E8]">
5858
Papers
5959
</h1>
60-
<div className="flex flex-wrap justify-center gap-2 sm:justify-start">
60+
<div className="flex flex-wrap justify-center gap-2 lg:justify-start">
6161
{[
6262
[
6363
"https://www.instagram.com/codechefvit/",
@@ -94,23 +94,23 @@ export default function Footer() {
9494
</div>
9595

9696
{/* Events */}
97-
<div className="flex w-full flex-col gap-2 text-black dark:text-white sm:w-[45%] lg:w-[15%]">
97+
<div className="flex w-full flex-col gap-2 text-black dark:text-white lg:w-[15%]">
9898
<h3 className="font-jost text-xl font-semibold">Events</h3>
9999
<Link href="https://devsoc25.codechefvit.com" target="_blank">DevSoc</Link>
100100
<Link href="https://gravitas.codechefvit.com" target="_blank">CookOff</Link>
101101
<Link href="https://gravitas.codechefvit.com" target="_blank">Clueminati</Link>
102102
</div>
103103

104104
{/* Projects */}
105-
<div className="flex w-full flex-col gap-2 text-black dark:text-white sm:w-[45%] lg:w-[20%]">
105+
<div className="flex w-full flex-col gap-2 text-black dark:text-white lg:w-[20%]">
106106
<h3 className="font-jost text-xl font-semibold">Our Projects</h3>
107107
<Link href="https://papers.codechefvit.com" target="_blank">Papers</Link>
108108
<Link href="https://contactify.codechefvit.com" target="_blank">Contactify</Link>
109109
<Link href="https://ffcs.codechefvit.com" target="_blank">FFCS-inator</Link>
110110
</div>
111111

112112
{/* Suggestions */}
113-
<div className="flex w-full flex-col gap-1 text-black dark:text-white sm:w-[45%] lg:w-[25%] items-center sm:items-start">
113+
<div className="flex w-full flex-col gap-1 text-black dark:text-white lg:w-[25%] items-center lg:items-start">
114114
<Link
115115
href={`mailto:codechefvit@gmail.com`}
116116
className="flex items-center gap-2 font-jost text-xl font-semibold mb-2"

src/components/FreshersBanner.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"use client";
2+
3+
import { Info, X } from "lucide-react";
4+
import { useEffect, useState } from "react";
5+
6+
export default function Banner() {
7+
const [visible, setVisible] = useState(true);
8+
9+
useEffect(() => {
10+
const bannerStatus = localStorage.getItem("banner");
11+
if (bannerStatus === "freshers") {
12+
setVisible(false);
13+
}
14+
}, []);
15+
16+
const closeBanner = () => {
17+
localStorage.setItem("banner", "freshers");
18+
setVisible(false);
19+
};
20+
21+
if (!visible) return null;
22+
23+
return (
24+
<div className="z-[60] w-full bg-[#fef3c7] text-[#5a3000] shadow-sm">
25+
<div className="relative mx-auto flex max-w-screen-2xl flex-col items-start justify-between gap-3 px-4 py-3 sm:justify-center sm:flex-row sm:items-center sm:gap-4 md:px-8 md:py-3">
26+
<div className="flex items-center gap-2">
27+
<Info className="h-5 w-5 text-[#d97706]" />
28+
<span className="font-semibold text-base tracking-wide text-[#78350f]">
29+
Attention Freshers!
30+
</span>
31+
</div>
32+
33+
<p className="text-sm text-[#5a3000] sm:text-right flex-1">
34+
If papers for your subject are not yet available, click on your subject and explore related subjects until papers become available, as these are newly introduced courses.
35+
</p>
36+
37+
<button
38+
onClick={closeBanner}
39+
className="absolute right-4 top-4 text-[#78350f] hover:text-[#a84b0f] transition sm:static sm:self-start"
40+
aria-label="Dismiss banner"
41+
>
42+
<X className="h-5 w-5" />
43+
</button>
44+
</div>
45+
</div>
46+
);
47+
}

0 commit comments

Comments
 (0)