diff --git a/src/components/bitcoinConnect/SubscriptionPaymentButton.js b/src/components/bitcoinConnect/SubscriptionPaymentButton.js index 701f2b1..b3927ae 100644 --- a/src/components/bitcoinConnect/SubscriptionPaymentButton.js +++ b/src/components/bitcoinConnect/SubscriptionPaymentButton.js @@ -18,7 +18,7 @@ const PaymentModal = dynamic( { ssr: false } ); -const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptionSuccess, setIsProcessing }) => { +const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptionSuccess, setIsProcessing, oneTime = false, recurring = false }) => { const [invoice, setInvoice] = useState(null); const [showRecurringOptions, setShowRecurringOptions] = useState(false); const [nwcInput, setNwcInput] = useState(''); @@ -207,31 +207,35 @@ const SubscriptionPaymentButtons = ({ onSuccess, onError, onRecurringSubscriptio <> {!invoice && (
-
)} {showRecurringOptions && ( diff --git a/src/components/feeds/MessageInput.js b/src/components/feeds/MessageInput.js index 0b725f3..b7af0e7 100644 --- a/src/components/feeds/MessageInput.js +++ b/src/components/feeds/MessageInput.js @@ -6,7 +6,7 @@ import { useNDKContext } from "@/context/NDKContext"; import { NDKEvent } from "@nostr-dev-kit/ndk"; import { useToast } from '@/hooks/useToast'; -const MessageInput = ({ collapsed, onToggle, onMessageSent }) => { +const MessageInput = ({ onMessageSent }) => { const [message, setMessage] = useState(''); const { ndk, addSigner } = useNDKContext(); const { showToast } = useToast(); @@ -34,7 +34,7 @@ const MessageInput = ({ collapsed, onToggle, onMessageSent }) => { }; return ( - { setMessage(e.target.value)} - rows={5} - cols={30} + rows={2} + cols={10} autoResize placeholder="Type your message here..." className="w-full" /> -
-
+ +
+
); diff --git a/src/components/navbar/user/UserAvatar.js b/src/components/navbar/user/UserAvatar.js index 014241c..ff49abd 100644 --- a/src/components/navbar/user/UserAvatar.js +++ b/src/components/navbar/user/UserAvatar.js @@ -5,7 +5,7 @@ import { useImageProxy } from '@/hooks/useImageProxy'; import { Button } from 'primereact/button'; import { Menu } from 'primereact/menu'; import useWindowWidth from '@/hooks/useWindowWidth'; -import {useSession, signOut} from 'next-auth/react'; +import { useSession, signOut } from 'next-auth/react'; import { Dialog } from 'primereact/dialog'; import 'primereact/resources/primereact.min.css'; import 'primeicons/primeicons.css'; @@ -55,7 +55,7 @@ const UserAvatar = () => { { label: 'Profile', icon: 'pi pi-user', - command: () => router.push('/profile') + command: () => router.push('/profile?tab=profile') }, { label: 'Create', @@ -87,22 +87,62 @@ const UserAvatar = () => { } else { userAvatar = (
-
); diff --git a/src/components/profile/UserContent.js b/src/components/profile/UserContent.js index 7b38b87..885b71a 100644 --- a/src/components/profile/UserContent.js +++ b/src/components/profile/UserContent.js @@ -114,7 +114,7 @@ const UserContent = () => { return (
-
+

Your Content

diff --git a/src/components/profile/UserProfile.js b/src/components/profile/UserProfile.js new file mode 100644 index 0000000..098a72c --- /dev/null +++ b/src/components/profile/UserProfile.js @@ -0,0 +1,116 @@ +import React, { useRef, useState, useEffect } from "react"; +import { DataTable } from "primereact/datatable"; +import { Button } from "primereact/button"; +import { Menu } from "primereact/menu"; +import { Column } from "primereact/column"; +import { useImageProxy } from "@/hooks/useImageProxy"; +import { useSession } from 'next-auth/react'; +import { ProgressSpinner } from "primereact/progressspinner"; +import PurchasedListItem from "@/components/profile/PurchasedListItem"; +import { useNDKContext } from "@/context/NDKContext"; +import { formatDateTime } from "@/utils/time"; +import { findKind0Fields } from "@/utils/nostr"; +import Image from "next/image"; +import BitcoinConnectButton from "@/components/bitcoinConnect/BitcoinConnect"; +import UserContent from "@/components/profile/UserContent"; +import SubscribeModal from "@/components/profile/subscription/SubscribeModal"; +const UserProfile = () => { + const [user, setUser] = useState(null); + const { data: session } = useSession(); + const { returnImageProxy } = useImageProxy(); + const { ndk, addSigner } = useNDKContext(); + const menu = useRef(null); + + useEffect(() => { + if (session?.user) { + setUser(session.user); + } + }, [session]); + + const menuItems = [ + { + label: "Edit", + icon: "pi pi-pencil", + command: () => { + // Add your edit functionality here + }, + }, + { + label: "Delete", + icon: "pi pi-trash", + command: () => { + // Add your delete functionality here + }, + }, + ]; + + const header = ( +
+ Purchases +
+ ); + + return ( + user && ( +
+
+
+ user's avatar + menu.current.toggle(e)} + > + +
+ +

+ {user.username || "Anon"} +

+

+ {user.pubkey} +

+ {user && ( + + )} +
+ {!session || !session?.user || !ndk ? ( + + ) : ( + + + { + console.log("rowData", rowData); + return + }} + header="Name" + > + item.courseId) ? "course" : "resource"} header="Category"> + formatDateTime(rowData?.createdAt)} header="Date"> + + )} +
+ ) + ); +}; + +export default UserProfile; diff --git a/src/components/profile/UserSettings.js b/src/components/profile/UserSettings.js new file mode 100644 index 0000000..4587302 --- /dev/null +++ b/src/components/profile/UserSettings.js @@ -0,0 +1,108 @@ +import React, { useRef, useState, useEffect } from "react"; +import { Button } from "primereact/button"; +import { DataTable } from "primereact/datatable"; +import { Column } from "primereact/column"; +import { useImageProxy } from "@/hooks/useImageProxy"; +import { useSession } from 'next-auth/react'; +import { ProgressSpinner } from "primereact/progressspinner"; +import { useNDKContext } from "@/context/NDKContext"; +import Image from "next/image"; +import BitcoinConnectButton from "@/components/bitcoinConnect/BitcoinConnect"; + +const UserSettings = () => { + const [user, setUser] = useState(null); + const { data: session } = useSession(); + const { returnImageProxy } = useImageProxy(); + const { ndk } = useNDKContext(); + + useEffect(() => { + if (session?.user) { + setUser(session.user); + } + }, [session]); + + const relayUrls = [ + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.snort.social/", + "wss://relay.nostr.band/", + "wss://nostr.mutinywallet.com/", + "wss://relay.mutinywallet.com/", + "wss://relay.primal.net/" + ]; + + const relayStatusBody = (url) => { + // Placeholder for relay status, replace with actual logic later + const isConnected = Math.random() > 0.5; + return ( + + ); + }; + + const relayActionsBody = () => { + return ( +
+
+ ); + }; + + const header = ( +
+ Relays +
+ ); + + return ( + user && ( +
+
+
+ user's avatar +
+ +

+ {user.username || "Anon"} +

+

+ {user.pubkey} +

+
+

Connect Your Lightning Wallet

+ +
+
+ {!session || !session?.user || !ndk ? ( + + ) : ( + + url} header="Relay URL"> + + + + )} +
+ ) + ); +}; + +export default UserSettings; diff --git a/src/components/profile/subscription/SubscribeModal.js b/src/components/profile/subscription/SubscribeModal.js index 85b83cb..06d0da3 100644 --- a/src/components/profile/subscription/SubscribeModal.js +++ b/src/components/profile/subscription/SubscribeModal.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { Dialog } from 'primereact/dialog'; import { ProgressSpinner } from 'primereact/progressspinner'; import SubscriptionPaymentButtons from '@/components/bitcoinConnect/SubscriptionPaymentButton'; @@ -8,13 +8,33 @@ import { useRouter } from 'next/router'; import { useToast } from '@/hooks/useToast'; import { Card } from 'primereact/card'; import { Badge } from 'primereact/badge'; +import { Button } from "primereact/button"; +import { Menu } from "primereact/menu"; +import { Message } from "primereact/message"; -// todo encrypt nwc before saving in db -const SubscribeModal = ({ visible, onHide }) => { +const SubscribeModal = ({ user }) => { const { data: session, update } = useSession(); const { showToast } = useToast(); const router = useRouter(); const [isProcessing, setIsProcessing] = useState(false); + const [visible, setVisible] = useState(false); + const [subscribed, setSubscribed] = useState(false); + const [subscribedUntil, setSubscribedUntil] = useState(null); + const [subscriptionExpiredAt, setSubscriptionExpiredAt] = useState(null); + const menu = useRef(null); + + useEffect(() => { + if (user && user.role) { + setSubscribed(user.role.subscribed); + const subscribedAt = new Date(user.role.lastPaymentAt); + const subscribedUntil = new Date(subscribedAt.getTime() + 31 * 24 * 60 * 60 * 1000); + setSubscribedUntil(subscribedUntil); + if (user.role.subscriptionExpiredAt) { + const expiredAt = new Date(user.role.subscriptionExpiredAt) + setSubscriptionExpiredAt(expiredAt); + } + } + }, [user]); const handleSubscriptionSuccess = async (response) => { setIsProcessing(true); @@ -58,55 +78,121 @@ const SubscribeModal = ({ visible, onHide }) => { } }; + const menuItems = [ + { + label: "Renew Subscription", + icon: "pi pi-bolt", + command: () => { + // Add your renew functionality here + }, + }, + { + label: "Schedule 1:1", + icon: "pi pi-calendar", + command: () => { + // Add your schedule functionality here + }, + }, + { + label: "Cancel Subscription", + icon: "pi pi-trash", + command: () => { + // Add your cancel functionality here + }, + }, + ]; + + const subscriptionCardTitle = ( +
+ Plebdevs Subscription + menu.current.toggle(e)} + > + +
+ ); + return ( - - {isProcessing ? ( -
- - Processing subscription... -
- ) : ( - -
-

Unlock Premium Benefits

-

Subscribe now and elevate your development journey!

+ <> + + {subscribed && ( +
+ +

Thank you for your support 🎉

+

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

-
-
- - Access ALL current and future content -
-
- - Join PlebLab Bitcoin Hackerspace Slack -
-
- - Exclusive 1:1 booking calendar -
-
- - Personal mentorship & guidance -
+ )} + {(!subscribed && !subscriptionExpiredAt) && ( +
+ +
-
- - I WILL MAKE SURE YOU WIN HARD AND LEVEL UP AS A DEV! + )} + {subscriptionExpiredAt && ( +
+ +
- - - )} -
+ )} + + setVisible(false)} + className="p-fluid pb-0 w-fit" + > + {isProcessing ? ( +
+ + Processing subscription... +
+ ) : ( + +
+

Unlock Premium Benefits

+

Subscribe now and elevate your development journey!

+
+
+
+ + Access ALL current and future content +
+
+ + Join PlebLab Bitcoin Hackerspace Slack +
+
+ + Exclusive 1:1 booking calendar +
+
+ + Personal mentorship & guidance +
+
+
+ + I WILL MAKE SURE YOU WIN HARD AND LEVEL UP AS A DEV! +
+ +
+ )} +
+ ); }; diff --git a/src/components/profile/subscription/UserSubscription.js b/src/components/profile/subscription/UserSubscription.js index 5fe1b43..dc86ac6 100644 --- a/src/components/profile/subscription/UserSubscription.js +++ b/src/components/profile/subscription/UserSubscription.js @@ -1,12 +1,20 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, useRef, useEffect } from 'react'; +import { useSession } from 'next-auth/react'; +import { useRouter } from 'next/router'; +import { useToast } from '@/hooks/useToast'; +import axios from 'axios'; +import { Card } from 'primereact/card'; import { Button } from "primereact/button"; import { Menu } from "primereact/menu"; import { Message } from "primereact/message"; -import { Card } from "primereact/card"; -import SubscribeModal from "@/components/profile/subscription/SubscribeModal"; +import { ProgressSpinner } from 'primereact/progressspinner'; +import SubscriptionPaymentButtons from '@/components/bitcoinConnect/SubscriptionPaymentButton'; const UserSubscription = ({ user }) => { - const [subscribeModalVisible, setSubscribeModalVisible] = useState(false); + const { data: session, update } = useSession(); + const { showToast } = useToast(); + const router = useRouter(); + const [isProcessing, setIsProcessing] = useState(false); const [subscribed, setSubscribed] = useState(false); const [subscribedUntil, setSubscribedUntil] = useState(null); const [subscriptionExpiredAt, setSubscriptionExpiredAt] = useState(null); @@ -25,34 +33,77 @@ const UserSubscription = ({ user }) => { } }, [user]); + const handleSubscriptionSuccess = async (paymentResponse) => { + setIsProcessing(true); + try { + const response = await axios.post('/api/subscription/create', { + paymentResponse, + }); + if (response.data.success) { + showToast('success', 'Subscription successful!'); + await update(); + router.push('/dashboard'); + } else { + showToast('error', 'Subscription failed. Please try again.'); + } + } catch (error) { + console.error('Subscription error:', error); + showToast('error', 'An error occurred. Please try again.'); + } finally { + setIsProcessing(false); + } + }; + + const handleSubscriptionError = (error) => { + console.error('Subscription error:', error); + showToast('error', 'An error occurred during subscription. Please try again.'); + }; + + const handleRecurringSubscriptionSuccess = async (paymentResponse) => { + setIsProcessing(true); + try { + const response = await axios.post('/api/subscription/recurring', { + paymentResponse, + }); + if (response.data.success) { + showToast('success', 'Recurring subscription set up successfully!'); + await update(); + router.push('/dashboard'); + } else { + showToast('error', 'Failed to set up recurring subscription. Please try again.'); + } + } catch (error) { + console.error('Recurring subscription error:', error); + showToast('error', 'An error occurred. Please try again.'); + } finally { + setIsProcessing(false); + } + }; + const menuItems = [ { label: "Renew Subscription", icon: "pi pi-bolt", command: () => { - // Add your edit functionality here + // Add your renew functionality here }, }, { label: "Schedule 1:1", icon: "pi pi-calendar", command: () => { - // Add your edit functionality here + // Add your schedule functionality here }, }, { label: "Cancel Subscription", icon: "pi pi-trash", command: () => { - // Add your delete functionality here + // Add your cancel functionality here }, }, ]; - const openSubscribeModal = () => { - setSubscribeModalVisible(true); - }; - const subscriptionCardTitle = (
Plebdevs Subscription @@ -65,41 +116,90 @@ const UserSubscription = ({ user }) => { ); return ( - <> - +
+

Subscription Management

+ {subscribed && (
-

Thank you for your support 🎉

+

Thank you for your support 🎉

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

)} {(!subscribed && !subscriptionExpiredAt) && (
-
)} {subscriptionExpiredAt && (
-
)}
- setSubscribeModalVisible(false)} - /> - + + + {isProcessing ? ( +
+ + Processing subscription... +
+ ) : ( +
+

Choose your subscription plan:

+
+ +

Monthly Subscription

+

Get access to all PlebDevs features / content one month at a time.

+ +
+ +

Recurring Monthly Subscription

+

Setup auto recurring monthly payments for uninterrupted access.

+ +
+
+
+ )} +
+ + +
    +
  • Access to exclusive content
  • +
  • Priority support
  • +
  • Early access to new features
  • +
  • Community forums
  • +
+
+ + +
+
+

How does the subscription work?

+

Our subscription provides monthly access to all PlebDevs features. You can choose between a one-time payment or a recurring subscription.

+
+
+

Can I cancel my subscription?

+

Yes, you can cancel your subscription at any time. Your access will remain active until the end of the current billing period.

+
+ {/* Add more FAQ items as needed */} +
+
+
); }; diff --git a/src/components/sidebar/Sidebar.js b/src/components/sidebar/Sidebar.js index 69cffe0..ed9a5f1 100644 --- a/src/components/sidebar/Sidebar.js +++ b/src/components/sidebar/Sidebar.js @@ -1,6 +1,7 @@ import React from 'react'; import { Accordion, AccordionTab } from 'primereact/accordion'; import { useRouter } from 'next/router'; +import { useSession, signOut } from 'next-auth/react'; import 'primeicons/primeicons.css'; import styles from "./sidebar.module.css"; @@ -13,40 +14,59 @@ const Sidebar = () => { return pathWithQuery === path; }; + const { data: session } = useSession(); + return ( -
-
router.push('/')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/') ? 'bg-gray-700' : ''}`}> -

Home

+
+
+
router.push('/')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/') ? 'bg-gray-700' : ''}`}> +

Home

+
+
router.push('/content')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/content') ? 'bg-gray-700' : ''}`}> +

Content

+
+
router.push('/create')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/create') ? 'bg-gray-700' : ''}`}> +

Create

+
+
session ? router.push('/profile?tab=subscribe') : router.push('/auth/signin')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/profile?tab=subscribe') ? 'bg-gray-700' : ''}`}> +

Subscribe

+
+ + ({ + className: `hover:bg-gray-700 rounded-lg ${isActive('/feed') ? 'bg-gray-700' : ''} ${styles['p-accordion-header-link']}` + }), + content: styles['p-accordion-content'] + }} + header={"Community"}> +
router.push('/feed?channel=global')} className={`w-full cursor-pointer py-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=global') ? 'bg-gray-700' : ''}`}> +

global

+
+
router.push('/feed?channel=nostr')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=nostr') ? 'bg-gray-700' : ''}`}> +

nostr

+
+
router.push('/feed?channel=discord')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=discord') ? 'bg-gray-700' : ''}`}> +

discord

+
+
router.push('/feed?channel=stackernews')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=stackernews') ? 'bg-gray-700' : ''}`}> +

stackernews

+
+
+
-
router.push('/content')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/content') ? 'bg-gray-700' : ''}`}> -

Content

+
+
router.push('/profile?tab=settings')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/profile?tab=settings') ? 'bg-gray-700' : ''}`}> +

Settings

+
+
session ? signOut() : router.push('/auth/signin')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg`}> +

{session ? 'Logout' : 'Login'}

+
+ {/* todo: have to add this extra button to push the sidebar to the right space but it doesnt seem to cause any negative side effects? */} +
+

Logout

+
-
router.push('/create')} className={`w-full flex flex-row items-center cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/create') ? 'bg-gray-700' : ''}`}> -

Create

-
- - ({ - className: `hover:bg-gray-700 rounded-lg ${isActive('/feed') ? 'bg-gray-700' : ''} ${styles['p-accordion-header-link']}` - }), - content: styles['p-accordion-content'] - }} - header={"Community"}> -
router.push('/feed?channel=global')} className={`w-full cursor-pointer py-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=global') ? 'bg-gray-700' : ''}`}> -

