Update subscribe and about page, a bunch of styling and mobile styling fixes

This commit is contained in:
austinkelsay 2024-09-27 16:30:04 -05:00
parent bcf6c9bf9a
commit ace772f32c
9 changed files with 162 additions and 135 deletions

View File

@ -34,23 +34,6 @@ const UserProfile = () => {
}
}, [session]);
const menuItems = [
{
label: "Edit",
icon: "pi pi-pencil",
command: () => {
showToast("warn", "Alert", "This feature is not yet implemented");
},
},
{
label: "Delete",
icon: "pi pi-trash",
command: () => {
showToast("warn", "Alert", "This feature is not yet implemented");
},
},
];
const header = (
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
<span className="text-xl text-900 font-bold text-[#f8f8ff]">Progress</span>
@ -80,11 +63,6 @@ const UserProfile = () => {
height={100}
className="rounded-full my-4"
/>
<i
className="pi pi-ellipsis-h absolute right-24 text-2xl my-4 cursor-pointer hover:opacity-75"
onClick={(e) => menu.current.toggle(e)}
></i>
<Menu model={menuItems} popup ref={menu} />
</div>
<h1 className="text-center text-2xl my-2">

View File

@ -191,7 +191,7 @@ const UserSettings = () => {
<span className="font-bold">NIP-05:</span> {user.nip05.name}@plebdevs.com
</h3>
)}
<div className="flex flex-col w-1/2 mx-auto justify-between items-center">
<div className="flex flex-col w-1/2 mx-auto justify-between items-center max-mob:w-full max-tab:w-full">
<h2 className="text-xl my-2 max-mob:text-base max-tab:text-base">Connect Your Lightning Wallet</h2>
<BitcoinConnectButton />
</div>

View File

@ -6,7 +6,7 @@ import { useSession } from 'next-auth/react';
const allTasks = [
{ status: 'Create Account', completed: true, tier: 'Pleb', courseId: null },
{
status: 'PlebDevs Starter (Coming Soon)',
status: 'PlebDevs Starter',
completed: false,
tier: 'New Dev',
courseId: null,
@ -16,8 +16,26 @@ const allTasks = [
{ status: 'Push Commit', completed: false }
]
},
{ status: 'PlebDevs Course 1', completed: false, tier: 'Junior Dev', courseId: 'd20e2e9b-5123-4a91-b27f-d75ea1d5811e' },
{ status: 'PlebDevs Course 2', completed: false, tier: 'Plebdev', courseId: 'aa3b1641-ad2b-4ef4-9f0f-38951ae307b7' },
{
status: 'PlebDevs Course 1',
completed: false,
tier: 'Junior Dev',
courseId: 'd20e2e9b-5123-4a91-b27f-d75ea1d5811e',
subTasks: [
{ status: 'Complete the course', completed: false },
{ status: 'Submit Link to completed project', completed: false },
]
},
{
status: 'PlebDevs Course 2',
completed: false,
tier: 'Plebdev',
courseId: 'aa3b1641-ad2b-4ef4-9f0f-38951ae307b7',
subTasks: [
{status: 'Complete the course', completed: false},
{ status: 'Submit Link to completed project', completed: false },
]
},
];
const UserProgress = () => {
@ -74,7 +92,7 @@ const UserProgress = () => {
}, []);
return (
<div className="bg-gray-800 rounded-3xl p-6 w-[500px] mx-auto my-8">
<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>
<p className="text-gray-400 mb-4">Track your progress from Pleb to Plebdev</p>
@ -121,8 +139,9 @@ const UserProgress = () => {
</span>
</div>
}
>
<ul className="space-y-2 mt-2 ml-9">
>
<ul className="space-y-2 mt-2 ml-9 max-mob:mt-0 max-tab:mt-0">
<span className="text-gray-400 text-xs">(Coming Soon)</span>
{task.subTasks.map((subTask, subIndex) => (
<li key={subIndex} className="flex items-center">
{subTask.completed ? (

View File

@ -1,8 +1,10 @@
import React, { useState, useEffect } from 'react';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import useWindowWidth from '@/hooks/useWindowWidth';
const CalendlyEmbed = ({ visible, onHide }) => {
const windowWidth = useWindowWidth();
useEffect(() => {
if (visible) {
const script = document.createElement('script');
@ -26,7 +28,7 @@ const CalendlyEmbed = ({ visible, onHide }) => {
<Dialog
header="Schedule a Meeting"
visible={visible}
style={{ width: '50vw' }}
style={{ width: windowWidth < 768 ? '100vw' : '50vw' }}
footer={dialogFooter}
onHide={onHide}
>

View File

@ -7,6 +7,7 @@ import { InputText } from 'primereact/inputtext';
import { ProgressSpinner } from 'primereact/progressspinner';
import { InputNumber } from 'primereact/inputnumber';
import GenericButton from '@/components/buttons/GenericButton';
import useWindowWidth from '@/hooks/useWindowWidth';
const LightningAddressForm = ({ visible, onHide }) => {
const [isProcessing, setIsProcessing] = useState(false);
@ -20,6 +21,7 @@ const LightningAddressForm = ({ visible, onHide }) => {
const [lndHost, setLndHost] = useState('');
const [lndPort, setLndPort] = useState('8080');
const windowWidth = useWindowWidth();
const { data: session, update } = useSession();
const { showToast } = useToast();
@ -86,7 +88,7 @@ const LightningAddressForm = ({ visible, onHide }) => {
};
return (
<Dialog header="Lightning Address" visible={visible} onHide={onHide}>
<Dialog header="Lightning Address" visible={visible} onHide={onHide} style={{ width: windowWidth < 768 ? '100vw' : '60vw' }}>
{existingLightningAddress ? (
<p>Update your Lightning Address details</p>
) : (

View File

@ -6,6 +6,7 @@ import { useToast } from '@/hooks/useToast';
import { InputText } from 'primereact/inputtext';
import { ProgressSpinner } from 'primereact/progressspinner';
import GenericButton from '@/components/buttons/GenericButton';
import useWindowWidth from '@/hooks/useWindowWidth';
const Nip05Form = ({ visible, onHide }) => {
const [isProcessing, setIsProcessing] = useState(false);
@ -13,6 +14,7 @@ const Nip05Form = ({ visible, onHide }) => {
const [pubkey, setPubkey] = useState('');
const [name, setName] = useState('');
const windowWidth = useWindowWidth();
const { data: session, update } = useSession();
const { showToast } = useToast();
@ -74,7 +76,7 @@ const Nip05Form = ({ visible, onHide }) => {
};
return (
<Dialog header="NIP-05" visible={visible} onHide={onHide}>
<Dialog header="NIP-05" visible={visible} onHide={onHide} style={{ width: windowWidth < 768 ? '100vw' : '60vw' }}>
{existingNip05 ? (
<p>Update your Pubkey and Name</p>
) : (

View File

@ -23,6 +23,7 @@ const SubscribeModal = ({ user }) => {
const { data: session, update } = useSession();
const { showToast } = useToast();
const router = useRouter();
const menu = useRef(null);
const [isProcessing, setIsProcessing] = useState(false);
const [visible, setVisible] = useState(false);
const [subscribed, setSubscribed] = useState(false);
@ -31,7 +32,6 @@ const SubscribeModal = ({ user }) => {
const [calendlyVisible, setCalendlyVisible] = useState(false);
const [lightningAddressVisible, setLightningAddressVisible] = useState(false);
const [nip05Visible, setNip05Visible] = useState(false);
const menu = useRef(null);
const [cancelSubscriptionVisible, setCancelSubscriptionVisible] = useState(false);
const [renewSubscriptionVisible, setRenewSubscriptionVisible] = useState(false);

View File

@ -219,8 +219,8 @@ const UserSubscription = () => {
<div className="flex flex-col">
<div className="flex flex-col gap-4">
<GenericButton severity="info" outlined className="w-fit text-start" label="Schedule 1:1" icon="pi pi-calendar" onClick={() => setCalendlyVisible(true)} />
<GenericButton severity="help" outlined className="w-fit text-start" label="Update Nostr NIP-05" icon="pi pi-at" onClick={() => setNip05Visible(true)} />
<GenericButton severity="warning" outlined className="w-fit text-start" label="Update Lightning Address" icon={<i style={{ color: "orange" }} className="pi pi-bolt mr-2"></i>} onClick={() => setLightningAddressVisible(true)} />
<GenericButton severity="help" outlined className="w-fit text-start" label={user?.nip05 ? "Update Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05"} icon="pi pi-at" onClick={() => setNip05Visible(true)} />
<GenericButton severity="warning" outlined className="w-fit text-start" label={user?.lightningAddress ? "Update Lightning Address" : "Claim PlebDevs Lightning Address"} icon={<i style={{ color: "orange" }} className="pi pi-bolt mr-2"></i>} onClick={() => setLightningAddressVisible(true)} />
</div>
</div>
)}
@ -228,7 +228,7 @@ const UserSubscription = () => {
<Card title="Manage Subscription" className="mb-4">
<div className='flex flex-col gap-4'>
<GenericButton outlined className="w-fit" label="Renew Subscription" icon="pi pi-sync" onClick={() => setRenewSubscriptionVisible(true)} />
<GenericButton outlined className="w-fit" label="Cancel Subscription" icon="pi pi-trash" onClick={() => setCancelSubscriptionVisible(true)} />
<GenericButton severity="danger" outlined className="w-fit" label="Cancel Subscription" icon="pi pi-trash" onClick={() => setCancelSubscriptionVisible(true)} />
</div>
</Card>
</>
@ -238,12 +238,24 @@ const UserSubscription = () => {
<div className="flex flex-col gap-4">
<div>
<h3 className="text-lg font-semibold">How does the subscription work?</h3>
<p>Our subscription provides monthly access to all PlebDevs features. You can choose between a one-time payment or a recurring subscription.</p>
<p>Think of the subscriptions as a paetreon type model. You pay a monthly fee and in return you get access to premium features and all of the paid content. You can cancel at any time.</p>
</div>
<div>
<h3 className="text-lg font-semibold">How do I Subscribe? (Pay as you go)</h3>
<p>The pay as you go subscription is a one-time payment that gives you access to all of the premium features for one month. You will need to manually renew your subscription every month.</p>
</div>
<div>
<h3 className="text-lg font-semibold">How do I Subscribe? (Recurring)</h3>
<p>The recurring subscription option allows you to submit a Nostr Wallet Connect URI that will be used to automatically send the subscription fee every month. You can cancel at any time.</p>
</div>
<div>
<h3 className="text-lg font-semibold">Can I cancel my subscription?</h3>
<p>Yes, you can cancel your subscription at any time. Your access will remain active until the end of the current billing period.</p>
</div>
<div>
<h3 className="text-lg font-semibold">What happens if I don&apos;t renew my subscription?</h3>
<p>If you don&apos;t renew your subscription, your access to 1:1 calendar and paid content will be removed. However, you will still have access to your plebdevs Lightning Address, NIP-05, and any content that you paid for.</p>
</div>
{/* Add more FAQ items as needed */}
</div>
</Card>

View File

@ -1,119 +1,131 @@
import React, { useState } from 'react';
import React from 'react';
import Image from 'next/image';
import NostrIcon from '../../public/images/nostr.png';
import { Tooltip } from 'primereact/tooltip';
import { useToast } from "@/hooks/useToast"
import { Card } from 'primereact/card';
import { Message } from 'primereact/message';
import { useToast } from "@/hooks/useToast";
import useWindowWidth from "@/hooks/useWindowWidth";
import GenericButton from '@/components/buttons/GenericButton';
const AboutPage = () => {
const {showToast} = useToast()
const { showToast } = useToast();
const windowWidth = useWindowWidth();
const isMobileView = windowWidth <= 768;
const copyToClipboard = async (text) => {
try {
await navigator.clipboard.writeText(text);
showToast("success", "Copied", "Copied Lightning Address to clipboard")
if (window && window?.webln && window?.webln?.lnurl) {
await window.webln.enable();
const result = await window.webln.lnurl("austin@bitcoinpleb.dev");
if (result && result?.preimage) {
showToast("success", "Copied", "Copied Lightning Address to clipboard")
await navigator.clipboard.writeText(text);
showToast("success", "Copied", "Copied Lightning Address to clipboard");
if (window && window?.webln && window?.webln?.lnurl) {
await window.webln.enable();
const result = await window.webln.lnurl("austin@bitcoinpleb.dev");
if (result && result?.preimage) {
showToast("success", "Payment Sent", "Thank you for your donation!");
}
}
}
} catch (err) {
console.error('Failed to copy:', err);
console.error('Failed to copy:', err);
}
};
};
return (
<div className="max-w-4xl mx-auto py-8 px-4">
<h1 className="text-3xl font-bold mb-6">About PlebDevs</h1>
<div className="bg-gray-700 rounded-lg p-6 mb-8">
<p className="text-lg flex items-center">
<i className="pi pi-info-circle text-blue-500 mr-3 text-2xl"></i>
PlebDevs is a custom-built education platform designed to help new and aspiring developers, with a special focus on Bitcoin Lightning and Nostr technologies.
</p>
{/* <p className='text-lg font-semibold px-9 mt-4'> */}
<p className='font-normal text-lg px-9 mt-4 mb-2'>The pitch is simple:</p>
<ul className='list-disc list-inside ml-16 space-y-2'>
<li>Learn how to code 💻</li>
<li>Build Bitcoin / Lightning / Nostr apps </li>
<li>Become a developer 🚀</li>
</ul>
{/* </p> */}
</div>
<div className="p-4 max-w-4xl mx-auto">
{windowWidth < 768 && (
<h1 className="text-3xl font-bold mb-6">About PlebDevs</h1>
)}
<div className="space-y-8">
<h2 className="text-2xl font-bold flex items-center">
<i className="pi pi-star text-yellow-500 mr-3 text-2xl"></i>
Key Features
</h2>
{/* Feature sections */}
<FeatureSection
icon="pi-cloud"
title="Content Distribution"
description="All educational content is published to Nostr and actively pulled from Nostr relays, ensuring decentralized and up-to-date information."
/>
<FeatureSection
icon="pi-file-edit"
title="Content Types"
description={
<ul className="list-disc list-inside ml-6 space-y-2">
<li><span className="font-bold">Documents:</span> Markdown documents posted as NIP-23 long-form events on Nostr.</li>
<li><span className="font-bold">Videos:</span> Enhanced markdown files with rich media support, including embedded videos, also saved as NIP-23 events.</li>
<li><span className="font-bold">Courses:</span> Nostr lists that combine multiple documents and videos into a structured learning path.</li>
</ul>
}
/>
</div>
<div className="mt-12 bg-gray-700 rounded-lg p-6">
<p className="italic text-lg flex items-center">
<i className="pi pi-flag text-blue-500 mr-3 text-2xl"></i>
PlebDevs aims to provide a comprehensive, decentralized learning experience for aspiring developers, with a strong emphasis on emerging technologies in the Bitcoin ecosystem.
</p>
</div>
<div className="mt-12 bg-gray-700 rounded-lg p-6">
<div className="flex items-center justify-center space-x-16">
<Tooltip target=".pi-github" content={isMobileView ? null : "GitHub"} position="bottom" />
<a href="https://github.com/pleb-devs" target="_blank" rel="noopener noreferrer">
<i className="pi pi-github text-white text-5xl"></i>
</a>
<Tooltip target=".pi-twitter" content={isMobileView ? null : "X.com"} position="bottom" />
<a href="https://x.com/pleb_devs" target="_blank" rel="noopener noreferrer">
<i className="pi pi-twitter text-black text-5xl"></i>
</a>
<Tooltip target=".nostr-icon" content={isMobileView ? null : "Nostr"} position="bottom" />
<a href="https://nostr.com/plebdevs@plebdevs.com" target="_blank" rel="noopener noreferrer">
<Image src={NostrIcon} alt="Nostr" width={44} height={44} className='nostr-icon' />
</a>
<Tooltip target=".pi-bolt" content={isMobileView ? null : "Donate"} position="bottom" />
<p onClick={() => copyToClipboard("austin@bitcoinpleb.dev")} className='cursor-pointer'>
<i className="pi pi-bolt text-yellow-500 text-5xl"></i>
</p>
<Card className="mb-4">
<div className='flex flex-row gap-4'>
<Message pt={{
icon: {
className: 'hidden'
}
}} severity="info" text="PlebDevs is a fully Lightning and Nostr integrated education, content, and community platform designed to help new and aspiring developers, with a focus on Bitcoin / Lightning / Nostr technologies." />
<Message pt={{
icon: {
className: 'hidden'
}
}} severity="success" text="PlebDevs offers a personal yet distributed learning experience, combining videos, courses, documents, and community channels through Nostr, monetizing with Lightning, and integrating them into a single platform" />
</div>
</div>
<div className="mt-4">
<h3 className='font-bold mb-2'>The pitch is simple:</h3>
<ul className='list-disc list-inside ml-6 space-y-2'>
<li className='text-lg'>Learn how to code 💻</li>
<li className='text-lg'>Build Bitcoin / Lightning / Nostr apps </li>
<li className='text-lg'>Become a developer 🚀</li>
</ul>
</div>
</Card>
<Card title="Key Features" className="mb-4">
<div className="flex flex-col gap-4">
<div className="flex flex-col items-start justify-center">
<div className='flex flex-row items-start justify-center'>
<i className="pi pi-cloud text-2xl text-primary mr-2 text-blue-400"></i>
<h3 className='text-lg font-semibold'>Content Distribution:</h3>
</div>
<p className='text-lg'>All educational content is published to Nostr and actively pulled from Nostr relays, ensuring distributed and up-to-date information.</p>
</div>
<div className="flex items-start">
<i className="pi pi-file-edit text-2xl text-primary mr-2 text-green-400 mt-1"></i>
<div>
<h3 className="text-lg font-semibold">Content Types:</h3>
<p className='text-lg'>high signal, Bitcoin, Lightning, Nostr educational content.</p>
<ul className="list-disc list-inside ml-2 mt-2 space-y-2">
<li><span className="text-lg font-semibold">Documents:</span> Markdown documents posted as NIP-23 long-form events on Nostr.</li>
<li><span className="text-lg font-semibold">Videos:</span> Enhanced markdown files with rich media support, including embedded videos, also saved as NIP-23 events.</li>
<li><span className="text-lg font-semibold">Courses:</span> Nostr lists that combine multiple documents and videos into a structured learning path.</li>
</ul>
</div>
</div>
<div className="flex items-start">
<i className="pi pi-users text-2xl text-primary mr-2 text-purple-400 mt-1"></i>
<div>
<h3 className="text-lg font-semibold">Community:</h3>
<p className='text-lg'>All of the current PlebDevs Community channels.</p>
<ul className="list-disc list-inside ml-2 mt-2 space-y-2">
<li><span className="text-lg font-semibold">Nostr:</span> Public plebdevs nostr chat</li>
<li><span className="text-lg font-semibold">Discord:</span> PlebDevs Discord server</li>
<li><span className="text-lg font-semibold">StackerNews:</span> StackerNews ~devs territory</li>
</ul>
</div>
</div>
</div>
</Card>
<Card title="Connect with Us" className="mb-4">
<div className="flex flex-wrap gap-4 justify-center">
<GenericButton
severity="secondary"
outlined
icon="pi pi-github"
label="GitHub"
onClick={() => window.open('https://github.com/pleb-devs', '_blank')}
/>
<GenericButton
severity="info"
outlined
icon="pi pi-twitter"
label="X.com"
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"
onClick={() => window.open('https://nostr.com/plebdevs@plebdevs.com', '_blank')}
/>
<GenericButton
severity="warning"
outlined
icon="pi pi-bolt"
label="Donate"
onClick={() => copyToClipboard("austin@bitcoinpleb.dev")}
/>
</div>
</Card>
</div>
);
};
const FeatureSection = ({ icon, title, description }) => (
<div className="bg-gray-700 shadow-md rounded-lg p-6">
<h3 className="text-xl font-bold mb-4 flex items-center">
<i className={`pi ${icon} text-blue-500 mr-3 text-2xl`}></i>
{title}
</h3>
{typeof description === 'string' ? (
<p>{description}</p>
) : (
description
)}
</div>
);
export default AboutPage;