mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 19:01:19 +00:00
Styling fixes, add video to promotional carousel
This commit is contained in:
parent
3ce3a2f037
commit
17ca8fcf59
BIN
public/videos/plebdevs-montage.mp4
Normal file
BIN
public/videos/plebdevs-montage.mp4
Normal file
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
import Image from "next/image"
|
||||
import { useState } from "react"
|
||||
import { useState, useRef, useEffect } from "react"
|
||||
import { useImageProxy } from "@/hooks/useImageProxy"
|
||||
import GenericButton from "@/components/buttons/GenericButton"
|
||||
import { useRouter } from "next/router"
|
||||
@ -13,68 +13,69 @@ const promotions = [
|
||||
title: "Developer education & community platform",
|
||||
description: "PlebDevs is your gateway to mastering Bitcoin, Lightning, and Nostr technologies. Join our community of aspiring developers and start your journey today!",
|
||||
icon: "pi pi-code",
|
||||
image: "https://media.istockphoto.com/id/537331500/photo/programming-code-abstract-technology-background-of-software-deve.jpg?s=612x612&w=0&k=20&c=jlYes8ZfnCmD0lLn-vKvzQoKXrWaEcVypHnB5MuO-g8=",
|
||||
video: "/videos/plebdevs-montage.mp4",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category: "COURSES",
|
||||
title: "Structured learning paths for new devs",
|
||||
description: "Dive into our comprehensive courses covering Bitcoin protocol, Lightning Network, and Nostr. From basics to advanced topics, we've got you covered.",
|
||||
category: "CONTENT",
|
||||
title: "Comprehensive Learning Resources",
|
||||
description: "Access our extensive library of courses, videos, and documents. From structured learning paths to hands-on workshops, we've got everything you need to master Bitcoin, Lightning, and Nostr development.",
|
||||
icon: "pi pi-book",
|
||||
image: "https://media.istockphoto.com/id/1224500457/photo/programming-code-abstract-technology-background-of-software-developer-and-computer-script.jpg?s=612x612&w=0&k=20&c=nHMypkMTU1HUUW85Zt0Ff7MDbq17n0eVeXaoM9Knt4Q=",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category: "VIDEOS",
|
||||
title: "Hands-on workshops and devleloper video content",
|
||||
description: "Watch and learn with our interactive video workshops. Get practical experience building real Bitcoin and Lightning applications.",
|
||||
icon: "pi pi-video",
|
||||
image: "https://newsroom.siliconslopes.com/content/images/2018/10/code.jpg",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: "DOCUMENTS",
|
||||
title: "In-depth Resources and Documentation",
|
||||
description: "Access our extensive library of documents, including guides, resources, and best practices for Bitcoin development.",
|
||||
icon: "pi pi-file",
|
||||
image: "https://img.freepik.com/free-photo/programming-background-with-person-working-with-codes-computer_23-2150010125.jpg",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
category: "COMMUNITY",
|
||||
title: "Join Our Community",
|
||||
title: "Join Our Community of learners, hackers, and plebs",
|
||||
description: "Connect with other developers, share your projects, and get support from our community of Bitcoin enthusiasts.",
|
||||
icon: "pi pi-users",
|
||||
image: "https://pikwizard.com/pw/medium/50238b1cad4ff412fdafc1325efa1c9f.jpg",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
id: 4,
|
||||
category: "LIGHTNING / NOSTR",
|
||||
title: "Lightning and Nostr integrated",
|
||||
title: "Lightning and Nostr integrated platform",
|
||||
description: "This platform is the first of its kind to integrate Lightning Network and Nostr protocols, allowing users to send and receive payments and interact with the Nostr network.",
|
||||
icon: "pi pi-bolt",
|
||||
image: "https://www.financemagnates.com/wp-content/uploads/2016/05/Bicoin-lightning.jpg",
|
||||
},
|
||||
]
|
||||
|
||||
// todo bigger ore simple CTA to get users into the content
|
||||
const InteractivePromotionalCarousel = () => {
|
||||
const [selectedPromotion, setSelectedPromotion] = useState(promotions[0])
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const windowWidth = useWindowWidth();
|
||||
const isMobileView = windowWidth <= 1360;
|
||||
const router = useRouter();
|
||||
const videoRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (videoRef.current && selectedPromotion.video) {
|
||||
videoRef.current.play();
|
||||
}
|
||||
}, [selectedPromotion]);
|
||||
|
||||
return (
|
||||
<div className={`flex ${isMobileView ? 'flex-col' : 'flex-row'} bg-gray-900 text-white m-4 mx-14 rounded-lg ${isMobileView ? 'h-auto' : 'h-[620px]'} ${isMobileView ? 'w-full mx-0 ml-0 mt-0' : null}`}>
|
||||
<div className={isMobileView ? 'w-full' : 'lg:w-2/3 relative'}>
|
||||
<Image
|
||||
src={returnImageProxy(selectedPromotion.image)}
|
||||
alt={selectedPromotion.title}
|
||||
width={800}
|
||||
height={600}
|
||||
className={`object-cover w-full ${isMobileView ? 'h-[300px]' : 'h-full'} rounded-lg opacity-75`}
|
||||
/>
|
||||
{selectedPromotion.video ? (
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={selectedPromotion.video}
|
||||
className={`object-cover w-full ${isMobileView ? 'h-[300px]' : 'h-full'} rounded-lg`}
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={returnImageProxy(selectedPromotion.image)}
|
||||
alt={selectedPromotion.title}
|
||||
width={800}
|
||||
height={600}
|
||||
className={`object-cover w-full ${isMobileView ? 'h-[300px]' : 'h-full'} rounded-lg opacity-75`}
|
||||
/>
|
||||
)}
|
||||
{isMobileView ? (
|
||||
<div className="p-6 space-y-2">
|
||||
<div className="uppercase text-sm font-bold text-[#f8f8ff]">{selectedPromotion.category}</div>
|
||||
@ -93,17 +94,13 @@ const InteractivePromotionalCarousel = () => {
|
||||
<GenericButton onClick={() => router.push('/content?tag=all')} severity="primary" icon={<i className="pi pi-eye pr-2" />} label="View all content" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
</div>
|
||||
);
|
||||
case "COURSES":
|
||||
case "CONTENT":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="View All Courses" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "VIDEOS":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="View All Videos" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "DOCUMENTS":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Documents" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
<>
|
||||
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="Courses" className="py-2 font-semibold" size="small" outlined />
|
||||
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="Videos" className="py-2 font-semibold" size="small" outlined />
|
||||
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="Documents" className="py-2 font-semibold" size="small" outlined />
|
||||
</>
|
||||
);
|
||||
case "COMMUNITY":
|
||||
return (
|
||||
@ -141,17 +138,13 @@ const InteractivePromotionalCarousel = () => {
|
||||
<GenericButton onClick={() => router.push('/content?tag=all')} severity="primary" icon={<i className="pi pi-eye pr-2" />} label="View all content" className="py-2 font-semibold" size="small" outlined />
|
||||
</>
|
||||
);
|
||||
case "COURSES":
|
||||
case "CONTENT":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="View All Courses" className="py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "VIDEOS":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="View All Videos" className="py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "DOCUMENTS":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Documents" className="py-2 font-semibold" size="small" outlined />
|
||||
<>
|
||||
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="Courses" className="py-2 font-semibold" size="small" outlined />
|
||||
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="Videos" className="py-2 font-semibold" size="small" outlined />
|
||||
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="Documents" className="py-2 font-semibold" size="small" outlined />
|
||||
</>
|
||||
);
|
||||
case "COMMUNITY":
|
||||
return (
|
||||
@ -177,8 +170,8 @@ const InteractivePromotionalCarousel = () => {
|
||||
{promotions.map((promo) => (
|
||||
<div
|
||||
key={promo.id}
|
||||
className={`flex-shrink-0 w-48 space-y-2 cursor-pointer transition-colors duration-200 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
|
||||
} p-3 rounded-lg shadow-lg`}
|
||||
className={`flex-shrink-0 w-64 space-y-4 cursor-pointer transition-colors duration-200 bg-gray-800 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
|
||||
} p-4 rounded-lg shadow-lg`}
|
||||
onClick={() => setSelectedPromotion(promo)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<i className={`${promo.icon} text-2xl text-[#f8f8ff]`}></i>
|
||||
@ -192,14 +185,14 @@ const InteractivePromotionalCarousel = () => {
|
||||
promotions.map((promo) => (
|
||||
<div
|
||||
key={promo.id}
|
||||
className={`space-y-2 cursor-pointer transition-colors duration-200 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
|
||||
} p-3 rounded-lg shadow-lg`}
|
||||
className={`space-y-8 cursor-pointer transition-colors duration-200 bg-gray-800 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
|
||||
} p-4 rounded-lg shadow-lg`}
|
||||
onClick={() => setSelectedPromotion(promo)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<i className={`${promo.icon} text-2xl text-[#f8f8ff]`}></i>
|
||||
<div className="text-sm font-bold text-[#f8f8ff]">{promo.category}</div>
|
||||
<div className="text-lg font-semibold text-[#f8f8ff]">{promo.category}</div>
|
||||
</div>
|
||||
<h4 className="text-white font-semibold">{promo.title}</h4>
|
||||
<h4 className="text-white text-lg">{promo.title}</h4>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
@ -103,7 +103,7 @@ export default function CourseDetailsNew({ processedEvent, paidCourse, lessons,
|
||||
if (paidCourse && !decryptionPerformed) {
|
||||
return (
|
||||
<CoursePaymentButton
|
||||
lnAddress={lnAddress}
|
||||
lnAddress={author?.lnAddress}
|
||||
amount={processedEvent.price}
|
||||
onSuccess={handlePaymentSuccess}
|
||||
onError={handlePaymentError}
|
||||
|
@ -21,8 +21,6 @@ const MDDisplay = dynamic(
|
||||
}
|
||||
);
|
||||
|
||||
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
|
||||
|
||||
const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => {
|
||||
const [zapAmount, setZapAmount] = useState(0);
|
||||
const router = useRouter();
|
||||
@ -96,7 +94,7 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,
|
||||
</p>
|
||||
<div className="flex flex-row items-center justify-center w-full mt-4">
|
||||
<ResourcePaymentButton
|
||||
lnAddress={lnAddress}
|
||||
lnAddress={author?.lightningAddress}
|
||||
amount={price}
|
||||
onSuccess={handlePaymentSuccess}
|
||||
onError={handlePaymentError}
|
||||
|
@ -21,8 +21,6 @@ const MDDisplay = dynamic(
|
||||
}
|
||||
);
|
||||
|
||||
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
|
||||
|
||||
const VideoDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => {
|
||||
const [zapAmount, setZapAmount] = useState(0);
|
||||
const router = useRouter();
|
||||
@ -99,7 +97,7 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au
|
||||
</p>
|
||||
<div className="flex flex-row items-center justify-center w-full mt-4 z-10">
|
||||
<ResourcePaymentButton
|
||||
lnAddress={lnAddress}
|
||||
lnAddress={author?.lnAddress}
|
||||
amount={price}
|
||||
onSuccess={handlePaymentSuccess}
|
||||
onError={handlePaymentError}
|
||||
|
@ -23,7 +23,6 @@ import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
|
||||
// todo make the summarry save in a formatted way so we can keep this spaces and line breaks
|
||||
const DocumentForm = ({ draft = null, isPublished = false }) => {
|
||||
const [title, setTitle] = useState(draft?.title || '');
|
||||
const [summary, setSummary] = useState(draft?.summary || '');
|
||||
|
@ -41,7 +41,7 @@ const allTasks = [
|
||||
const UserProgress = () => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [currentTier, setCurrentTier] = useState('Pleb');
|
||||
const [expanded, setExpanded] = useState(null);
|
||||
const [expandedItems, setExpandedItems] = useState({});
|
||||
const [completedCourses, setCompletedCourses] = useState([]);
|
||||
const [tasks, setTasks] = useState([]);
|
||||
|
||||
@ -91,6 +91,13 @@ const UserProgress = () => {
|
||||
fetchProgress();
|
||||
}, []);
|
||||
|
||||
const handleAccordionChange = (index, isExpanded) => {
|
||||
setExpandedItems(prev => ({
|
||||
...prev,
|
||||
[index]: isExpanded
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-gray-800 rounded-3xl p-6 w-[500px] max-mob:w-full max-tab:w-full mx-auto my-8">
|
||||
<h1 className="text-3xl font-bold text-white mb-2">Dev Journey (Coming Soon)</h1>
|
||||
@ -115,7 +122,11 @@ const UserProgress = () => {
|
||||
{tasks.map((task, index) => (
|
||||
<li key={index}>
|
||||
{task.subTasks ? (
|
||||
<Accordion className="border-none" onTabOpen={(e) => setExpanded(true)} onTabClose={(e) => setExpanded(false)}>
|
||||
<Accordion
|
||||
className="border-none"
|
||||
onTabChange={(e) => handleAccordionChange(index, e.index === 0)}
|
||||
activeIndex={expandedItems[index] ? 0 : null}
|
||||
>
|
||||
<AccordionTab
|
||||
pt={{
|
||||
root: { className: 'border-none p-0' },
|
||||
@ -129,10 +140,12 @@ const UserProgress = () => {
|
||||
<div className="bg-gray-800 flex items-center justify-between w-full font-normal">
|
||||
<div className="flex items-center">
|
||||
<div className="w-6 h-6 bg-gray-700 rounded-full flex items-center justify-center mr-3">
|
||||
<i className="pi pi-info-circle text-white text-lg"></i>
|
||||
<i className="pi pi-info-circle text-white text-2xl"></i>
|
||||
</div>
|
||||
<div className="flex items-center justify-between w-[160px] max-mob:w-[100px]">
|
||||
<span className="text-lg text-gray-400">{task.status}</span>
|
||||
<i className={`pi pi-chevron-down text-gray-400 ml-2 transition-transform duration-300 ${expandedItems[index] ? 'rotate-180' : ''}`} />
|
||||
</div>
|
||||
<span className="text-lg text-gray-400">{task.status}</span>
|
||||
<i className={`pi pi-chevron-down text-gray-400 ml-2 transition-transform duration-300 ${expanded ? 'rotate-180' : ''}`} />
|
||||
</div>
|
||||
<span className="bg-blue-500 text-white text-xs px-2 py-1 rounded-full w-20 text-center">
|
||||
{task.tier}
|
||||
|
@ -98,28 +98,30 @@ const AboutPage = () => {
|
||||
severity="secondary"
|
||||
outlined
|
||||
icon="pi pi-github"
|
||||
label="GitHub"
|
||||
tooltip="Github"
|
||||
className="text-gray-300"
|
||||
onClick={() => window.open('https://github.com/pleb-devs', '_blank')}
|
||||
/>
|
||||
<GenericButton
|
||||
severity="info"
|
||||
outlined
|
||||
icon="pi pi-twitter"
|
||||
label="X.com"
|
||||
tooltip="X"
|
||||
onClick={() => window.open('https://x.com/pleb_devs', '_blank')}
|
||||
/>
|
||||
<GenericButton
|
||||
severity="help"
|
||||
outlined
|
||||
icon={<Image src={NostrIcon} alt="Nostr" width={20} height={20} className="mr-2" />}
|
||||
label="Nostr"
|
||||
icon={<Image src={NostrIcon} alt="Nostr" width={20} height={20} className="mr-0" />}
|
||||
tooltip="Nostr"
|
||||
onClick={() => window.open('https://nostr.com/plebdevs@plebdevs.com', '_blank')}
|
||||
/>
|
||||
<GenericButton
|
||||
severity="warning"
|
||||
className="text-yellow-400"
|
||||
outlined
|
||||
icon="pi pi-bolt"
|
||||
label="Donate"
|
||||
tooltip="Donate"
|
||||
onClick={() => copyToClipboard("austin@bitcoinpleb.dev")}
|
||||
/>
|
||||
</div>
|
||||
|
@ -5,7 +5,7 @@ import VideosCarousel from '@/components/content/carousels/VideosCarousel';
|
||||
import DocumentsCarousel from '@/components/content/carousels/DocumentsCarousel';
|
||||
import InteractivePromotionalCarousel from '@/components/content/carousels/InteractivePromotionalCarousel';
|
||||
|
||||
// todo: make paid course videos and documents not appear in carousels
|
||||
// todo: make paid course video and document lessons not appear in carousels
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
|
@ -34,6 +34,12 @@ export const findKind0Fields = async (kind0) => {
|
||||
fields.pubkey = pubkey;
|
||||
}
|
||||
|
||||
const lud16 = findTruthyPropertyValue(kind0, ['lud16', 'lightning', 'lnurl', 'lnurlp', 'lnurlw']);
|
||||
|
||||
if (lud16) {
|
||||
fields.lightningAddress = lud16;
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user