global

-
-
router.push('/feed?channel=nostr')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=nostr') ? 'bg-gray-700' : ''}`}> -

nostr

-
-
router.push('/feed?channel=discord')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=discord') ? 'bg-gray-700' : ''}`}> -

discord

-
-
router.push('/feed?channel=stackernews')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/feed?channel=stackernews') ? 'bg-gray-700' : ''}`}> -

stackernews

-
-
-
); }; diff --git a/src/pages/auth/signin.js b/src/pages/auth/signin.js index 3190323..b7fa2fe 100644 --- a/src/pages/auth/signin.js +++ b/src/pages/auth/signin.js @@ -45,7 +45,7 @@ export default function SignIn() { } return ( -
+

Sign In

diff --git a/src/pages/profile.js b/src/pages/profile.js index 3bc70cd..9f05da4 100644 --- a/src/pages/profile.js +++ b/src/pages/profile.js @@ -1,130 +1,77 @@ -import React, { useRef, useState, useEffect } from "react"; -import { Button } from "primereact/button"; -import { DataTable } from "primereact/datatable"; -import { Menu } from "primereact/menu"; -import { Column } from "primereact/column"; -import { Message } from "primereact/message"; -import { Card } from "primereact/card"; -import { useImageProxy } from "@/hooks/useImageProxy"; -import { useSession } from 'next-auth/react'; -import { ProgressSpinner } from "primereact/progressspinner"; -import PurchasedListItem from "@/components/profile/PurchasedListItem"; -import { useNDKContext } from "@/context/NDKContext"; -import { formatDateTime } from "@/utils/time"; +import React, { useState, useEffect } from "react"; +import { TabView, TabPanel } from "primereact/tabview"; +import UserProfile from "@/components/profile/UserProfile"; +import UserSettings from "@/components/profile/UserSettings"; import UserContent from "@/components/profile/UserContent"; -import Image from "next/image"; -import BitcoinConnectButton from "@/components/bitcoinConnect/BitcoinConnect"; import UserSubscription from "@/components/profile/subscription/UserSubscription"; +import { useRouter } from "next/router"; const Profile = () => { - const [user, setUser] = useState(null); - const { data: session } = useSession(); - const { returnImageProxy } = useImageProxy(); - const { ndk } = useNDKContext(); - const menu = useRef(null); + const router = useRouter(); + const [activeTab, setActiveTab] = useState(0); + + const tabs = ["profile", "settings", "content", "subscribe"]; useEffect(() => { - if (session?.user) { - setUser(session.user); + const { tab } = router.query; + if (tab) { + const index = tabs.indexOf(tab.toLowerCase()); + if (index !== -1) { + setActiveTab(index); + } } - }, [session]); + }, [router.query]); - const menuItems = [ - { - label: "Edit", - icon: "pi pi-pencil", - command: () => { - // Add your edit functionality here - }, - }, - { - label: "Delete", - icon: "pi pi-trash", - command: () => { - // Add your delete functionality here - }, - }, - ]; - - const header = ( -
- Purchases -
- ); - - const openSubscribeModal = () => { - setSubscribeModalVisible(true); + const onTabChange = (e) => { + const newIndex = e.index; + setActiveTab(newIndex); + router.push(`/profile?tab=${tabs[newIndex]}`, undefined, { shallow: true }); }; - const subscriptionCardTitle = ( -
- Plebdevs Subscription - menu.current.toggle(e)} - > - -
- ); - return ( - user && ( -
-
-
- user's avatar - menu.current.toggle(e)} - > - -
- -

- {user.username || "Anon"} -

-

- {user.pubkey} -

-
-

Connect Your Lightning Wallet

- -
- {user && ( - - )} -
- {!session || !session?.user || !ndk ? ( - - ) : ( - - - { - console.log("rowData", rowData); - return - }} - header="Name" - > - item.courseId) ? "course" : "resource"} header="Category"> - formatDateTime(rowData?.createdAt)} header="Date"> - - - )} - -
- ) +
+ + + + + + + + + + + + + + +
); };