Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ yarn-error.log*
# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env.lcal
.env.local
.env*.local
.env.prod
# vercel
Expand Down
26 changes: 8 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 98 additions & 62 deletions src/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import React from "react";
import { type IPaper } from "@/interface";
import Image from "next/image";
import { Eye, Download, Check } from "lucide-react";
Expand All @@ -23,6 +24,8 @@ interface CardProps {
}

const Card = ({ paper, onSelect, isSelected }: CardProps) => {
const [previewOpen, setPreviewOpen] = React.useState(false);

const handleDownload = async (paper: IPaper) => {
await downloadFile(getSecureUrl(paper.file_url), generateFileName(paper));
};
Expand All @@ -34,77 +37,110 @@ const Card = ({ paper, onSelect, isSelected }: CardProps) => {
const paperLink = `/paper/${paper._id}`;

return (
<div
className={cn(
"overflow-hidden rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] font-play transition-all duration-150 hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-[#171720] hover:dark:bg-[#262635]",
isSelected && "bg-white",
)}
>
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pls add this back

<Image
src={paper.thumbnail_url}
alt={paper.subject}
width={320}
height={180}
className="w-full object-cover p-4 pb-3 md:h-[250px]"
/>

<div className="justify-center">
<div className="flex flex-row items-center justify-between px-4 pb-2">
<div className="text-md font-play font-medium">
{extractBracketContent(paper.subject)}
</div>
<div className="flex gap-2">
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
<Eye size={22} />
</Link>
<Download
size={20}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
void handleDownload(paper);
}}
className="cursor-pointer"
/>
<>
<div
className={cn(
"overflow-hidden rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] font-play transition-all duration-150 hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-[#171720] hover:dark:bg-[#262635]",
isSelected && "bg-white",
)}
>

<Image
src={paper.thumbnail_url}
alt={paper.subject}
width={320}
height={180}
className="w-full object-cover p-4 pb-3 md:h-[250px]"
/>

<div className="justify-center">
<div className="flex flex-row items-center justify-between px-4 pb-2">
<div className="text-md font-play font-medium">
{extractBracketContent(paper.subject)}
</div>
</div>
</div>

<div className="h-[1px] w-full bg-[#734DFF] dark:bg-[#36266D]" />
<div className="h-[1px] w-full bg-[#734DFF] dark:bg-[#36266D]" />

<div className="space-y-2 p-4">
<div className="font-play text-lg font-semibold">
{extractWithoutBracketContent(paper.subject)}
</div>
<div className="flex flex-wrap gap-2">
<Capsule>{paper.exam}</Capsule>
<Capsule>{paper.slot}</Capsule>
<Capsule>{paper.year}</Capsule>
<Capsule>{paper.semester}</Capsule>
<div className="space-y-2 p-4">
<div className="font-play text-lg font-semibold">
{extractWithoutBracketContent(paper.subject)}
</div>
<div className="flex flex-wrap gap-2">
<Capsule>{paper.exam}</Capsule>
<Capsule>{paper.slot}</Capsule>
<Capsule>{paper.year}</Capsule>
<Capsule>{paper.semester}</Capsule>
</div>
</div>
</div>
</div>
</Link>

<div className="flex items-center justify-between gap-2 px-4 pb-4 font-play">
<div className="flex items-center gap-2">
<input
checked={isSelected}
onChange={handleCheckboxChange}
className="h-5 w-5 accent-[#7480FF]"
type="checkbox"


<div className="flex justify-end gap-2 px-4 pb-2">
<Eye
size={22}
className="cursor-pointer"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setPreviewOpen(true);
}}
/>

<Download
size={20}
onClick={(e) => {
e.stopPropagation();
void handleDownload(paper);
}}
className="cursor-pointer"
/>
<p>Select</p>
</div>
{paper.answer_key_included && (
<div className="flex items-center gap-2 font-normal text-[#7480FF]">
<Check color="#7480FF" />
Answer Key

<div className="flex items-center justify-between gap-2 px-4 pb-4 font-play">
<div className="flex items-center gap-2">
<input
checked={isSelected}
onChange={handleCheckboxChange}
className="h-5 w-5 accent-[#7480FF]"
type="checkbox"
/>
<p>Select</p>
</div>
)}

