diff --git a/src/components/content/courses/CourseDetails.js b/src/components/content/courses/CourseDetails.js index c2e308d..06ae2ec 100644 --- a/src/components/content/courses/CourseDetails.js +++ b/src/components/content/courses/CourseDetails.js @@ -21,6 +21,7 @@ import WelcomeModal from '@/components/onboarding/WelcomeModal'; import { ProgressSpinner } from 'primereact/progressspinner'; import { Toast } from 'primereact/toast'; import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu'; +import { Divider } from 'primereact/divider'; export default function CourseDetails({ processedEvent, @@ -201,54 +202,63 @@ export default function CourseDetails({
-
- course image -
-
-
- router.push('/')} - /> -
- {isCompleted && } -
-

{processedEvent.name}

- + {/* Header with course image, title and options */} +
+ {/* Course image */} +
+ course image
-
- {processedEvent.topics && - processedEvent.topics.length > 0 && - processedEvent.topics.map((topic, index) => ( - - ))} -
-
- {processedEvent.description && - processedEvent.description - .split('\n') - .map((line, index) =>

{line}

)} -
-
+ + {/* Title and options */} +
+
+
+ {isCompleted && ( + + )} +

{processedEvent.name}

+
+
+ + +
+
+ + {/* Topics/tags */} +
+ {processedEvent.topics && + processedEvent.topics.length > 0 && + processedEvent.topics.map((topic, index) => ( + + ))} +
+ + {/* Author info */}
avatar image -

- By{' '} +

+ Created by{' '}

-
- +
+
+ + + + {/* Course details */} +
+ {/* Left column: Description */} +
+

About This Course

+
+ {processedEvent.description && + processedEvent.description + .split('\n') + .map((line, index) =>

{line}

)} +
+ + {/* Payment section */} +
+ {renderPaymentMessage()} +
+
+ + {/* Right column: Course details */} +
+

Course Information

+ +
+
+

Course Content

+
+
+

Lessons

+

{lessons.length}

