Updated carousels layout with menutab of tags on home page

This commit is contained in:
austinkelsay 2025-03-18 13:05:27 -05:00 committed by austinkelsay
parent 3d8ba067e7
commit 79a0559995
No known key found for this signature in database
GPG Key ID: 5A763922E5BA08EE
10 changed files with 239 additions and 56 deletions

View File

@ -2,7 +2,6 @@ import React, { useEffect, useState, useRef } from 'react';
import useWindowWidth from '@/hooks/useWindowWidth';
import Image from 'next/image';
import { getSession, signIn, useSession } from 'next-auth/react';
import { useImageProxy } from '@/hooks/useImageProxy';
import { useRouter } from 'next/router';
import { Avatar } from 'primereact/avatar';
import { AvatarGroup } from 'primereact/avatargroup';
@ -15,7 +14,6 @@ const HeroBanner = () => {
const techs = ['Bitcoin', 'Lightning', 'Nostr'];
const windowWidth = useWindowWidth();
const router = useRouter();
const { returnImageProxy } = useImageProxy();
const { data: session } = useSession();
const isTabView = windowWidth <= 1360;
@ -48,10 +46,10 @@ const HeroBanner = () => {
};
const getHeroHeight = () => {
if (isSuperWideScreen) return 'h-[900px]';
if (isWideScreen) return 'h-[700px]';
if (isMobile) return 'h-[450px]';
return 'h-[600px]';
if (isSuperWideScreen) return 'h-[700px]';
if (isWideScreen) return 'h-[550px]';
if (isMobile) return 'h-[400px]';
return 'h-[500px]';
};
const handleLearnToCode = async () => {
@ -105,7 +103,7 @@ const HeroBanner = () => {
};
return (
<div className={`${getHeroHeight()} ${isTabView ? 'mx-0' : 'm-14'} relative flex justify-center items-center overflow-hidden drop-shadow-2xl`}>
<div className={`${getHeroHeight()} ${isTabView ? 'mx-0 w-full' : 'm-4'} relative flex justify-center items-center overflow-hidden drop-shadow-2xl rounded-lg`}>
<Image
src={HeroImage}
alt="Banner"
@ -117,7 +115,7 @@ const HeroBanner = () => {
<div className="absolute inset-0 bg-gradient-to-br from-black via-black/20 to-transparent rounded-lg" />
{!isTabView && (
<div className="absolute right-0 top-24 bottom-0 w-1/2 overflow-hidden rounded-l-lg opacity-100 p-2 rounded-lg shadow-lg mr-2">
<div className="absolute right-0 top-auto bottom-auto w-1/2 overflow-hidden opacity-100 p-4 shadow-lg">
<video
className="w-full object-cover rounded-lg shadow-lg"
autoPlay
@ -132,8 +130,8 @@ const HeroBanner = () => {
</div>
)}
<div className={`absolute inset-0 flex flex-col justify-center ${isTabView ? 'items-center text-center' : 'items-start pl-8'}`}>
<h1 className={`text-4xl sm:text-4xl lg:text-6xl font-bold leading-tight mb-6 ${isTabView ? 'px-4' : 'max-w-[50%]'}`}>
<div className={`absolute inset-0 flex flex-col justify-center ${isTabView ? 'items-center text-center px-4' : 'items-start pl-8'}`}>
<h1 className={`text-4xl sm:text-4xl lg:text-6xl font-bold leading-tight mb-4 ${isTabView ? 'px-4' : 'max-w-[50%]'}`}>
<span className="block">Learn to code</span>
<span className={`block ${isTabView ? `transition-opacity duration-500 ${isAnimating ? 'opacity-0' : 'opacity-100'}` : ''}`}>
Build on{' '}
@ -144,16 +142,16 @@ const HeroBanner = () => {
<span className="block">Become a dev</span>
</h1>
{isMobile ? (
<h3 className="text-[#f8f8ff] mb-8 font-semibold">
<h3 className="text-[#f8f8ff] mb-6 font-semibold">
A one of a kind developer education, content, and community platform built on Nostr and fully Lightning integrated.
</h3>
) : (
<h2 className="text-[#f8f8ff] mb-8 font-semibold max-w-[42%]">
<h2 className="text-[#f8f8ff] mb-6 font-semibold max-w-[42%]">
A one of a kind developer education, content, and community platform built on Nostr and fully Lightning integrated.
</h2>
)}
<div
className="mb-8 flex flex-row hover:opacity-70 cursor-pointer"
className="mb-6 flex flex-row hover:opacity-70 cursor-pointer"
onClick={() => !isMobile && window.open('https://www.udemy.com/user/austin-james-kelsay/', '_blank')}
style={{ cursor: isMobile ? 'default' : 'pointer' }}
>
@ -180,21 +178,10 @@ const HeroBanner = () => {
label="Learn To Code"
icon={<i className="pi pi-book pr-2 text-2xl" />}
rounded
severity="info"
className="border-2"
size={isMobile ? null : "large"}
outlined
onClick={handleLearnToCode}
/>
<GenericButton
label="Level Up"
icon={<i className="pi pi-video pr-2 text-2xl" />}
rounded
size={isMobile ? null : "large"}
severity="success"
className="border-2"
outlined
onClick={() => router.push('/content?tag=all')}
onClick={handleLearnToCode}
/>
</div>
</div>

View File

@ -56,18 +56,18 @@ export default function CoursesCarousel() {
return (
<>
<h3 className={`ml-[6%] mt-4 max-mob:text-3xl max-mob:ml-10`}>Courses</h3>
<Divider className={`${isMobileView ? '' : 'hidden'}`} />
<h3 className={`ml-[3%] mt-4 max-mob:text-2xl max-tab:ml-10 max-mob:ml-5`}>Courses</h3>
<Divider className='w-[95%] mx-auto max-tab:hidden max-mob:w-[100%]' />
<div className={"min-h-[384px]"}>
<Carousel
value={coursesLoading || !processedCourses.length ? [{}, {}, {}] : [...processedCourses]}
numVisible={2}
pt={{
previousButton: {
className: isMobileView ? 'm-0' : ''
className: 'm-0'
},
nextButton: {
className: isMobileView ? 'm-0' : ''
className: 'm-0'
}
}}
itemTemplate={(item) =>

View File

@ -82,17 +82,17 @@ export default function DocumentsCarousel() {
return (
<>
<h3 className={`ml-[6%] mt-4 max-mob:text-3xl max-mob:ml-10`}>Documents</h3>
<Divider className={`${isMobileView ? '' : 'hidden'}`} />
<h3 className={`ml-[3%] mt-4 max-mob:text-2xl max-tab:ml-10 max-mob:ml-5`}>Documents</h3>
<Divider className='w-[95%] mx-auto max-tab:hidden max-mob:w-[100%]' />
<Carousel
value={documentsLoading || !processedDocuments.length ? [{}, {}, {}] : [...processedDocuments]}
numVisible={2}
pt={{
previousButton: {
className: isMobileView ? 'm-0' : ''
className: 'm-0'
},
nextButton: {
className: isMobileView ? 'm-0' : ''
className: 'm-0'
}
}}
itemTemplate={(item) =>

View File

@ -83,17 +83,17 @@ export default function VideosCarousel() {
return (
<>
<h3 className={`ml-[6%] mt-4 max-mob:text-3xl max-mob:ml-10`}>Videos</h3>
<Divider className={`${isMobileView ? '' : 'hidden'}`} />
<h3 className={`ml-[3%] mt-4 max-mob:text-2xl max-tab:ml-10 max-mob:ml-5`}>Videos</h3>
<Divider className='w-[95%] mx-auto max-tab:hidden max-mob:w-[100%]' />
<Carousel
value={videosLoading || !processedVideos.length ? [{}, {}, {}] : [...processedVideos]}
numVisible={2}
pt={{
previousButton: {
className: isMobileView ? 'm-0' : ''
className: 'm-0'
},
nextButton: {
className: isMobileView ? 'm-0' : ''
className: 'm-0'
}
}}
itemTemplate={(item) =>

View File

@ -9,7 +9,7 @@ const appConfig = {
"wss://purplerelay.com/",
"wss://relay.devs.tools/"
],
authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345"],
authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345", "6260f29fa75c91aaa292f082e5e87b438d2ab4fdf96af398567b01802ee2fcd4"],
customLightningAddresses: [
{
// todo remove need for lowercase

View File

@ -11,6 +11,7 @@ export function useCourses() {
const [coursesError, setCoursesError] = useState(null);
const { contentIds } = useContentIdsQuery()
// const contentIds = ["f6825391-831c-44da-904a-9ac3d149b7be","f73c37f4-df2e-4f7d-a838-dce568c76136","f538f5c5-1a72-4804-8eb1-3f05cea64874","e58a42c0-c7c6-4b0e-8206-ace7df59a2b8","751ba534-e13a-4ed6-8f8b-452bf482f944","711924b4-3efe-4603-8a23-0ed42c88a63c","42f19d87-3a40-46bb-96b9-e7d31ded89bb","d084d45e-05fa-48fc-afa7-b54499f9e21e","ef588bcb-79a3-4ce9-a370-28c1dc7b604e","5bb34e83-599b-4494-9790-db2ac087baed","67d24075-7cda-4d38-93ad-cd0ef32f13f1","91c15fc0-bb11-43b4-83e6-4b8fd3a826ac","e25f3d3b-f28b-4edd-a325-380564e6db7d","558f88a6-fd1e-4482-9ae7-0f52d7fb82f0","181ed1a5-1f6b-4f68-bb5f-3c2f993f3c72","a0a7d5d2-d416-4bff-9a7f-836b51891fed","38180242-dd5c-4bae-ace4-d46b854d5b7d","5aeb5c26-0d6e-48d7-9436-11ff490a2adc","8b08d474-2ee9-4468-9976-19284c562e13","7d1c7b9b-0ad4-4199-a462-eeb936ea30c9","d607625a-81e1-4098-ba17-068ed29b05ed","e0b93cb8-ba23-4a6b-b7c9-efda9c886b7c","26fb6c60-3655-4193-998d-6e920caead68","4372a797-3565-44ab-a801-042ed5087873","18fd6bfd-fec1-4496-9871-2630b4f3d3b0","f3a61a3e-7523-43ff-9df3-7581c1436c36","b4deaa04-0c78-4526-80ec-362944fe35b6","dbc8672a-d09e-413b-a076-7b86bcc75ca6","5bba9ee9-a37b-496d-b524-e70ebd7140cc","c14bbbde-63b1-4341-a848-f67e07ee8618","e3d7ccfc-0374-46b9-98ff-5526247e13e5","164a7d28-3677-4f68-9fbb-ce2ff5cc4684","89da8522-f8dd-44e2-a49d-7be58328c9ff","03586c4c-5c4a-4bd6-b5e9-bfad02bb22c5","2c79dca4-334b-4805-9e97-fb1a27351580","93f52ce2-ebeb-445b-b698-757815ddb450","ee31bfb2-a2cd-4a31-87e1-da4f3e3c70fa","8cb1d556-df1b-4fef-a625-69c1fe1c397c","1f889e07-0fe2-4944-976d-7e602d3c1fb7","2553ed1a-d8ab-4fa5-a160-4a668a672a5e","80aac9d4-8bef-4a92-9ee9-dea1c2d66c3a","6fe3cb4b-2571-4e3b-9159-db78325ee5cc","a3083ab5-0187-4b77-83d1-29ae1f644559","e5399c72-9b95-46d6-a594-498e673b6c58","f93827ed-68ad-4b5e-af33-f7424b37f0d6","16a65e26-e5d9-450f-9b98-79d539b8acb0","8ce74cf3-bc67-4e95-83b6-a01983a1b586","6d8260b3-c902-46ec-8aed-f3b8c8f1229b"]
const {ndk, addSigner} = useNDKContext();
useEffect(() => {

View File

@ -11,6 +11,7 @@ export function useDocuments() {
const [documentsError, setDocumentsError] = useState(null);
const { contentIds } = useContentIdsQuery()
// const contentIds = ["f6825391-831c-44da-904a-9ac3d149b7be","f73c37f4-df2e-4f7d-a838-dce568c76136","f538f5c5-1a72-4804-8eb1-3f05cea64874","e58a42c0-c7c6-4b0e-8206-ace7df59a2b8","751ba534-e13a-4ed6-8f8b-452bf482f944","711924b4-3efe-4603-8a23-0ed42c88a63c","42f19d87-3a40-46bb-96b9-e7d31ded89bb","d084d45e-05fa-48fc-afa7-b54499f9e21e","ef588bcb-79a3-4ce9-a370-28c1dc7b604e","5bb34e83-599b-4494-9790-db2ac087baed","67d24075-7cda-4d38-93ad-cd0ef32f13f1","91c15fc0-bb11-43b4-83e6-4b8fd3a826ac","e25f3d3b-f28b-4edd-a325-380564e6db7d","558f88a6-fd1e-4482-9ae7-0f52d7fb82f0","181ed1a5-1f6b-4f68-bb5f-3c2f993f3c72","a0a7d5d2-d416-4bff-9a7f-836b51891fed","38180242-dd5c-4bae-ace4-d46b854d5b7d","5aeb5c26-0d6e-48d7-9436-11ff490a2adc","8b08d474-2ee9-4468-9976-19284c562e13","7d1c7b9b-0ad4-4199-a462-eeb936ea30c9","d607625a-81e1-4098-ba17-068ed29b05ed","e0b93cb8-ba23-4a6b-b7c9-efda9c886b7c","26fb6c60-3655-4193-998d-6e920caead68","4372a797-3565-44ab-a801-042ed5087873","18fd6bfd-fec1-4496-9871-2630b4f3d3b0","f3a61a3e-7523-43ff-9df3-7581c1436c36","b4deaa04-0c78-4526-80ec-362944fe35b6","dbc8672a-d09e-413b-a076-7b86bcc75ca6","5bba9ee9-a37b-496d-b524-e70ebd7140cc","c14bbbde-63b1-4341-a848-f67e07ee8618","e3d7ccfc-0374-46b9-98ff-5526247e13e5","164a7d28-3677-4f68-9fbb-ce2ff5cc4684","89da8522-f8dd-44e2-a49d-7be58328c9ff","03586c4c-5c4a-4bd6-b5e9-bfad02bb22c5","2c79dca4-334b-4805-9e97-fb1a27351580","93f52ce2-ebeb-445b-b698-757815ddb450","ee31bfb2-a2cd-4a31-87e1-da4f3e3c70fa","8cb1d556-df1b-4fef-a625-69c1fe1c397c","1f889e07-0fe2-4944-976d-7e602d3c1fb7","2553ed1a-d8ab-4fa5-a160-4a668a672a5e","80aac9d4-8bef-4a92-9ee9-dea1c2d66c3a","6fe3cb4b-2571-4e3b-9159-db78325ee5cc","a3083ab5-0187-4b77-83d1-29ae1f644559","e5399c72-9b95-46d6-a594-498e673b6c58","f93827ed-68ad-4b5e-af33-f7424b37f0d6","16a65e26-e5d9-450f-9b98-79d539b8acb0","8ce74cf3-bc67-4e95-83b6-a01983a1b586","6d8260b3-c902-46ec-8aed-f3b8c8f1229b"]
const {ndk, addSigner} = useNDKContext();
useEffect(() => {

View File

@ -11,6 +11,7 @@ export function useVideos() {
const [videosError, setVideosError] = useState(null);
const { contentIds } = useContentIdsQuery()
// const contentIds = ["f6825391-831c-44da-904a-9ac3d149b7be","f73c37f4-df2e-4f7d-a838-dce568c76136","f538f5c5-1a72-4804-8eb1-3f05cea64874","e58a42c0-c7c6-4b0e-8206-ace7df59a2b8","751ba534-e13a-4ed6-8f8b-452bf482f944","711924b4-3efe-4603-8a23-0ed42c88a63c","42f19d87-3a40-46bb-96b9-e7d31ded89bb","d084d45e-05fa-48fc-afa7-b54499f9e21e","ef588bcb-79a3-4ce9-a370-28c1dc7b604e","5bb34e83-599b-4494-9790-db2ac087baed","67d24075-7cda-4d38-93ad-cd0ef32f13f1","91c15fc0-bb11-43b4-83e6-4b8fd3a826ac","e25f3d3b-f28b-4edd-a325-380564e6db7d","558f88a6-fd1e-4482-9ae7-0f52d7fb82f0","181ed1a5-1f6b-4f68-bb5f-3c2f993f3c72","a0a7d5d2-d416-4bff-9a7f-836b51891fed","38180242-dd5c-4bae-ace4-d46b854d5b7d","5aeb5c26-0d6e-48d7-9436-11ff490a2adc","8b08d474-2ee9-4468-9976-19284c562e13","7d1c7b9b-0ad4-4199-a462-eeb936ea30c9","d607625a-81e1-4098-ba17-068ed29b05ed","e0b93cb8-ba23-4a6b-b7c9-efda9c886b7c","26fb6c60-3655-4193-998d-6e920caead68","4372a797-3565-44ab-a801-042ed5087873","18fd6bfd-fec1-4496-9871-2630b4f3d3b0","f3a61a3e-7523-43ff-9df3-7581c1436c36","b4deaa04-0c78-4526-80ec-362944fe35b6","dbc8672a-d09e-413b-a076-7b86bcc75ca6","5bba9ee9-a37b-496d-b524-e70ebd7140cc","c14bbbde-63b1-4341-a848-f67e07ee8618","e3d7ccfc-0374-46b9-98ff-5526247e13e5","164a7d28-3677-4f68-9fbb-ce2ff5cc4684","89da8522-f8dd-44e2-a49d-7be58328c9ff","03586c4c-5c4a-4bd6-b5e9-bfad02bb22c5","2c79dca4-334b-4805-9e97-fb1a27351580","93f52ce2-ebeb-445b-b698-757815ddb450","ee31bfb2-a2cd-4a31-87e1-da4f3e3c70fa","8cb1d556-df1b-4fef-a625-69c1fe1c397c","1f889e07-0fe2-4944-976d-7e602d3c1fb7","2553ed1a-d8ab-4fa5-a160-4a668a672a5e","80aac9d4-8bef-4a92-9ee9-dea1c2d66c3a","6fe3cb4b-2571-4e3b-9159-db78325ee5cc","a3083ab5-0187-4b77-83d1-29ae1f644559","e5399c72-9b95-46d6-a594-498e673b6c58","f93827ed-68ad-4b5e-af33-f7424b37f0d6","16a65e26-e5d9-450f-9b98-79d539b8acb0","8ce74cf3-bc67-4e95-83b6-a01983a1b586","6d8260b3-c902-46ec-8aed-f3b8c8f1229b"]
const {ndk, addSigner} = useNDKContext();
useEffect(() => {

View File

@ -36,7 +36,7 @@ export default function MyApp({
<Layout>
<div className="flex flex-col min-h-screen">
<Navbar />
<main className="flex-1 container mx-auto px-4 py-4">
<main>
<Component {...pageProps} />
<Analytics />
</main>

View File

@ -1,25 +1,218 @@
import Head from 'next/head';
import React from 'react';
import React, { useEffect, useState, useMemo } from 'react';
// import GenericCarousel from '@/components/content/carousels/GenericCarousel';
import CoursesCarousel from '@/components/content/carousels/CoursesCarousel';
import VideosCarousel from '@/components/content/carousels/VideosCarousel';
import DocumentsCarousel from '@/components/content/carousels/DocumentsCarousel';
import { parseEvent, parseCourseEvent } from '@/utils/nostr';
import { useDocuments } from '@/hooks/nostr/useDocuments';
import { useVideos } from '@/hooks/nostr/useVideos';
import { useCourses } from '@/hooks/nostr/useCourses';
import { TabMenu } from 'primereact/tabmenu';
import 'primeicons/primeicons.css';
import GenericButton from '@/components/buttons/GenericButton';
import { useRouter } from 'next/router';
import HeroBanner from '@/components/banner/HeroBanner';
const TOP_CONTENT_IDS = [
"72aa9f48-a0d1-4e0e-ba6b-de3e8ef8e9ad",
"d493fc22-20ab-47fc-ab55-b017b92bb0fe",
"936a024a-719c-455c-8c9d-2657ebd8a5f0",
"dfa1e4ed-1387-418d-a166-84dbdb9701df",
"f806c5c6-7b88-4be6-911e-0f7e08a0dac2",
"ad217a77-7422-45eb-979e-fe8cefd89a1f",
"3dcc434a-7b65-4cf4-883a-db117fe97b77",
"e0495afa-d879-47d8-a8be-843785e47611",
"8670318e-91d0-443f-9ceb-8324cd1bdabc",
"5d67444d-0b91-423b-b927-3def8535adc8"
];
const MenuTab = ({ selectedTopic, onTabChange, allTopics }) => {
const router = useRouter();
// Define the hardcoded priority items that always appear first
const priorityItems = ['Top', 'Courses', 'Videos', 'Documents', 'Free', 'Paid'];
// Items that should be filtered out from topics
const blacklistedItems = ['document', 'video', 'course'];
// Get dynamic topics, excluding hardcoded and blacklisted items
const otherItems = allTopics.filter(item =>
!priorityItems.includes(item) &&
!blacklistedItems.includes(item)
);
// Only take the first 4 dynamic topics to keep the menu clean
// Additional topics will be accessible through the "More" page
const limitedOtherItems = otherItems.slice(0, 8);
// Combine all menu items: priority items + up to 4 dynamic topics + More
const allItems = [...priorityItems, ...limitedOtherItems, 'More'];
const menuItems = allItems.map((item) => {
let icon = 'pi pi-tag';
if (item === 'Top') icon = 'pi pi-star';
else if (item === 'Documents') icon = 'pi pi-file';
else if (item === 'Videos') icon = 'pi pi-video';
else if (item === 'Courses') icon = 'pi pi-desktop';
else if (item === 'Free') icon = 'pi pi-lock-open';
else if (item === 'Paid') icon = 'pi pi-lock';
else if (item === 'More') icon = 'pi pi-ellipsis-h';
const isMore = item === 'More';
const path = isMore ? '/content?tag=all' : item === 'Top' ? '/' : `/content?tag=${item.toLowerCase()}`;
return {
label: (
<GenericButton
className={`${selectedTopic === item ? 'bg-primary text-white' : ''}`}
onClick={() => {
onTabChange(item);
router.push(path);
}}
outlined={selectedTopic !== item}
rounded
size='small'
label={item}
icon={icon}
/>
),
command: () => {
onTabChange(item);
router.push(path);
}
};
});
return (
<div className="w-full">
<TabMenu
model={menuItems}
activeIndex={allItems.indexOf(selectedTopic)}
onTabChange={(e) => onTabChange(allItems[e.index])}
pt={{
menu: { className: 'bg-transparent border-none my-2 mb-4' },
action: ({ context, parent }) => ({
className: 'cursor-pointer select-none flex items-center relative no-underline overflow-hidden border-b-2 p-2 font-bold rounded-t-lg',
style: { top: '2px' }
}),
menuitem: { className: 'mr-0' }
}}
/>
</div>
);
}
export default function Home() {
return (
<>
<Head>
<title>PlebDevs</title>
<meta name="description" content="Build on Bitcoin" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<HeroBanner />
<CoursesCarousel />
<VideosCarousel />
<DocumentsCarousel />
</main>
</>
);
const router = useRouter();
const { documents, documentsLoading } = useDocuments();
const { videos, videosLoading } = useVideos();
const { courses, coursesLoading } = useCourses();
const [processedDocuments, setProcessedDocuments] = useState([]);
const [processedVideos, setProcessedVideos] = useState([]);
const [processedCourses, setProcessedCourses] = useState([]);
const [allContent, setAllContent] = useState([]);
const [allTopics, setAllTopics] = useState([]);
const [selectedTopic, setSelectedTopic] = useState('Top');
const [filteredContent, setFilteredContent] = useState([]);
const memoizedFilteredContent = useMemo(() => filteredContent, [filteredContent]);
useEffect(() => {
if (documents && !documentsLoading) {
const processedDocuments = documents.map(document => ({...parseEvent(document), type: 'document'}));
setProcessedDocuments(processedDocuments);
}
}, [documents, documentsLoading]);
useEffect(() => {
if (videos && !videosLoading) {
const processedVideos = videos.map(video => ({...parseEvent(video), type: 'video'}));
setProcessedVideos(processedVideos);
}
}, [videos, videosLoading]);
useEffect(() => {
if (courses && !coursesLoading) {
const processedCourses = courses.map(course => ({...parseCourseEvent(course), type: 'course'}));
setProcessedCourses(processedCourses);
}
}, [courses, coursesLoading]);
useEffect(() => {
const allContent = [...processedDocuments, ...processedVideos, ...processedCourses];
setAllContent(allContent);
const uniqueTopics = new Set(allContent.map(item => item.topics).flat());
const otherTopics = Array.from(uniqueTopics).filter(topic =>
!['Top', 'Courses', 'Videos', 'Documents', 'Free', 'Paid', 'More'].includes(topic) &&
!['document', 'video', 'course'].includes(topic)
);
setAllTopics(otherTopics);
if (selectedTopic === 'Top') {
const topContent = allContent.filter(item => TOP_CONTENT_IDS.includes(item.d));
setFilteredContent(topContent);
} else {
filterContent(selectedTopic, allContent);
}
}, [processedDocuments, processedVideos, processedCourses, selectedTopic]);
const filterContent = (topic, content) => {
let filtered = content;
if (topic !== 'Top' && topic !== 'More') {
const topicLower = topic.toLowerCase();
if (['courses', 'videos', 'documents'].includes(topicLower)) {
filtered = content.filter(item => item.type === topicLower.slice(0, -1));
} else if (topicLower === 'free') {
filtered = content.filter(item => !item.price || Number(item.price) === 0);
} else if (topicLower === 'paid') {
filtered = content.filter(item => item.price && Number(item.price) > 0);
} else {
filtered = content.filter(item => item.topics && item.topics.includes(topicLower));
}
}
setFilteredContent(filtered);
};
const handleTopicChange = (newTopic) => {
setSelectedTopic(newTopic);
if (newTopic === 'Top') {
const topContent = allContent.filter(item => TOP_CONTENT_IDS.includes(item.d));
setFilteredContent(topContent);
router.push('/');
} else if (newTopic === 'More') {
router.push('/content?tag=all');
} else {
filterContent(newTopic, allContent);
router.push(`/content?tag=${newTopic.toLowerCase()}`);
}
};
return (
<>
<Head>
<title>PlebDevs</title>
<meta name="description" content="Build on Bitcoin" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<HeroBanner />
<div className="w-full px-12">
<MenuTab
selectedTopic={selectedTopic}
onTabChange={handleTopicChange}
allTopics={allTopics}
className="max-w-[90%] mx-auto"
/>
</div>
<div className="w-full px-4">
<CoursesCarousel />
<VideosCarousel />
<DocumentsCarousel />
</div>
</main>
</>
);
}