mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-03 07:42:03 +00:00
start/continue lesson button on course overview
This commit is contained in:
parent
d906625168
commit
724e7aa642
@ -31,6 +31,9 @@ export default function CourseDetails({
|
||||
handlePaymentError,
|
||||
isMobileView,
|
||||
showCompletedTag = true,
|
||||
completedLessons,
|
||||
onLessonSelect,
|
||||
toggleToContentTab
|
||||
}) {
|
||||
const [zapAmount, setZapAmount] = useState(0);
|
||||
const [author, setAuthor] = useState(null);
|
||||
@ -213,7 +216,10 @@ export default function CourseDetails({
|
||||
returnImageProxy,
|
||||
renderPaymentMessage,
|
||||
isCompleted,
|
||||
showCompletedTag
|
||||
showCompletedTag,
|
||||
completedLessons,
|
||||
onLessonSelect,
|
||||
toggleToContentTab
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -4,6 +4,7 @@ import { Tag } from 'primereact/tag';
|
||||
import ZapDisplay from '@/components/zaps/ZapDisplay';
|
||||
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
|
||||
import { Divider } from 'primereact/divider';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
|
||||
export default function DesktopCourseDetails({
|
||||
processedEvent,
|
||||
@ -17,9 +18,46 @@ export default function DesktopCourseDetails({
|
||||
returnImageProxy,
|
||||
renderPaymentMessage,
|
||||
isCompleted,
|
||||
showCompletedTag
|
||||
showCompletedTag,
|
||||
completedLessons,
|
||||
onLessonSelect,
|
||||
toggleToContentTab
|
||||
}) {
|
||||
|
||||
// Calculate next lesson to start/continue
|
||||
const getNextLessonIndex = () => {
|
||||
if (!lessons || lessons.length === 0) return 0;
|
||||
|
||||
// If no completed lessons, start with the first one
|
||||
if (completedLessons.length === 0) return 0;
|
||||
|
||||
// Find the highest completed lesson index
|
||||
const lessonIndices = lessons.map((lesson, index) => {
|
||||
return { id: lesson.id, index };
|
||||
});
|
||||
|
||||
// Get indices of completed lessons
|
||||
const completedIndices = lessonIndices
|
||||
.filter(item => completedLessons.includes(item.id))
|
||||
.map(item => item.index);
|
||||
|
||||
// Get the max completed index
|
||||
const maxCompletedIndex = Math.max(...completedIndices);
|
||||
|
||||
// Return the next lesson index (or the last one if all completed)
|
||||
return Math.min(maxCompletedIndex + 1, lessons.length - 1);
|
||||
};
|
||||
|
||||
const nextLessonIndex = getNextLessonIndex();
|
||||
const buttonLabel = completedLessons.length === 0
|
||||
? "Start Lesson 1"
|
||||
: `Continue to Lesson ${nextLessonIndex + 1}`;
|
||||
|
||||
const handleContinueClick = () => {
|
||||
onLessonSelect(nextLessonIndex);
|
||||
toggleToContentTab();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Header with course image, title and options */}
|
||||
@ -33,7 +71,7 @@ export default function DesktopCourseDetails({
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Title and options */}
|
||||
<div className="flex-1">
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
@ -56,7 +94,7 @@ export default function DesktopCourseDetails({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Topics/tags */}
|
||||
<div className="flex flex-wrap gap-2 mb-3">
|
||||
{processedEvent.topics &&
|
||||
@ -65,7 +103,7 @@ export default function DesktopCourseDetails({
|
||||
<Tag className="text-white" key={index} value={topic}></Tag>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
{/* Author info */}
|
||||
<div className="flex items-center">
|
||||
<Image
|
||||
@ -89,9 +127,9 @@ export default function DesktopCourseDetails({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<Divider className="my-4" />
|
||||
|
||||
|
||||
{/* Course details */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Left column: Description */}
|
||||
@ -99,19 +137,19 @@ export default function DesktopCourseDetails({
|
||||
<h2 className="text-xl font-semibold mb-3 text-white">About This Course</h2>
|
||||
<div className="text-gray-300 mb-4">
|
||||
{processedEvent?.description?.split('\n')
|
||||
.map((line, index) => <p key={index} className="mb-2">{line}</p>)}
|
||||
.map((line, index) => <p key={index} className="mb-2">{line}</p>)}
|
||||
</div>
|
||||
|
||||
|
||||
{/* Payment section */}
|
||||
<div className="mt-4">
|
||||
{renderPaymentMessage()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Right column: Course details */}
|
||||
<div className="bg-gray-800 rounded-lg h-fit p-4">
|
||||
<h2 className="text-xl font-semibold mb-3 text-white">Course Information</h2>
|
||||
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-gray-300 font-medium mb-2">Course Content</h3>
|
||||
@ -128,7 +166,7 @@ export default function DesktopCourseDetails({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{processedEvent.published && (
|
||||
<div>
|
||||
<h3 className="text-gray-300 font-medium mb-2">Details</h3>
|
||||
@ -141,6 +179,18 @@ export default function DesktopCourseDetails({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Continue/Start button */}
|
||||
{lessons && lessons.length > 0 && !isCompleted && (
|
||||
<div className="mt-4 flex justify-start">
|
||||
<GenericButton
|
||||
label={buttonLabel}
|
||||
icon="pi pi-play"
|
||||
onClick={handleContinueClick}
|
||||
outlined={true}
|
||||
disabled={paidCourse && !decryptionPerformed}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -4,6 +4,7 @@ import { Tag } from 'primereact/tag';
|
||||
import ZapDisplay from '@/components/zaps/ZapDisplay';
|
||||
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
|
||||
import { Divider } from 'primereact/divider';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
|
||||
export default function MobileCourseDetails({
|
||||
processedEvent,
|
||||
@ -17,8 +18,45 @@ export default function MobileCourseDetails({
|
||||
returnImageProxy,
|
||||
renderPaymentMessage,
|
||||
isCompleted,
|
||||
showCompletedTag
|
||||
showCompletedTag,
|
||||
completedLessons,
|
||||
onLessonSelect,
|
||||
toggleToContentTab
|
||||
}) {
|
||||
// Calculate next lesson to start/continue
|
||||
const getNextLessonIndex = () => {
|
||||
if (!lessons || lessons.length === 0) return 0;
|
||||
|
||||
// If no completed lessons, start with the first one
|
||||
if (completedLessons.length === 0) return 0;
|
||||
|
||||
// Find the highest completed lesson index
|
||||
const lessonIndices = lessons.map((lesson, index) => {
|
||||
return { id: lesson.id, index };
|
||||
});
|
||||
|
||||
// Get indices of completed lessons
|
||||
const completedIndices = lessonIndices
|
||||
.filter(item => completedLessons.includes(item.id))
|
||||
.map(item => item.index);
|
||||
|
||||
// Get the max completed index
|
||||
const maxCompletedIndex = Math.max(...completedIndices);
|
||||
|
||||
// Return the next lesson index (or the last one if all completed)
|
||||
return Math.min(maxCompletedIndex + 1, lessons.length - 1);
|
||||
};
|
||||
|
||||
const nextLessonIndex = getNextLessonIndex();
|
||||
const buttonLabel = completedLessons.length === 0
|
||||
? "Start Lesson 1"
|
||||
: `Continue to Lesson ${nextLessonIndex + 1}`;
|
||||
|
||||
const handleContinueClick = () => {
|
||||
onLessonSelect(nextLessonIndex);
|
||||
toggleToContentTab();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Mobile-specific layout */}
|
||||
@ -128,6 +166,19 @@ export default function MobileCourseDetails({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Continue/Start button */}
|
||||
{lessons && lessons.length > 0 && !isCompleted && (
|
||||
<div className="mt-4 flex justify-start">
|
||||
<GenericButton
|
||||
label={buttonLabel}
|
||||
icon="pi pi-play"
|
||||
onClick={handleContinueClick}
|
||||
outlined={true}
|
||||
disabled={paidCourse && !decryptionPerformed}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -120,7 +120,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (isPaid && !decryptionPerformed) {
|
||||
return (
|
||||
<div className="w-full p-8 rounded-lg flex flex-col items-center justify-center">
|
||||
@ -133,7 +133,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return <MarkdownDisplay content={lesson.content} className="p-4 rounded-lg w-full" />;
|
||||
};
|
||||
|
||||
@ -141,13 +141,15 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
||||
<div className="w-full">
|
||||
<Toast ref={toastRef} />
|
||||
<div className="relative w-[80%] h-[200px] mx-auto mb-24">
|
||||
<Image
|
||||
alt="lesson background image"
|
||||
src={returnImageProxy(lesson.image)}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black bg-opacity-20"></div>
|
||||
<div className="relative w-full h-full mt-2 rounded-lg">
|
||||
<Image
|
||||
alt="lesson background image"
|
||||
src={returnImageProxy(lesson.image)}
|
||||
fill
|
||||
className="object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-gray-800 bg-opacity-20"></div>
|
||||
</div>
|
||||
<div className="w-full mx-auto px-4 py-8 -mt-32 relative z-10">
|
||||
<div className="mb-8 bg-gray-800/70 rounded-lg p-4">
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import CourseDetails from '../details/CourseDetails';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
|
||||
/**
|
||||
* Component to display course overview with details
|
||||
@ -13,12 +14,15 @@ const CourseOverview = ({
|
||||
handlePaymentSuccess,
|
||||
handlePaymentError,
|
||||
isMobileView,
|
||||
completedLessons
|
||||
completedLessons,
|
||||
onLessonSelect,
|
||||
toggleToContentTab
|
||||
}) => {
|
||||
// Determine if course is completed
|
||||
const isCompleted = lessons && lessons.length > 0 && completedLessons.length === lessons.length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`bg-gray-800 rounded-lg border border-gray-800 shadow-md ${isMobileView ? 'p-4' : 'p-6'}`}>
|
||||
{isMobileView && course && (
|
||||
<div className="mb-2">
|
||||
@ -50,8 +54,12 @@ const CourseOverview = ({
|
||||
handlePaymentError={handlePaymentError}
|
||||
isMobileView={isMobileView}
|
||||
showCompletedTag={!isMobileView}
|
||||
completedLessons={completedLessons}
|
||||
onLessonSelect={onLessonSelect}
|
||||
toggleToContentTab={toggleToContentTab}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -242,6 +242,14 @@ const Course = () => {
|
||||
handlePaymentError={handlePaymentError}
|
||||
isMobileView={isMobileView}
|
||||
completedLessons={completedLessons}
|
||||
onLessonSelect={(index) => {
|
||||
handleLessonSelect(index);
|
||||
// Update URL with active parameter
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('active', index);
|
||||
router.push(url, undefined, { shallow: true });
|
||||
}}
|
||||
toggleToContentTab={() => toggleTab(1)} // Assuming content tab is at index 1
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user