+
+ {paidCourse && ( +
+

Price

+

{processedEvent.price} sats

+
+ )} +
+
+ + {processedEvent.published && ( +
+

Details

+
+

Published

+

+ {new Date(processedEvent.published * 1000).toLocaleDateString()} +

+
+
+ )}
-
{renderPaymentMessage()}
diff --git a/src/components/content/courses/CourseSidebar.js b/src/components/content/courses/CourseSidebar.js index fe6e7ee..f4728ea 100644 --- a/src/components/content/courses/CourseSidebar.js +++ b/src/components/content/courses/CourseSidebar.js @@ -1,5 +1,7 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { Tag } from 'primereact/tag'; +import { Button } from 'primereact/button'; +import { Sidebar } from 'primereact/sidebar'; import Image from 'next/image'; import { useImageProxy } from '@/hooks/useImageProxy'; @@ -10,9 +12,26 @@ const CourseSidebar = ({ completedLessons, isMobileView, onClose, - sidebarVisible, + sidebarVisible: parentSidebarVisible, + setSidebarVisible, }) => { const { returnImageProxy } = useImageProxy(); + const [visible, setVisible] = useState(true); + + // Sync with parent state if provided + useEffect(() => { + if (typeof parentSidebarVisible !== 'undefined') { + setVisible(parentSidebarVisible); + } + }, [parentSidebarVisible]); + + const handleToggle = () => { + const newState = !visible; + setVisible(newState); + if (setSidebarVisible) { + setSidebarVisible(newState); + } + }; const LessonItem = ({ lesson, index }) => (
  • ); - // Desktop sidebar implementation + // Sidebar content component for reuse + const SidebarContent = () => ( +
    +
    +

    Course Lessons

    + {visible && ( +
    +
    +
      + {lessons.map((lesson, index) => ( + + ))} +
    +
    +
    + ); + + // Toggle button (used for both desktop and mobile) + const ToggleButton = () => ( +
    +
    + ); + + // Desktop implementation with integrated toggle button if (!isMobileView) { return ( -
    -
    -
    -
    -

    Course Lessons

    -
    -
    -
      - {lessons.map((lesson, index) => ( - - ))} -
    + <> + {/* Sidebar content */} +
    +
    +
    +
    + +
    -
    - ); - } - - // Mobile sidebar implementation - completely restructured for better scrolling - if (isMobileView && sidebarVisible) { - return ( -
    -
    -

    Course Lessons

    -
    - {/* Scrollable container with fixed height */} -
    -
    -
      - {lessons.map((lesson, index) => ( - - ))} -
    -
    -
    -
    + {/* Detached toggle button when sidebar is closed */} + {!visible && } + ); } - return null; + // Mobile implementation with PrimeReact's Sidebar + return ( + <> + {/* Mobile toggle button - only shown when sidebar is closed */} + {!visible && ( +
    +
    + )} + + {/* Mobile sidebar */} + +
    +

    Course Lessons

    +
    + +
    +
      + {lessons.map((lesson, index) => ( + + ))} +
    +
    +
    + + ); }; export default CourseSidebar; diff --git a/src/components/profile/DataTables/UserProgressTable.js b/src/components/profile/DataTables/UserProgressTable.js index 4863323..986ccec 100644 --- a/src/components/profile/DataTables/UserProgressTable.js +++ b/src/components/profile/DataTables/UserProgressTable.js @@ -1,7 +1,6 @@ import React from 'react'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; -import useWindowWidth from '@/hooks/useWindowWidth'; import ProgressListItem from '@/components/content/lists/ProgressListItem'; import { formatDateTime } from '@/utils/time'; import { ProgressSpinner } from 'primereact/progressspinner'; diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js index bf4d13f..74954e9 100644 --- a/src/pages/course/[slug]/index.js +++ b/src/pages/course/[slug]/index.js @@ -16,6 +16,7 @@ import dynamic from 'next/dynamic'; import ZapThreadsWrapper from '@/components/ZapThreadsWrapper'; import appConfig from '@/config/appConfig'; import useWindowWidth from '@/hooks/useWindowWidth'; +import MenuTab from '@/components/menutab/MenuTab'; const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), { ssr: false, @@ -182,10 +183,22 @@ const Course = () => { const [nsec, setNsec] = useState(null); const [npub, setNpub] = useState(null); const [sidebarVisible, setSidebarVisible] = useState(false); + const [nAddress, setNAddress] = useState(null); const windowWidth = useWindowWidth(); const isMobileView = windowWidth <= 968; const [activeTab, setActiveTab] = useState('content'); // Default to content tab on mobile + useEffect(() => { + if (router.isReady) { + const { slug } = router.query; + if (slug.includes('naddr')) { + setNAddress(slug); + } else { + // todo: no naddress? + } + } + }, [router.isReady, router.query.slug]); + const setCompleted = useCallback(lessonId => { setCompletedLessons(prev => [...prev, lessonId]); }, []); @@ -297,13 +310,93 @@ const Course = () => { ); }; - const toggleTab = tab => { - setActiveTab(tab); - if (tab === 'lessons') { - setSidebarVisible(true); - } else { - setSidebarVisible(false); + const toggleTab = (index) => { + const tabMap = ['overview', 'content', 'qa']; + // If mobile and we have the lessons tab, insert it at index 2 + if (isMobileView) { + tabMap.splice(2, 0, 'lessons'); } + + const tabName = tabMap[index]; + setActiveTab(tabName); + + // Only show/hide sidebar on mobile - desktop keeps sidebar visible + if (isMobileView) { + if (tabName === 'lessons') { + setSidebarVisible(true); + } else { + setSidebarVisible(false); + } + } + }; + + // Map active tab name back to index for MenuTab + const getActiveTabIndex = () => { + const tabMap = ['overview', 'content', 'qa']; + if (isMobileView) { + tabMap.splice(2, 0, 'lessons'); + } + + return tabMap.indexOf(activeTab); + }; + + // Create tab items for MenuTab + const getTabItems = () => { + const items = [ + { + label: 'Course Overview', + icon: 'pi pi-home', + }, + { + label: 'Lesson Content', + icon: 'pi pi-book', + } + ]; + + // Add lessons tab only on mobile + if (isMobileView) { + items.push({ + label: 'Course Lessons', + icon: 'pi pi-list', + }); + } + + items.push({ + label: 'Q&A', + icon: 'pi pi-comments', + }); + + return items; + }; + + // Render the QA section (empty for now) + const renderQASection = () => { + return ( +
    +

    Comments

    + +
    + ); + }; + + // Render Course Overview section + const renderOverviewSection = () => { + return ( +
    + +
    + ); }; if (courseLoading || decryptionLoading) { @@ -350,7 +443,7 @@ const Course = () => { return ( <> - {course && paidCourse !== null && ( + {/* {course && paidCourse !== null && ( { handlePaymentSuccess={handlePaymentSuccess} handlePaymentError={handlePaymentError} /> - )} + )} */} -
    - {/* Mobile tab navigation */} - {isMobileView && ( -
    - - -
    - )} - -
    - {/* Course Sidebar Component */} - { - setSidebarVisible(false); - if (isMobileView) setActiveTab('content'); - }} - sidebarVisible={sidebarVisible} +
    + {/* Tab navigation using MenuTab component */} +
    + toggleTab(index)} /> +
    - {/* Main content */} +
    + {/* Main content area - keep existing implementation */}
    - {uniqueLessons.length > 0 && uniqueLessons[activeIndex] ? ( -
    - {renderLesson(uniqueLessons[activeIndex])} -
    - ) : ( -
    -

    Select a lesson from the sidebar to begin learning.

    -
    - )} + {/* Overview tab content */} +
    + {renderOverviewSection()} +
    + + {/* Content tab content */} +
    + {uniqueLessons.length > 0 && uniqueLessons[activeIndex] ? ( +
    + {renderLesson(uniqueLessons[activeIndex])} +
    + ) : ( +
    +

    Select a lesson from the sidebar to begin learning.

    +
    + )} - {course?.content && ( -
    - + {course?.content && ( +
    + +
    + )} +
    + + {/* Lessons tab - only visible on mobile */} +
    +
    +

    Please use the sidebar to navigate lessons.

    - )} +
    + + {/* QA tab content */} +
    + {renderQASection()} +
    +
    + + {/* Course Sidebar Component - Always visible on desktop, hidden on mobile unless lessons tab is active */} +
    + { + handleLessonSelect(index); + if (isMobileView) { + toggleTab(getTabItems().findIndex(item => item.label === 'Lesson Content')); // On mobile, switch to content tab when a lesson is selected + } + }} + completedLessons={completedLessons} + isMobileView={isMobileView} + onClose={() => { + setSidebarVisible(false); + setActiveTab('content'); + }} + sidebarVisible={sidebarVisible || !isMobileView} // Always visible on desktop + setSidebarVisible={setSidebarVisible} + />