Skip to content

Commit 604cee0

Browse files
Merge pull request #410 from Advik-Gupta/staging
Make pinned subjects rearrangable by dragging
2 parents d28a18f + 814b2e8 commit 604cee0

4 files changed

Lines changed: 306 additions & 182 deletions

File tree

src/app/api/user-papers/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ export async function POST(req: Request) {
3131
{ status: 500 },
3232
);
3333
}
34-
}
34+
}

src/components/PinnedPapersCarousel.tsx

Lines changed: 134 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ function PinnedPapersCarousel() {
2323
const [displayPapers, setDisplayPapers] = useState<IUpcomingPaper[]>([]);
2424
useEffect(() => {
2525
const handleResize = () => {
26-
if(window.innerWidth <= 540){
26+
if (window.innerWidth <= 540) {
2727
setChunkSize(2);
28-
}
29-
else if (window.innerWidth <= 920) {
28+
} else if (window.innerWidth <= 920) {
3029
setChunkSize(4);
3130
} else {
3231
setChunkSize(8);
@@ -48,7 +47,9 @@ function PinnedPapersCarousel() {
4847
{ subject: "add_subject_button", slots: [] } as IUpcomingPaper,
4948
];
5049
} else {
51-
chunkedPapers.push([{ subject: "add_subject_button", slots: [] } as IUpcomingPaper]);
50+
chunkedPapers.push([
51+
{ subject: "add_subject_button", slots: [] } as IUpcomingPaper,
52+
]);
5253
}
5354
}
5455

@@ -96,42 +97,41 @@ function PinnedPapersCarousel() {
9697
}, []);
9798

9899
useEffect(() => {
99-
const handleSubjectsChange = () => {
100-
void (async () => {
101-
try {
102-
const storedSubjects = JSON.parse(
103-
localStorage.getItem("userSubjects") ?? "[]",
104-
) as StoredSubjects;
105-
106-
const response = await axios.post<{ subject: string; slots: string[] }[]>(
107-
"/api/user-papers",
108-
storedSubjects,
109-
);
110-
111-
const fetchedPapers = response.data;
112-
113-
const fetchedSubjectsSet = new Set(
114-
fetchedPapers.map((paper) => paper.subject),
115-
);
116-
117-
const storedSubjectsArray = Array.isArray(storedSubjects)
118-
? storedSubjects
119-
: [];
120-
const missingSubjects = storedSubjectsArray
121-
.filter((subject: string) => !fetchedSubjectsSet.has(subject))
122-
.map((subject: string) => ({
123-
subject,
124-
slots: [],
125-
})) as { subject: string; slots: string[] }[];
126-
127-
const allDisplayPapers = [...fetchedPapers, ...missingSubjects];
128-
129-
setDisplayPapers(allDisplayPapers);
130-
} catch (error) {
131-
console.error("Failed to fetch papers:", error);
132-
}
133-
})();
134-
};
100+
const handleSubjectsChange = () => {
101+
void (async () => {
102+
try {
103+
const storedSubjects = JSON.parse(
104+
localStorage.getItem("userSubjects") ?? "[]",
105+
) as StoredSubjects;
106+
107+
const response = await axios.post<
108+
{ subject: string; slots: string[] }[]
109+
>("/api/user-papers", storedSubjects);
110+
111+
const fetchedPapers = response.data;
112+
113+
const fetchedSubjectsSet = new Set(
114+
fetchedPapers.map((paper) => paper.subject),
115+
);
116+
117+
const storedSubjectsArray = Array.isArray(storedSubjects)
118+
? storedSubjects
119+
: [];
120+
const missingSubjects = storedSubjectsArray
121+
.filter((subject: string) => !fetchedSubjectsSet.has(subject))
122+
.map((subject: string) => ({
123+
subject,
124+
slots: [],
125+
})) as { subject: string; slots: string[] }[];
126+
127+
const allDisplayPapers = [...fetchedPapers, ...missingSubjects];
128+
129+
setDisplayPapers(allDisplayPapers);
130+
} catch (error) {
131+
console.error("Failed to fetch papers:", error);
132+
}
133+
})();
134+
};
135135

136136
window.addEventListener("userSubjectsChanged", handleSubjectsChange);
137137

@@ -143,83 +143,102 @@ function PinnedPapersCarousel() {
143143
const plugins = [Autoplay({ delay: 8000, stopOnInteraction: true })];
144144

145145
return (
146-
<div className="px-4 mt-8 md:mt-4">
146+
<div className="mt-8 px-4 md:mt-4">
147147
<div className="">
148-
{displayPapers.length > 0 ?
149-
<Carousel
150-
opts={{
151-
align: "start",
152-
loop: true,
153-
}}
154-
plugins={plugins}
155-
className="w-full"
156-
>
157-
{(() => {
158-
const totalItems = displayPapers.length + 1;
159-
const needsNav = totalItems > chunkSize;
160-
return needsNav ? (
161-
<div className="relative mt-4 flex justify-end gap-4">
162-
<CarouselPrevious className="relative" />
163-
<CarouselNext className="relative" />
164-
</div>
165-
) : null;
166-
})()}
167-
<CarouselContent>
168-
{isLoading ? (
169-
<CarouselItem
170-
className={`grid ${
171-
chunkSize === 2 ? "grid-cols-1 grid-rows-2" : chunkSize === 4 ? "grid-cols-2 grid-rows-2" : "grid-cols-4"
172-
} gap-4 lg:auto-rows-fr`}
173-
>
174-
<SkeletonPaperCard length={chunkSize} />
175-
</CarouselItem>
176-
) : (
177-
chunkedPapers.map((paperGroup, index) => {
178-
const placeholdersNeeded = (chunkSize - paperGroup.length) % chunkSize;
179-
return (
180-
<CarouselItem
181-
key={`carousel-item-${index}`}
182-
className={`grid ${
183-
chunkSize === 2 ? "grid-cols-1 grid-rows-2" : chunkSize === 4 ? "grid-cols-2 grid-rows-2" : "grid-cols-4"
184-
} gap-4 lg:auto-rows-fr`}
185-
>
186-
{paperGroup.map((paper, subIndex) => (
187-
paper.subject === "add_subject_button" ?
188-
<div key={subIndex} className="h-full border-dashed border border-[#734DFF] dark:border-[#36266D] rounded-sm font-bold hover:bg-[#EFEAFF] dark:bg-transparent dark:hover:bg-[#1A1823] bg-[#FFFFFF]">
189-
<PinnedModal triggerName={"Add Subjects"} page={"Carousel"}/>
190-
</div>
191-
:
192-
<div key={subIndex} className="h-full">
193-
<UpcomingPaper
194-
subject={paper.subject}
195-
slots={paper.slots}
196-
/>
197-
</div>
198-
))}
199-
200-
{Array.from({ length: placeholdersNeeded }).map(
201-
(_, placeholderIndex) => (
202-
<div
203-
key={`placeholder-${placeholderIndex}`}
204-
className="invisible h-full"
205-
></div>
206-
),
207-
)}
208-
</CarouselItem>
209-
);
210-
})
211-
)}
212-
</CarouselContent>
213-
</Carousel> :
214-
<div className={`relative flex flex-col justify-center gap-4 items-center text-center font-bold`}
215-
>
216-
Start pinning subjects for quick and easy access.
217-
<div className="flex h-8 items-center gap-1 rounded-full border border-[#3A3745] bg-[#e8e9ff] px-2.5 py-1 text-xs font-semibold text-gray-700 transition hover:bg-slate-50 dark:bg-black dark:text-white dark:hover:bg-[#1A1823] sm:h-9 sm:gap-2 sm:px-3.5 sm:py-1.5 sm:text-sm md:h-10 md:px-4 md:py-2 md:text-base">
218-
<span className="truncate">
219-
<PinnedModal/>
220-
</span>
148+
{displayPapers.length > 0 ? (
149+
<Carousel
150+
opts={{
151+
align: "start",
152+
loop: true,
153+
}}
154+
plugins={plugins}
155+
className="w-full"
156+
>
157+
{(() => {
158+
const totalItems = displayPapers.length + 1;
159+
const needsNav = totalItems > chunkSize;
160+
return needsNav ? (
161+
<div className="relative mt-4 flex justify-end gap-4">
162+
<CarouselPrevious className="relative" />
163+
<CarouselNext className="relative" />
164+
</div>
165+
) : null;
166+
})()}
167+
<CarouselContent>
168+
{isLoading ? (
169+
<CarouselItem
170+
className={`grid ${
171+
chunkSize === 2
172+
? "grid-cols-1 grid-rows-2"
173+
: chunkSize === 4
174+
? "grid-cols-2 grid-rows-2"
175+
: "grid-cols-4"
176+
} gap-4 lg:auto-rows-fr`}
177+
>
178+
<SkeletonPaperCard length={chunkSize} />
179+
</CarouselItem>
180+
) : (
181+
chunkedPapers.map((paperGroup, index) => {
182+
const placeholdersNeeded =
183+
(chunkSize - paperGroup.length) % chunkSize;
184+
return (
185+
<CarouselItem
186+
key={`carousel-item-${index}`}
187+
className={`grid ${
188+
chunkSize === 2
189+
? "grid-cols-1 grid-rows-2"
190+
: chunkSize === 4
191+
? "grid-cols-2 grid-rows-2"
192+
: "grid-cols-4"
193+
} gap-4 lg:auto-rows-fr`}
194+
>
195+
{paperGroup.map((paper, subIndex) =>
196+
paper.subject === "add_subject_button" ? (
197+
<div
198+
key={subIndex}
199+
className="h-full rounded-sm border border-dashed border-[#734DFF] bg-[#FFFFFF] font-bold hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-transparent dark:hover:bg-[#1A1823]"
200+
>
201+
<PinnedModal
202+
triggerName={"Add Subjects"}
203+
page={"Carousel"}
204+
/>
205+
</div>
206+
) : (
207+
<div key={subIndex} className="h-full">
208+
<UpcomingPaper
209+
subject={paper.subject}
210+
slots={paper.slots}
211+
/>
212+
</div>
213+
),
214+
)}
215+
216+
{Array.from({ length: placeholdersNeeded }).map(
217+
(_, placeholderIndex) => (
218+
<div
219+
key={`placeholder-${placeholderIndex}`}
220+
className="invisible h-full"
221+
></div>
222+
),
223+
)}
224+
</CarouselItem>
225+
);
226+
})
227+
)}
228+
</CarouselContent>
229+
</Carousel>
230+
) : (
231+
<div
232+
className={`relative flex flex-col items-center justify-center gap-4 text-center font-bold`}
233+
>
234+
Start pinning subjects for quick and easy access.
235+
<div className="flex h-8 items-center gap-1 rounded-full border border-[#3A3745] bg-[#e8e9ff] px-2.5 py-1 text-xs font-semibold text-gray-700 transition hover:bg-slate-50 dark:bg-black dark:text-white dark:hover:bg-[#1A1823] sm:h-9 sm:gap-2 sm:px-3.5 sm:py-1.5 sm:text-sm md:h-10 md:px-4 md:py-2 md:text-base">
236+
<span className="truncate">
237+
<PinnedModal />
238+
</span>
239+
</div>
221240
</div>
222-
</div>}
241+
)}
223242
</div>
224243
</div>
225244
);

0 commit comments

Comments
 (0)