From e4c0a63ae67d12ce5b3ae77d2bfd28ffd50f06da Mon Sep 17 00:00:00 2001 From: Atharva Sharma Date: Mon, 20 Apr 2026 02:51:47 +0530 Subject: [PATCH 1/2] fix carousel grid --- src/components/PinnedPapersCarousel.tsx | 267 ------------------------ 1 file changed, 267 deletions(-) diff --git a/src/components/PinnedPapersCarousel.tsx b/src/components/PinnedPapersCarousel.tsx index 3ee010a..e69de29 100644 --- a/src/components/PinnedPapersCarousel.tsx +++ b/src/components/PinnedPapersCarousel.tsx @@ -1,267 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import axios from "axios"; -import { type IUpcomingPaper } from "@/interface"; -import UpcomingPaper from "./UpcomingPaper"; -import { - Carousel, - CarouselContent, - CarouselItem, - CarouselNext, - CarouselPrevious, -} from "@/components/ui/carousel"; -import Autoplay from "embla-carousel-autoplay"; -import { chunkArray } from "@/lib/utils/array"; -import { type StoredSubjects } from "@/interface"; -import SkeletonPaperCard from "./SkeletonPaperCard"; -import PinnedModal from "./ui/PinnedModal"; - -function PinnedPapersCarousel() { - const [isLoading, setIsLoading] = useState(true); - const [chunkSize, setChunkSize] = useState(4); - const [displayPapers, setDisplayPapers] = useState([]); - useEffect(() => { - const handleResize = () => { - if (window.innerWidth <= 540) { - setChunkSize(2); - } else if (window.innerWidth <= 920) { - setChunkSize(4); - } else { - setChunkSize(8); - } - }; - - handleResize(); // initialize - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, []); - - const chunkedPapers = chunkArray(displayPapers, chunkSize); - - if (chunkedPapers.length > 0) { - const lastChunkIndex = chunkedPapers.length - 1; - if ((chunkedPapers[lastChunkIndex]?.length ?? 0) < chunkSize) { - chunkedPapers[lastChunkIndex] = [ - ...(chunkedPapers[lastChunkIndex] ?? []), - { subject: "add_subject_button", slots: [] } as IUpcomingPaper, - ]; - } else { - chunkedPapers.push([ - { subject: "add_subject_button", slots: [] } as IUpcomingPaper, - ]); - } - } - - const fetchPapers = async () => { - try { - setIsLoading(true); - - const storedSubjects = JSON.parse( - localStorage.getItem("userSubjects") ?? "[]", - ) as StoredSubjects; - - const response = await axios.post<{ subject: string; slots: string[] }[]>( - "/api/user-papers", - storedSubjects, - ); - - const fetchedPapers = response.data; - - const fetchedSubjectsSet = new Set( - fetchedPapers.map((paper) => paper.subject), - ); - - const storedSubjectsArray = Array.isArray(storedSubjects) - ? storedSubjects - : []; - const missingSubjects = storedSubjectsArray - .filter((subject: string) => !fetchedSubjectsSet.has(subject)) - .map((subject: string) => ({ - subject, - slots: [], - })) as { subject: string; slots: string[] }[]; - - const allDisplayPapers = [...fetchedPapers, ...missingSubjects]; - - allDisplayPapers.sort((a, b) => { - const aIndex = storedSubjects.indexOf(a.subject); - const bIndex = storedSubjects.indexOf(b.subject); - - return ( - (aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) - - (bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex) - ); - }); - - setDisplayPapers(allDisplayPapers); - } catch (error) { - console.error("Failed to fetch papers:", error); - } finally { - setIsLoading(false); - } - }; - - useEffect(() => { - void fetchPapers(); - }, []); - - useEffect(() => { - const handleSubjectsChange = () => { - void (async () => { - try { - const storedSubjects = JSON.parse( - localStorage.getItem("userSubjects") ?? "[]", - ) as StoredSubjects; - - const response = await axios.post< - { subject: string; slots: string[] }[] - >("/api/user-papers", storedSubjects); - - const fetchedPapers = response.data; - - const fetchedSubjectsSet = new Set( - fetchedPapers.map((paper) => paper.subject), - ); - - const storedSubjectsArray = Array.isArray(storedSubjects) - ? storedSubjects - : []; - const missingSubjects = storedSubjectsArray - .filter((subject: string) => !fetchedSubjectsSet.has(subject)) - .map((subject: string) => ({ - subject, - slots: [], - })) as { subject: string; slots: string[] }[]; - - const allDisplayPapers = [...fetchedPapers, ...missingSubjects]; - - allDisplayPapers.sort((a, b) => { - const aIndex = storedSubjects.indexOf(a.subject); - const bIndex = storedSubjects.indexOf(b.subject); - - return ( - (aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) - - (bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex) - ); - }); - - setDisplayPapers(allDisplayPapers); - } catch (error) { - console.error("Failed to fetch papers:", error); - } - })(); - }; - - window.addEventListener("userSubjectsChanged", handleSubjectsChange); - - return () => { - window.removeEventListener("userSubjectsChanged", handleSubjectsChange); - }; - }, []); - - const plugins = [Autoplay({ delay: 8000, stopOnInteraction: true })]; - - return ( -
-
- {displayPapers.length > 0 ? ( - - {(() => { - const totalItems = displayPapers.length + 1; - const needsNav = totalItems > chunkSize; - return needsNav ? ( -
- - -
- ) : null; - })()} - - {isLoading ? ( - - - - ) : ( - chunkedPapers.map((paperGroup, index) => { - const placeholdersNeeded = - (chunkSize - paperGroup.length) % chunkSize; - return ( - - {paperGroup.map((paper, subIndex) => - paper.subject === "add_subject_button" ? ( -
- -
- ) : ( -
- -
- ), - )} - - {Array.from({ length: placeholdersNeeded }).map( - (_, placeholderIndex) => ( -
- ), - )} -
- ); - }) - )} -
-
- ) : ( -
- Start pinning subjects for quick and easy access. -
- - - -
-
- )} -
-
- ); -} - -export default PinnedPapersCarousel; From 1209a7112ee68f6218371f51db772866ad9123b7 Mon Sep 17 00:00:00 2001 From: Atharva Sharma Date: Mon, 20 Apr 2026 02:56:18 +0530 Subject: [PATCH 2/2] merge --- src/components/PinnedPapersCarousel.tsx | 268 ++++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/src/components/PinnedPapersCarousel.tsx b/src/components/PinnedPapersCarousel.tsx index e69de29..ec90d28 100644 --- a/src/components/PinnedPapersCarousel.tsx +++ b/src/components/PinnedPapersCarousel.tsx @@ -0,0 +1,268 @@ +"use client"; + +import { useEffect, useState } from "react"; +import axios from "axios"; +import { type IUpcomingPaper } from "@/interface"; +import UpcomingPaper from "./UpcomingPaper"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import Autoplay from "embla-carousel-autoplay"; +import { chunkArray } from "@/lib/utils/array"; +import { type StoredSubjects } from "@/interface"; +import SkeletonPaperCard from "./SkeletonPaperCard"; +import PinnedModal from "./ui/PinnedModal"; + +function PinnedPapersCarousel() { + const [isLoading, setIsLoading] = useState(true); + const [chunkSize, setChunkSize] = useState(4); + const [displayPapers, setDisplayPapers] = useState([]); + useEffect(() => { + const handleResize = () => { + if (window.innerWidth <= 540) { + setChunkSize(2); + } else if (window.innerWidth <= 920) { + setChunkSize(4); + } else { + setChunkSize(8); + } + }; + + handleResize(); // initialize + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + const chunkedPapers = chunkArray(displayPapers, chunkSize); + + if (chunkedPapers.length > 0) { + const lastChunkIndex = chunkedPapers.length - 1; + if ((chunkedPapers[lastChunkIndex]?.length ?? 0) < chunkSize) { + chunkedPapers[lastChunkIndex] = [ + ...(chunkedPapers[lastChunkIndex] ?? []), + { subject: "add_subject_button", slots: [] } as IUpcomingPaper, + ]; + } else { + chunkedPapers.push([ + { subject: "add_subject_button", slots: [] } as IUpcomingPaper, + ]); + } + } + + const fetchPapers = async () => { + try { + setIsLoading(true); + + const storedSubjects = JSON.parse( + localStorage.getItem("userSubjects") ?? "[]", + ) as StoredSubjects; + + const response = await axios.post<{ subject: string; slots: string[] }[]>( + "/api/user-papers", + storedSubjects, + ); + + const fetchedPapers = response.data; + + const fetchedSubjectsSet = new Set( + fetchedPapers.map((paper) => paper.subject), + ); + + const storedSubjectsArray = Array.isArray(storedSubjects) + ? storedSubjects + : []; + const missingSubjects = storedSubjectsArray + .filter((subject: string) => !fetchedSubjectsSet.has(subject)) + .map((subject: string) => ({ + subject, + slots: [], + })) as { subject: string; slots: string[] }[]; + + const allDisplayPapers = [...fetchedPapers, ...missingSubjects]; + + allDisplayPapers.sort((a, b) => { + const aIndex = storedSubjects.indexOf(a.subject); + const bIndex = storedSubjects.indexOf(b.subject); + + return ( + (aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) - + (bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex) + ); + }); + + setDisplayPapers(allDisplayPapers); + } catch (error) { + console.error("Failed to fetch papers:", error); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + void fetchPapers(); + }, []); + + useEffect(() => { + const handleSubjectsChange = () => { + void (async () => { + try { + const storedSubjects = JSON.parse( + localStorage.getItem("userSubjects") ?? "[]", + ) as StoredSubjects; + + const response = await axios.post< + { subject: string; slots: string[] }[] + >("/api/user-papers", storedSubjects); + + const fetchedPapers = response.data; + + const fetchedSubjectsSet = new Set( + fetchedPapers.map((paper) => paper.subject), + ); + + const storedSubjectsArray = Array.isArray(storedSubjects) + ? storedSubjects + : []; + const missingSubjects = storedSubjectsArray + .filter((subject: string) => !fetchedSubjectsSet.has(subject)) + .map((subject: string) => ({ + subject, + slots: [], + })) as { subject: string; slots: string[] }[]; + + const allDisplayPapers = [...fetchedPapers, ...missingSubjects]; + + allDisplayPapers.sort((a, b) => { + const aIndex = storedSubjects.indexOf(a.subject); + const bIndex = storedSubjects.indexOf(b.subject); + + return ( + (aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) - + (bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex) + ); + }); + + setDisplayPapers(allDisplayPapers); + } catch (error) { + console.error("Failed to fetch papers:", error); + } + })(); + }; + + window.addEventListener("userSubjectsChanged", handleSubjectsChange); + + return () => { + window.removeEventListener("userSubjectsChanged", handleSubjectsChange); + }; + }, []); + + const plugins = [Autoplay({ delay: 8000, stopOnInteraction: true })]; + + return ( +
+
+ {displayPapers.length > 0 ? ( + + {(() => { + const totalItems = displayPapers.length + 1; + const needsNav = totalItems > chunkSize; + return needsNav ? ( +
+ + +
+ ) : null; + })()} + + {isLoading ? ( + + + + ) : ( + chunkedPapers.map((paperGroup, index) => { + const columns = chunkSize === 2 ? 1 : chunkSize === 4 ? 2 : 4; + const rows = Math.max(1, Math.ceil(paperGroup.length / columns)); + const placeholdersNeeded = columns * rows - paperGroup.length; + return ( + + {paperGroup.map((paper, subIndex) => + paper.subject === "add_subject_button" ? ( +
+ +
+ ) : ( +
+ +
+ ), + )} + + {Array.from({ length: placeholdersNeeded }).map( + (_, placeholderIndex) => ( +
+ ), + )} +
+ ); + }) + )} +
+
+ ) : ( +
+ Start pinning subjects for quick and easy access. +
+ + + +
+
+ )} +
+
+ ); +} + +export default PinnedPapersCarousel; \ No newline at end of file