mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-23 16:05:24 +00:00
massive improvements to course and lesson full screen and mobile layouts
This commit is contained in:
parent
874d903020
commit
b50aa9286a
@ -30,6 +30,8 @@ export default function CourseDetails({
|
|||||||
decryptionPerformed,
|
decryptionPerformed,
|
||||||
handlePaymentSuccess,
|
handlePaymentSuccess,
|
||||||
handlePaymentError,
|
handlePaymentError,
|
||||||
|
isMobileView,
|
||||||
|
showCompletedTag = true,
|
||||||
}) {
|
}) {
|
||||||
const [zapAmount, setZapAmount] = useState(0);
|
const [zapAmount, setZapAmount] = useState(0);
|
||||||
const [author, setAuthor] = useState(null);
|
const [author, setAuthor] = useState(null);
|
||||||
@ -40,7 +42,8 @@ export default function CourseDetails({
|
|||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
const windowWidth = useWindowWidth();
|
const windowWidth = useWindowWidth();
|
||||||
const isMobileView = windowWidth <= 768;
|
const localIsMobileView = windowWidth <= 768; // Use as fallback
|
||||||
|
const isPhone = isMobileView || localIsMobileView;
|
||||||
const { ndk } = useNDKContext();
|
const { ndk } = useNDKContext();
|
||||||
const menuRef = useRef(null);
|
const menuRef = useRef(null);
|
||||||
const toastRef = useRef(null);
|
const toastRef = useRef(null);
|
||||||
@ -205,42 +208,81 @@ export default function CourseDetails({
|
|||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{/* Header with course image, title and options */}
|
{/* Header with course image, title and options */}
|
||||||
<div className="flex mb-6">
|
{!isPhone && (
|
||||||
{/* Course image */}
|
<div className="flex mb-6">
|
||||||
<div className="relative w-52 h-32 mr-6 flex-shrink-0 rounded-lg overflow-hidden">
|
{/* Course image */}
|
||||||
<Image
|
<div className="relative w-52 h-32 mr-6 flex-shrink-0 rounded-lg overflow-hidden">
|
||||||
alt="course image"
|
<Image
|
||||||
src={returnImageProxy(processedEvent.image)}
|
alt="course image"
|
||||||
fill
|
src={returnImageProxy(processedEvent.image)}
|
||||||
className="object-cover"
|
fill
|
||||||
/>
|
className="object-cover"
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
{/* Title and options */}
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-start justify-between mb-2">
|
|
||||||
<div>
|
|
||||||
{isCompleted && (
|
|
||||||
<Tag severity="success" value="Completed" className="mb-2" />
|
|
||||||
)}
|
|
||||||
<h1 className="text-2xl font-bold text-white">{processedEvent.name}</h1>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<ZapDisplay
|
|
||||||
zapAmount={zapAmount}
|
|
||||||
event={processedEvent}
|
|
||||||
zapsLoading={zapsLoading && zapAmount === 0}
|
|
||||||
/>
|
|
||||||
<MoreOptionsMenu
|
|
||||||
menuItems={menuItems}
|
|
||||||
additionalLinks={processedEvent?.additionalLinks || []}
|
|
||||||
isMobileView={isMobileView}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Topics/tags */}
|
{/* Title and options */}
|
||||||
<div className="flex flex-wrap gap-2 mb-3">
|
<div className="flex-1">
|
||||||
|
<div className="flex items-start justify-between mb-2">
|
||||||
|
<div>
|
||||||
|
{isCompleted && showCompletedTag && (
|
||||||
|
<Tag severity="success" value="Completed" className="mb-2" />
|
||||||
|
)}
|
||||||
|
<h1 className="text-2xl font-bold text-white">{processedEvent.name}</h1>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<ZapDisplay
|
||||||
|
zapAmount={zapAmount}
|
||||||
|
event={processedEvent}
|
||||||
|
zapsLoading={zapsLoading && zapAmount === 0}
|
||||||
|
/>
|
||||||
|
<MoreOptionsMenu
|
||||||
|
menuItems={menuItems}
|
||||||
|
additionalLinks={processedEvent?.additionalLinks || []}
|
||||||
|
isMobileView={isPhone}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Topics/tags */}
|
||||||
|
<div className="flex flex-wrap gap-2 mb-3">
|
||||||
|
{processedEvent.topics &&
|
||||||
|
processedEvent.topics.length > 0 &&
|
||||||
|
processedEvent.topics.map((topic, index) => (
|
||||||
|
<Tag className="text-white" key={index} value={topic}></Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Author info */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Image
|
||||||
|
alt="avatar image"
|
||||||
|
src={returnImageProxy(author?.avatar, author?.pubkey)}
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
|
className="rounded-full mr-2"
|
||||||
|
/>
|
||||||
|
<p className="text-gray-300">
|
||||||
|
Created by{' '}
|
||||||
|
<a
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
target="_blank"
|
||||||
|
className="text-blue-300 hover:underline"
|
||||||
|
>
|
||||||
|
{author?.username || author?.name || author?.pubkey}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Mobile-specific layout */}
|
||||||
|
{isPhone && (
|
||||||
|
<div className="mb-4">
|
||||||
|
{/* Completed tag is now moved to the parent component */}
|
||||||
|
|
||||||
|
{/* Mobile topics/tags right below image (image is in parent component) */}
|
||||||
|
<div className="flex flex-wrap gap-2 mb-3 mt-2">
|
||||||
{processedEvent.topics &&
|
{processedEvent.topics &&
|
||||||
processedEvent.topics.length > 0 &&
|
processedEvent.topics.length > 0 &&
|
||||||
processedEvent.topics.map((topic, index) => (
|
processedEvent.topics.map((topic, index) => (
|
||||||
@ -248,41 +290,58 @@ export default function CourseDetails({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Author info */}
|
{/* Title and zaps in same row */}
|
||||||
<div className="flex items-center">
|
<div className="flex justify-between items-center mb-3">
|
||||||
<Image
|
<h1 className="text-xl font-bold text-white mr-3">{processedEvent.name}</h1>
|
||||||
alt="avatar image"
|
<ZapDisplay
|
||||||
src={returnImageProxy(author?.avatar, author?.pubkey)}
|
zapAmount={zapAmount}
|
||||||
width={32}
|
event={processedEvent}
|
||||||
height={32}
|
zapsLoading={zapsLoading && zapAmount === 0}
|
||||||
className="rounded-full mr-2"
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Author info and more options in bottom row */}
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Image
|
||||||
|
alt="avatar image"
|
||||||
|
src={returnImageProxy(author?.avatar, author?.pubkey)}
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
|
className="rounded-full mr-2"
|
||||||
|
/>
|
||||||
|
<p className="text-gray-300 text-sm">
|
||||||
|
Created by{' '}
|
||||||
|
<a
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
target="_blank"
|
||||||
|
className="text-blue-300 hover:underline"
|
||||||
|
>
|
||||||
|
{author?.username || author?.name || author?.pubkey}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<MoreOptionsMenu
|
||||||
|
menuItems={menuItems}
|
||||||
|
additionalLinks={processedEvent?.additionalLinks || []}
|
||||||
|
isMobileView={isPhone}
|
||||||
/>
|
/>
|
||||||
<p className="text-gray-300">
|
|
||||||
Created by{' '}
|
|
||||||
<a
|
|
||||||
rel="noreferrer noopener"
|
|
||||||
target="_blank"
|
|
||||||
className="text-blue-300 hover:underline"
|
|
||||||
>
|
|
||||||
{author?.username || author?.name || author?.pubkey}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<Divider className="my-4" />
|
<Divider className="my-4" />
|
||||||
|
|
||||||
{/* Course details */}
|
{/* Course details */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className={`grid grid-cols-1 ${isPhone ? 'gap-4' : 'lg:grid-cols-3 gap-6'}`}>
|
||||||
{/* Left column: Description */}
|
{/* Left column: Description */}
|
||||||
<div className="lg:col-span-2">
|
<div className={`${isPhone ? '' : 'lg:col-span-2'}`}>
|
||||||
<h2 className="text-xl font-semibold mb-3 text-white">About This Course</h2>
|
<h2 className="text-xl font-semibold mb-3 text-white">About This Course</h2>
|
||||||
<div className="text-gray-300 mb-4">
|
<div className="text-gray-300 mb-4">
|
||||||
{processedEvent.description &&
|
{processedEvent.description &&
|
||||||
processedEvent.description
|
processedEvent.description
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((line, index) => <p key={index} className="mb-2">{line}</p>)}
|
.map((line, index) => <p key={index} className={`${isPhone ? 'text-sm' : ''} mb-2`}>{line}</p>)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Payment section */}
|
{/* Payment section */}
|
||||||
@ -292,8 +351,8 @@ export default function CourseDetails({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right column: Course details */}
|
{/* Right column: Course details */}
|
||||||
<div className="bg-gray-800 rounded-lg p-4">
|
<div className={`bg-gray-800 rounded-lg h-fit ${isPhone ? 'p-3' : 'p-4'}`}>
|
||||||
<h2 className="text-xl font-semibold mb-3 text-white">Course Information</h2>
|
<h2 className={`${isPhone ? 'text-lg' : 'text-xl'} font-semibold mb-3 text-white`}>Course Information</h2>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
|
@ -14,6 +14,7 @@ const CourseSidebar = ({
|
|||||||
onClose,
|
onClose,
|
||||||
sidebarVisible: parentSidebarVisible,
|
sidebarVisible: parentSidebarVisible,
|
||||||
setSidebarVisible,
|
setSidebarVisible,
|
||||||
|
hideToggleButton = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
@ -85,7 +86,7 @@ const CourseSidebar = ({
|
|||||||
<div className="flex flex-col h-full bg-gray-800 text-[#f8f8ff] px-4 py-4">
|
<div className="flex flex-col h-full bg-gray-800 text-[#f8f8ff] px-4 py-4">
|
||||||
<div className="flex items-center justify-between border-b border-gray-700 pb-4 mb-4">
|
<div className="flex items-center justify-between border-b border-gray-700 pb-4 mb-4">
|
||||||
<h2 className="font-bold text-white text-lg">Course Lessons</h2>
|
<h2 className="font-bold text-white text-lg">Course Lessons</h2>
|
||||||
{visible && (
|
{visible && !hideToggleButton && !isMobileView && (
|
||||||
<Button
|
<Button
|
||||||
icon="pi pi-times"
|
icon="pi pi-times"
|
||||||
onClick={handleToggle}
|
onClick={handleToggle}
|
||||||
@ -105,89 +106,84 @@ const CourseSidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Toggle button (used for both desktop and mobile)
|
// Global toggle button container - only shown if hideToggleButton is false
|
||||||
const ToggleButton = () => (
|
const SidebarToggle = () => {
|
||||||
<div className="fixed right-0 top-1/3 z-50 m-0 p-0">
|
if (visible || hideToggleButton || isMobileView) return null;
|
||||||
<Button
|
|
||||||
icon="pi pi-chevron-left"
|
return (
|
||||||
onClick={handleToggle}
|
<div
|
||||||
className="shadow-md border-0 rounded-r-none rounded-l-md bg-blue-600 hover:bg-blue-700"
|
style={{
|
||||||
tooltip="Show lessons"
|
position: 'fixed',
|
||||||
tooltipOptions={{ position: 'left' }}
|
right: 0,
|
||||||
style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
|
top: '40%',
|
||||||
/>
|
zIndex: 99999,
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
transform: 'translateY(-50%)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon="pi pi-chevron-left"
|
||||||
|
onClick={handleToggle}
|
||||||
|
style={{
|
||||||
|
borderTopRightRadius: 0,
|
||||||
|
borderBottomRightRadius: 0,
|
||||||
|
boxShadow: '0 0 20px rgba(0,0,0,0.5)',
|
||||||
|
width: '3rem',
|
||||||
|
height: '5rem'
|
||||||
|
}}
|
||||||
|
className="shadow-2xl border-0 rounded-r-none rounded-l-xl bg-blue-600 hover:bg-blue-700 pointer-events-auto"
|
||||||
|
tooltip="Show lessons"
|
||||||
|
tooltipOptions={{ position: 'left' }}
|
||||||
|
pt={{
|
||||||
|
icon: { className: 'font-bold text-lg' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mobile content tab
|
||||||
|
const MobileLessonsTab = () => (
|
||||||
|
<div className="bg-gray-900 rounded-lg overflow-hidden border border-gray-800 shadow-md mb-6">
|
||||||
|
<div className="bg-gray-800 p-4 border-b border-gray-700">
|
||||||
|
<h2 className="font-bold text-white text-xl">Course Lessons</h2>
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{lessons.map((lesson, index) => (
|
||||||
|
<LessonItem key={index} lesson={lesson} index={index} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Desktop implementation with integrated toggle button
|
return (
|
||||||
if (!isMobileView) {
|
<>
|
||||||
return (
|
{/* Unified button approach for desktop - only if not hidden */}
|
||||||
<>
|
{!hideToggleButton && <SidebarToggle />}
|
||||||
{/* Sidebar content */}
|
|
||||||
<div className="relative flex flex-row-reverse">
|
{/* Mobile view - direct content instead of sidebar */}
|
||||||
|
{isMobileView && visible && (
|
||||||
|
<MobileLessonsTab />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Desktop sidebar */}
|
||||||
|
{!isMobileView && (
|
||||||
|
<div className="relative flex flex-row-reverse z-[999]">
|
||||||
<div
|
<div
|
||||||
className={`transition-all duration-300 flex ${
|
className={`transition-all duration-500 ease-in-out flex ${
|
||||||
visible ? 'w-80' : 'w-0 overflow-hidden'
|
visible ? 'w-80 opacity-100' : 'w-0 opacity-0 overflow-hidden'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="w-80 h-[calc(100vh-400px)] sticky top-8 overflow-hidden rounded-lg border border-gray-800 shadow-md bg-gray-900">
|
<div className="ml-2 w-80 h-[calc(100vh-400px)] sticky top-8 overflow-hidden rounded-lg border border-gray-800 shadow-md bg-gray-900">
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
<SidebarContent />
|
<SidebarContent />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Detached toggle button when sidebar is closed */}
|
|
||||||
{!visible && <ToggleButton />}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mobile implementation with PrimeReact's Sidebar
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Mobile toggle button - only shown when sidebar is closed */}
|
|
||||||
{!visible && (
|
|
||||||
<div className="fixed right-0 top-20 z-40 m-0 p-0">
|
|
||||||
<Button
|
|
||||||
icon="pi pi-list"
|
|
||||||
onClick={handleToggle}
|
|
||||||
className="shadow-md bg-blue-600 hover:bg-blue-700 border-0 rounded-r-none rounded-l-md"
|
|
||||||
tooltip="Show lessons"
|
|
||||||
tooltipOptions={{ position: 'left' }}
|
|
||||||
style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Mobile sidebar */}
|
|
||||||
<Sidebar
|
|
||||||
visible={visible}
|
|
||||||
position="right"
|
|
||||||
onHide={handleToggle}
|
|
||||||
className="bg-gray-900 p-0 shadow-lg"
|
|
||||||
style={{ width: '85vw', maxWidth: '350px' }}
|
|
||||||
showCloseIcon={false}
|
|
||||||
modal={false}
|
|
||||||
>
|
|
||||||
<div className="bg-gray-800 p-5 border-b border-gray-700 flex justify-between items-center">
|
|
||||||
<h2 className="font-bold text-white text-xl">Course Lessons</h2>
|
|
||||||
<Button
|
|
||||||
icon="pi pi-times"
|
|
||||||
onClick={handleToggle}
|
|
||||||
className="p-button-rounded p-button-text text-gray-300 hover:text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="overflow-y-auto h-full p-4 bg-gray-900">
|
|
||||||
<ul className="space-y-3">
|
|
||||||
{lessons.map((lesson, index) => (
|
|
||||||
<LessonItem key={index} lesson={lesson} index={index} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Sidebar>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TabMenu } from 'primereact/tabmenu';
|
import { TabMenu } from 'primereact/tabmenu';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
|
||||||
export default function MenuTab({ items, activeIndex, onTabChange }) {
|
export default function MenuTab({ items, activeIndex, onTabChange, sidebarVisible, onToggleSidebar, isMobileView = false }) {
|
||||||
return (
|
return (
|
||||||
<div className="w-[100%]">
|
<div className="w-[100%] relative">
|
||||||
<TabMenu
|
<TabMenu
|
||||||
className="w-full bg-transparent border-none"
|
className="w-full bg-transparent border-none"
|
||||||
model={items}
|
model={items}
|
||||||
@ -20,6 +21,32 @@ export default function MenuTab({ items, activeIndex, onTabChange }) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Sidebar toggle button positioned at the far right - hidden on mobile */}
|
||||||
|
{!isMobileView && (
|
||||||
|
<div className="absolute right-2 top-0 flex items-center h-full">
|
||||||
|
<Button
|
||||||
|
icon={sidebarVisible
|
||||||
|
? "pi pi-times"
|
||||||
|
: "pi pi-chevron-left"}
|
||||||
|
onClick={onToggleSidebar}
|
||||||
|
className="p-button-rounded text-blue-400 hover:text-blue-500 hover:bg-blue-100/10"
|
||||||
|
style={{
|
||||||
|
width: '2.5rem',
|
||||||
|
height: '2.5rem',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}
|
||||||
|
tooltip={sidebarVisible ? "Hide lessons" : "Show lessons"}
|
||||||
|
tooltipOptions={{ position: 'bottom' }}
|
||||||
|
aria-label="Toggle course lessons"
|
||||||
|
pt={{
|
||||||
|
icon: { className: 'font-bold text-lg' }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
|
|||||||
import appConfig from '@/config/appConfig';
|
import appConfig from '@/config/appConfig';
|
||||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||||
import MenuTab from '@/components/menutab/MenuTab';
|
import MenuTab from '@/components/menutab/MenuTab';
|
||||||
|
import { Tag } from 'primereact/tag';
|
||||||
|
|
||||||
const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
|
const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@ -330,6 +331,10 @@ const Course = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleSidebar = () => {
|
||||||
|
setSidebarVisible(!sidebarVisible);
|
||||||
|
};
|
||||||
|
|
||||||
// Map active tab name back to index for MenuTab
|
// Map active tab name back to index for MenuTab
|
||||||
const getActiveTabIndex = () => {
|
const getActiveTabIndex = () => {
|
||||||
const tabMap = ['overview', 'content', 'qa'];
|
const tabMap = ['overview', 'content', 'qa'];
|
||||||
@ -344,11 +349,11 @@ const Course = () => {
|
|||||||
const getTabItems = () => {
|
const getTabItems = () => {
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
label: 'Course Overview',
|
label: 'Overview',
|
||||||
icon: 'pi pi-home',
|
icon: 'pi pi-home',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Lesson Content',
|
label: 'Content',
|
||||||
icon: 'pi pi-book',
|
icon: 'pi pi-book',
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -356,7 +361,7 @@ const Course = () => {
|
|||||||
// Add lessons tab only on mobile
|
// Add lessons tab only on mobile
|
||||||
if (isMobileView) {
|
if (isMobileView) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Course Lessons',
|
label: 'Lessons',
|
||||||
icon: 'pi pi-list',
|
icon: 'pi pi-list',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -385,8 +390,32 @@ const Course = () => {
|
|||||||
|
|
||||||
// Render Course Overview section
|
// Render Course Overview section
|
||||||
const renderOverviewSection = () => {
|
const renderOverviewSection = () => {
|
||||||
|
// Get isCompleted status for use in the component
|
||||||
|
const isCompleted = completedLessons.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-900 rounded-lg border border-gray-800 shadow-md p-6">
|
<div className={`bg-gray-900 rounded-lg border border-gray-800 shadow-md ${isMobileView ? 'p-4' : 'p-6'}`}>
|
||||||
|
{isMobileView && course && (
|
||||||
|
<div className="mb-2">
|
||||||
|
{/* Completed tag above image in mobile view */}
|
||||||
|
{isCompleted && (
|
||||||
|
<div className="mb-2">
|
||||||
|
<Tag severity="success" value="Completed" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Course image */}
|
||||||
|
{course.image && (
|
||||||
|
<div className="w-full h-48 relative rounded-lg overflow-hidden mb-3">
|
||||||
|
<img
|
||||||
|
src={course.image}
|
||||||
|
alt={course.title}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<CourseDetails
|
<CourseDetails
|
||||||
processedEvent={course}
|
processedEvent={course}
|
||||||
paidCourse={paidCourse}
|
paidCourse={paidCourse}
|
||||||
@ -394,6 +423,8 @@ const Course = () => {
|
|||||||
decryptionPerformed={decryptionPerformed}
|
decryptionPerformed={decryptionPerformed}
|
||||||
handlePaymentSuccess={handlePaymentSuccess}
|
handlePaymentSuccess={handlePaymentSuccess}
|
||||||
handlePaymentError={handlePaymentError}
|
handlePaymentError={handlePaymentError}
|
||||||
|
isMobileView={isMobileView}
|
||||||
|
showCompletedTag={!isMobileView}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -461,16 +492,17 @@ const Course = () => {
|
|||||||
items={getTabItems()}
|
items={getTabItems()}
|
||||||
activeIndex={getActiveTabIndex()}
|
activeIndex={getActiveTabIndex()}
|
||||||
onTabChange={(index) => toggleTab(index)}
|
onTabChange={(index) => toggleTab(index)}
|
||||||
|
sidebarVisible={sidebarVisible}
|
||||||
|
onToggleSidebar={handleToggleSidebar}
|
||||||
|
isMobileView={isMobileView}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start mt-4">
|
{/* Revised layout structure to prevent content flexing */}
|
||||||
{/* Main content area - keep existing implementation */}
|
<div className="relative mt-4">
|
||||||
<div
|
{/* Main content area with fixed width */}
|
||||||
className={`transition-all duration-300 ${
|
<div className={`transition-all duration-500 ease-in-out ${isMobileView ? 'w-full' : 'w-full'}`}
|
||||||
isMobileView ? 'w-full' : ((!isMobileView && sidebarVisible) ? 'flex-1' : 'w-full')
|
style={!isMobileView && sidebarVisible ? {paddingRight: '320px'} : {}}>
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{/* Overview tab content */}
|
{/* Overview tab content */}
|
||||||
<div className={`${activeTab === 'overview' ? 'block' : 'hidden'}`}>
|
<div className={`${activeTab === 'overview' ? 'block' : 'hidden'}`}>
|
||||||
{renderOverviewSection()}
|
{renderOverviewSection()}
|
||||||
@ -508,33 +540,60 @@ const Course = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Course Sidebar Component - Always visible on desktop, hidden on mobile unless lessons tab is active */}
|
{/* Course Sidebar - positioned absolutely on desktop when visible */}
|
||||||
<div
|
{!isMobileView ? (
|
||||||
className={`flex-shrink-0 transition-all duration-300 ${
|
<div
|
||||||
(!isMobileView && sidebarVisible) ? 'ml-5 w-auto opacity-100' :
|
className={`transition-all duration-500 ease-in-out ${
|
||||||
|
sidebarVisible ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-full'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0',
|
||||||
|
right: '0',
|
||||||
|
width: '320px',
|
||||||
|
height: '100%',
|
||||||
|
zIndex: 999,
|
||||||
|
overflow: 'visible',
|
||||||
|
pointerEvents: sidebarVisible ? 'auto' : 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CourseSidebar
|
||||||
|
lessons={uniqueLessons}
|
||||||
|
activeIndex={activeIndex}
|
||||||
|
onLessonSelect={handleLessonSelect}
|
||||||
|
completedLessons={completedLessons}
|
||||||
|
isMobileView={isMobileView}
|
||||||
|
sidebarVisible={sidebarVisible}
|
||||||
|
setSidebarVisible={setSidebarVisible}
|
||||||
|
hideToggleButton={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={`flex-shrink-0 transition-all duration-300 z-[999] ${
|
||||||
(isMobileView && activeTab === 'lessons') ? 'ml-0 w-auto opacity-100' :
|
(isMobileView && activeTab === 'lessons') ? 'ml-0 w-auto opacity-100' :
|
||||||
'w-0 ml-0 opacity-0 overflow-hidden'
|
'w-0 ml-0 opacity-0 overflow-hidden'
|
||||||
}`}
|
}`}>
|
||||||
>
|
<CourseSidebar
|
||||||
<CourseSidebar
|
lessons={uniqueLessons}
|
||||||
lessons={uniqueLessons}
|
activeIndex={activeIndex}
|
||||||
activeIndex={activeIndex}
|
onLessonSelect={(index) => {
|
||||||
onLessonSelect={(index) => {
|
handleLessonSelect(index);
|
||||||
handleLessonSelect(index);
|
if (isMobileView) {
|
||||||
if (isMobileView) {
|
toggleTab(getTabItems().findIndex(item => item.label === 'Lesson Content'));
|
||||||
toggleTab(getTabItems().findIndex(item => item.label === 'Lesson Content')); // On mobile, switch to content tab when a lesson is selected
|
}
|
||||||
}
|
}}
|
||||||
}}
|
completedLessons={completedLessons}
|
||||||
completedLessons={completedLessons}
|
isMobileView={isMobileView}
|
||||||
isMobileView={isMobileView}
|
onClose={() => {
|
||||||
onClose={() => {
|
setSidebarVisible(false);
|
||||||
setSidebarVisible(false);
|
setActiveTab('content');
|
||||||
setActiveTab('content');
|
}}
|
||||||
}}
|
sidebarVisible={sidebarVisible}
|
||||||
sidebarVisible={sidebarVisible || !isMobileView} // Always visible on desktop
|
setSidebarVisible={setSidebarVisible}
|
||||||
setSidebarVisible={setSidebarVisible}
|
hideToggleButton={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user