Merge pull request #42 from AustinKelsay/bugfix/carousel-cards-disappear-when-clicking-zap

Grid based layout for resource cards using tailwind instead of using primereact carousel component, this fixed the bug
This commit is contained in:
Austin Kelsay 2025-03-18 16:24:30 -05:00 committed by GitHub
commit e72da87b01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,34 +1,14 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react'; import React, { useState, useEffect } from 'react';
import axios from 'axios'; import axios from 'axios';
import { Carousel } from 'primereact/carousel';
import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton'; import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton';
import { VideoTemplate } from '@/components/content/carousels/templates/VideoTemplate'; import { VideoTemplate } from '@/components/content/carousels/templates/VideoTemplate';
import { DocumentTemplate } from '@/components/content/carousels/templates/DocumentTemplate'; import { DocumentTemplate } from '@/components/content/carousels/templates/DocumentTemplate';
import { CourseTemplate } from '@/components/content/carousels/templates/CourseTemplate'; import { CourseTemplate } from '@/components/content/carousels/templates/CourseTemplate';
import { CombinedTemplate } from '@/components/content/carousels/templates/CombinedTemplate'; import { CombinedTemplate } from '@/components/content/carousels/templates/CombinedTemplate';
import debounce from 'lodash/debounce';
const responsiveOptions = [
{
breakpoint: '3000px',
numVisible: 3,
},
{
breakpoint: '1462px',
numVisible: 2,
},
{
breakpoint: '575px',
numVisible: 1,
}
];
export default function GenericCarousel({items, selectedTopic, title}) { export default function GenericCarousel({items, selectedTopic, title}) {
const [carousels, setCarousels] = useState([]);
const [lessons, setLessons] = useState([]); const [lessons, setLessons] = useState([]);
const memoizedItems = useMemo(() => items, [items]);
useEffect(() => { useEffect(() => {
axios.get('/api/lessons').then(res => { axios.get('/api/lessons').then(res => {
if (res.data) { if (res.data) {
@ -41,60 +21,51 @@ export default function GenericCarousel({items, selectedTopic, title}) {
}); });
}, []); }, []);
const getItemsPerCarousel = useCallback(() => { const generateUniqueTemplateKey = (item, index, type) => {
const width = window.innerWidth; if (!item) return `${type}-${index}`;
if (width <= 575) return 1; const baseKey = item.id || item.d || `${type}-${index}`;
if (width <= 1462) return 2; return `${type}-${baseKey}-${index}`;
return 3; };
}, []);
const updateCarousels = useCallback(() => { const renderItem = (item, index) => {
const itemsPerCarousel = getItemsPerCarousel(); if (!item) return <TemplateSkeleton key={generateUniqueTemplateKey(item, index, 'skeleton')} />;
const newCarousels = [];
for (let i = 0; i < memoizedItems.length; i += itemsPerCarousel) { if (item.topics?.includes('video') && item.topics?.includes('document')) {
newCarousels.push(memoizedItems.slice(i, i + itemsPerCarousel)); return <CombinedTemplate
key={generateUniqueTemplateKey(item, index, 'combined')}
resource={item}
isLesson={lessons.includes(item?.d)}
/>;
} else if (item.type === 'document') {
return <DocumentTemplate
key={generateUniqueTemplateKey(item, index, 'document')}
document={item}
isLesson={lessons.includes(item?.d)}
/>;
} else if (item.type === 'video') {
return <VideoTemplate
key={generateUniqueTemplateKey(item, index, 'video')}
video={item}
isLesson={lessons.includes(item?.d)}
/>;
} else if (item.type === 'course') {
return <CourseTemplate
key={generateUniqueTemplateKey(item, index, 'course')}
course={item}
/>;
} }
setCarousels(newCarousels); return <TemplateSkeleton key={generateUniqueTemplateKey(item, index, 'fallback')} />;
}, [memoizedItems, getItemsPerCarousel]); };
useEffect(() => {
updateCarousels();
const debouncedHandleResize = debounce(updateCarousels, 250);
window.addEventListener('resize', debouncedHandleResize);
return () => {
window.removeEventListener('resize', debouncedHandleResize);
};
}, [updateCarousels, memoizedItems]);
return ( return (
<> <div className="w-full px-4 mb-4">
{carousels.map((carouselItems, index) => ( <div className="grid grid-cols-2 gap-4 max-w-full max-tab:grid-cols-1 lg:grid-cols-3">
<Carousel {items.map((item, index) => (
key={index} <div key={generateUniqueTemplateKey(item, index, 'container')} className="w-full min-w-0">
value={carouselItems} {renderItem(item, index)}
itemTemplate={(item) => { </div>
if (carouselItems.length > 0) { ))}
if (item.topics?.includes('video') && item.topics?.includes('document')) { </div>
return <CombinedTemplate key={item.id} resource={item} isLesson={lessons.includes(item?.d)} />; </div>
} else if (item.type === 'document') {
return <DocumentTemplate key={item.id} document={item} isLesson={lessons.includes(item?.d)} />;
} else if (item.type === 'video') {
return <VideoTemplate key={item.id} video={item} isLesson={lessons.includes(item?.d)} />;
} else if (item.type === 'course') {
return <CourseTemplate key={item.id} course={item} />;
}
}
return <TemplateSkeleton key={Math.random()} />;
}}
responsiveOptions={responsiveOptions}
className="mb-4"
pt={{
previousButton: { className: 'hidden' },
nextButton: { className: 'hidden' }
}}
/>
))}
</>
); );
} }