start/continue lesson button on course overview

This commit is contained in:
austinkelsay 2025-05-14 12:29:13 -05:00
parent d906625168
commit 724e7aa642
No known key found for this signature in database
GPG Key ID: 5A763922E5BA08EE
6 changed files with 148 additions and 23 deletions

View File

@ -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 (

View File

@ -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>
</>

View File

@ -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>
</>

View File

@ -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">

View File

@ -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>
</>
);
};

View File

@ -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>