diff --git a/src/components/profile/subscription/CalendlyEmbed.js b/src/components/profile/subscription/CalendlyEmbed.js new file mode 100644 index 0000000..6e0dce3 --- /dev/null +++ b/src/components/profile/subscription/CalendlyEmbed.js @@ -0,0 +1,42 @@ +import React, { useState, useEffect } from 'react'; +import { Dialog } from 'primereact/dialog'; +import { Button } from 'primereact/button'; + +const CalendlyEmbed = ({ visible, onHide }) => { + useEffect(() => { + if (visible) { + const script = document.createElement('script'); + script.src = 'https://assets.calendly.com/assets/external/widget.js'; + script.async = true; + document.body.appendChild(script); + + return () => { + document.body.removeChild(script); + }; + } + }, [visible]); + + const dialogFooter = ( +
+
+ ); + + return ( + +
+
+ ); +}; + +export default CalendlyEmbed; \ No newline at end of file diff --git a/src/components/profile/subscription/CancelSubscription.js b/src/components/profile/subscription/CancelSubscription.js new file mode 100644 index 0000000..d448fa3 --- /dev/null +++ b/src/components/profile/subscription/CancelSubscription.js @@ -0,0 +1,43 @@ +import React, {useState, useEffect} from 'react'; +import {Dialog} from 'primereact/dialog'; +import axios from 'axios'; +import { useSession } from 'next-auth/react'; +import { useToast } from '@/hooks/useToast'; +import { ProgressSpinner } from 'primereact/progressspinner'; +import GenericButton from '@/components/buttons/GenericButton'; + +const CancelSubscription = ({visible, onHide}) => { + const { data: session, update } = useSession(); + const [isProcessing, setIsProcessing] = useState(false); + const { showToast } = useToast(); + const handleCancelSubscription = async () => { + setIsProcessing(true); + try { + const response = await axios.put('/api/users/subscription', { + userId: session.user.id, + isSubscribed: false, + nwc: null + }); + if (response.status === 200) { + showToast('success', 'Subscription canceled', 'Subscription canceled successfully'); + update(); + onHide(); + } + } catch (error) { + console.error('Error canceling subscription:', error); + showToast('error', 'Error canceling subscription', error.message); + setIsProcessing(false); + } + }; + + return ( + +

Are you sure you want to cancel your subscription?

+
+ {isProcessing ? : } +
+
+ ); +}; + +export default CancelSubscription; \ No newline at end of file diff --git a/src/components/profile/subscription/RenewSubscription.js b/src/components/profile/subscription/RenewSubscription.js new file mode 100644 index 0000000..bc22650 --- /dev/null +++ b/src/components/profile/subscription/RenewSubscription.js @@ -0,0 +1,79 @@ +import React, { useState } from 'react'; +import { Dialog } from 'primereact/dialog'; +import { Card } from 'primereact/card'; +import { ProgressSpinner } from 'primereact/progressspinner'; +import SubscriptionPaymentButtons from '@/components/bitcoinConnect/SubscriptionPaymentButton'; +import { useSession } from 'next-auth/react'; +import { useToast } from '@/hooks/useToast'; +import axios from 'axios'; + +const RenewSubscription = ({ visible, onHide, subscribedUntil }) => { + const [isProcessing, setIsProcessing] = useState(false); + const { data: session, update } = useSession(); + const { showToast } = useToast(); + + const handleSubscriptionSuccess = async (response) => { + setIsProcessing(true); + try { + const apiResponse = await axios.put('/api/users/subscription', { + userId: session.user.id, + isSubscribed: true, + }); + if (apiResponse.data) { + await update(); + showToast('success', 'Subscription Renewed', 'Your subscription has been renewed successfully.'); + onHide(); + } else { + throw new Error('Failed to update subscription status'); + } + } catch (error) { + console.error('Subscription renewal error:', error); + showToast('error', 'Subscription Renewal Failed', `Error: ${error.message}`); + } finally { + setIsProcessing(false); + } + }; + + const handleSubscriptionError = (error) => { + console.error('Subscription error:', error); + showToast('error', 'Subscription Renewal Failed', `An error occurred: ${error.message}`); + setIsProcessing(false); + }; + + const formatDate = (date) => { + if (!date) return 'N/A'; + return new Date(date).toLocaleDateString(); + }; + + return ( + + {isProcessing ? ( +
+
+ Processing renewal... +
+ ) : ( + +
+

Renew Your Subscription

+

Your current subscription is valid until {formatDate(subscribedUntil)}

+

Renew now to extend your access to premium benefits!

+
+ +
+ )} +
+ ); +}; + +export default RenewSubscription; \ No newline at end of file diff --git a/src/components/profile/subscription/SubscribeModal.js b/src/components/profile/subscription/SubscribeModal.js index 8aa68b8..377781a 100644 --- a/src/components/profile/subscription/SubscribeModal.js +++ b/src/components/profile/subscription/SubscribeModal.js @@ -11,6 +11,11 @@ import { Badge } from 'primereact/badge'; import GenericButton from '@/components/buttons/GenericButton'; import { Menu } from "primereact/menu"; import { Message } from "primereact/message"; +import CancelSubscription from '@/components/profile/subscription/CancelSubscription'; +import CalendlyEmbed from '@/components/profile/subscription/CalendlyEmbed'; +import NostrIcon from '../../../../public/images/nostr.png'; +import Image from 'next/image'; +import RenewSubscription from '@/components/profile/subscription/RenewSubscription'; const SubscribeModal = ({ user }) => { const { data: session, update } = useSession(); @@ -21,7 +26,10 @@ const SubscribeModal = ({ user }) => { const [subscribed, setSubscribed] = useState(false); const [subscribedUntil, setSubscribedUntil] = useState(null); const [subscriptionExpiredAt, setSubscriptionExpiredAt] = useState(null); + const [calendlyVisible, setCalendlyVisible] = useState(false); const menu = useRef(null); + const [cancelSubscriptionVisible, setCancelSubscriptionVisible] = useState(false); + const [renewSubscriptionVisible, setRenewSubscriptionVisible] = useState(false); useEffect(() => { if (user && user.role) { @@ -88,21 +96,21 @@ const SubscribeModal = ({ user }) => { label: "Renew Subscription", icon: "pi pi-bolt", command: () => { - // Add your renew functionality here + setRenewSubscriptionVisible(true); }, }, { label: "Schedule 1:1", icon: "pi pi-calendar", command: () => { - // Add your schedule functionality here + setCalendlyVisible(true); }, }, { label: "Cancel Subscription", icon: "pi pi-trash", command: () => { - // Add your cancel functionality here + setCancelSubscriptionVisible(true); }, }, ]; @@ -110,21 +118,23 @@ const SubscribeModal = ({ user }) => { const subscriptionCardTitle = (
Plebdevs Subscription - menu.current.toggle(e)} - > + > + )}
); return ( <> - + {subscribed && (
-

Thank you for your support 🎉

+

Thank you for your support 🎉

Pay-as-you-go subscription will renew on {subscribedUntil.toLocaleDateString()}

)} @@ -133,7 +143,7 @@ const SubscribeModal = ({ user }) => { setVisible(true)} /> @@ -143,7 +153,7 @@ const SubscribeModal = ({ user }) => { setVisible(true)} /> @@ -166,26 +176,26 @@ const SubscribeModal = ({ user }) => {

Unlock Premium Benefits

Subscribe now and elevate your development journey!

-
+
- - Access ALL current and future content + + Access ALL current and future PlebDevs content
- - Join PlebLab Bitcoin Hackerspace Slack + + Personal mentorship & guidance and access to exclusive 1:1 booking calendar
- - Exclusive 1:1 booking calendar + + Claim your own personal plebdevs.com Lightning Address
- - Personal mentorship & guidance + Nostr + Claim your own personal plebdevs.com Nostr NIP-05 identity
- + I WILL MAKE SURE YOU WIN HARD AND LEVEL UP AS A DEV!
{ )} + {calendlyVisible && ( + setCalendlyVisible(false)} + /> + )} + setCancelSubscriptionVisible(false)} + /> + setRenewSubscriptionVisible(false)} + subscribedUntil={subscribedUntil} + /> ); };