{paper.answer_key_included && (
<div className="flex items-center gap-2 font-normal text-[#7480FF]">
<Check color="#7480FF" />
Answer Key
</div>
)}
</div>
</div>
</div>

{previewOpen && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
onClick={() => setPreviewOpen(false)}
>
<div
className="relative w-[95%] max-w-5xl h-[90vh] rounded-lg bg-white p-2 dark:bg-[#171720]"
onClick={(e) => e.stopPropagation()}
>
<button
className="absolute right-3 top-3 z-10 text-xl"
onClick={() => setPreviewOpen(false)}
>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

use react lucide icon

</button>
<iframe
src={`${getSecureUrl(paper.file_url)}#toolbar=0`}
className="w-full h-full rounded-md"
/>
</div>
</div>
)}
</>
);
};

export default Card;
export default Card;
2 changes: 1 addition & 1 deletion src/components/ShareButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function ShareButton() {
const paperPath = origin + pathname;
return (
<Dialog>
<DialogTrigger asChild>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why tho ?

<DialogTrigger>
<Button className="aspect-square h-10 w-10 p-0">
<FaShare />
</Button>
Expand Down
4 changes: 3 additions & 1 deletion src/components/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SidebarSection from "./SidebarSection";
import { useFilters } from "@/context/filterContext";

function SideBar() {
// Get everything from context - no more prop drilling!
const [openItem, setOpenItem] = React.useState<string | undefined>(undefined);
const {
selectedExams,
selectedSlots,
Expand Down Expand Up @@ -138,6 +138,8 @@ function SideBar() {
data={section.data}
selected={section.selected}
updater={section.updater}
openItem={openItem}
setOpenItem={setOpenItem}
/>
))}
</div>
Expand Down
79 changes: 48 additions & 31 deletions src/components/SidebarSection.tsx
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

idt the sidebar needs this change, this affects ux , pls check this once and revert it, if it's not needed.

Screen.Recording.2026-04-15.101415.mp4

Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,59 @@ interface SidebarSectionProps {
data: { label: string; value: string }[];
selected: string[];
updater: (newVal: string[]) => void;

openItem: string | undefined;
setOpenItem: (val: string | undefined) => void;
}

const SidebarSection: React.FC<SidebarSectionProps> = ({
label,
data,
selected,
updater,
}) => (
<div className="flex w-full flex-col items-baseline justify-between border-b-2 border-[#36266d] px-[10px]">
<Accordion className="w-full" type="single" value={label}>
<AccordionItem className="border-none no-underline" value={label}>
<AccordionTrigger className="w-full no-underline">
<div className="font-play text-sm no-underline">{label}</div>
</AccordionTrigger>
<AccordionContent>
<div className="my-2 flex w-full flex-wrap items-center">
{data.map((item) => (
<SidebarButton
key={item.value}
selected={selected.includes(item.value)}
onClick={() => {
const newValues = selected.includes(item.value)
? selected.filter((v) => v !== item.value)
: [...selected, item.value];
updater(newValues);
}}
className="mb-2 mr-2"
>
{item.label}
</SidebarButton>
))}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
);
openItem,
setOpenItem,
}) => {
return (
<div className="flex w-full flex-col items-baseline justify-between border-b-2 border-[#36266d] px-[10px]">
<Accordion
className="w-full"
type="single"
collapsible
value={openItem}
onValueChange={setOpenItem}
>
<AccordionItem
className="border-none"
value={label} // ✅ IMPORTANT: stable value
>
<AccordionTrigger className="w-full">
<div className="font-play text-sm">{label}</div>
</AccordionTrigger>

<AccordionContent>
<div className="my-2 flex w-full flex-wrap items-center">
{data.map((item) => (
<SidebarButton
key={item.value}
selected={selected.includes(item.value)}
onClick={() => {
const newValues = selected.includes(item.value)
? selected.filter((v) => v !== item.value)
: [...selected, item.value];
updater(newValues);
}}
className="mb-2 mr-2"
>
{item.label}
</SidebarButton>
))}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
);
};

export default SidebarSection;
export default SidebarSection;
Loading