diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx index c3ebb3a0..ecfa804a 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx @@ -29,6 +29,8 @@ const CourseClient = (props: any) => { const router = useRouter() const isMobile = useMediaQuery('(max-width: 768px)') + console.log(course) + function getLearningTags() { if (!course?.learnings) { setLearnings([]) @@ -333,7 +335,7 @@ const CourseClient = (props: any) => { {isMobile && ( -
+
)} diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx index d0c721a4..e3216016 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/page.tsx @@ -72,7 +72,7 @@ const CoursePage = async (params: any) => { // Fetch course metadata once const course_meta = await getCourseMetadata( params.params.courseuuid, - { revalidate: 1800, tags: ['courses'] }, + { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null ) diff --git a/apps/web/components/Objects/Courses/CourseActions/CourseActionsMobile.tsx b/apps/web/components/Objects/Courses/CourseActions/CourseActionsMobile.tsx index 702b0ec4..c559f377 100644 --- a/apps/web/components/Objects/Courses/CourseActions/CourseActionsMobile.tsx +++ b/apps/web/components/Objects/Courses/CourseActions/CourseActionsMobile.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/navigation' import { useLHSession } from '@components/Contexts/LHSessionContext' import { getUriWithoutOrg, getUriWithOrg } from '@services/config/config' import { getProductsByCourse } from '@services/payments/products' -import { LogIn, LogOut, ShoppingCart } from 'lucide-react' +import { LogIn, LogOut, ShoppingCart, AlertCircle } from 'lucide-react' import Modal from '@components/Objects/StyledElements/Modal/Modal' import CoursePaidOptions from './CoursePaidOptions' import { checkPaidAccess } from '@services/payments/payments' @@ -13,11 +13,15 @@ import UserAvatar from '../../UserAvatar' import { getUserAvatarMediaDirectory } from '@services/media/media' interface Author { - user_uuid: string - avatar_image: string - first_name: string - last_name: string - username: string + user: { + user_uuid: string + avatar_image: string + first_name: string + last_name: string + username: string + } + authorship: 'CREATOR' | 'CONTRIBUTOR' | 'MAINTAINER' | 'REPORTER' + authorship_status: 'ACTIVE' | 'INACTIVE' | 'PENDING' } interface CourseRun { @@ -49,11 +53,81 @@ interface CourseActionsMobileProps { } } +// Component for displaying multiple authors +const MultipleAuthors = ({ authors }: { authors: Author[] }) => { + const displayedAvatars = authors.slice(0, 3) + const remainingCount = Math.max(0, authors.length - 3) + + // Avatar size for mobile + const avatarSize = 36 + const borderSize = "border-2" + + return ( +
+
+ {displayedAvatars.map((author, index) => ( +
+ +
+ ))} + {remainingCount > 0 && ( +
+
+ +{remainingCount} +
+
+ )} +
+ +
+ + {authors.length > 1 ? 'Authors' : 'Author'} + + {authors.length === 1 ? ( + + {authors[0].user.first_name && authors[0].user.last_name + ? `${authors[0].user.first_name} ${authors[0].user.last_name}` + : `@${authors[0].user.username}`} + + ) : ( + + {authors[0].user.first_name && authors[0].user.last_name + ? `${authors[0].user.first_name} ${authors[0].user.last_name}` + : `@${authors[0].user.username}`} + {authors.length > 1 && ` & ${authors.length - 1} more`} + + )} +
+
+ ) +} + const CourseActionsMobile = ({ courseuuid, orgslug, course }: CourseActionsMobileProps) => { const router = useRouter() const session = useLHSession() as any const [linkedProducts, setLinkedProducts] = useState([]) const [isLoading, setIsLoading] = useState(true) + const [isActionLoading, setIsActionLoading] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false) const [hasAccess, setHasAccess] = useState(null) @@ -107,106 +181,141 @@ const CourseActionsMobile = ({ courseuuid, orgslug, course }: CourseActionsMobil return } - if (isStarted) { - await removeCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token) - await revalidateTags(['courses'], orgslug) - router.refresh() - } else { - await startCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token) - await revalidateTags(['courses'], orgslug) - - // Get the first activity from the first chapter - const firstChapter = course.chapters?.[0] - const firstActivity = firstChapter?.activities?.[0] - - if (firstActivity) { - // Redirect to the first activity - router.push( - getUriWithOrg(orgslug, '') + - `/course/${courseuuid}/activity/${firstActivity.activity_uuid.replace('activity_', '')}` - ) - } else { + setIsActionLoading(true) + try { + if (isStarted) { + await removeCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token) + await revalidateTags(['courses'], orgslug) router.refresh() + } else { + await startCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token) + await revalidateTags(['courses'], orgslug) + + // Get the first activity from the first chapter + const firstChapter = course.chapters?.[0] + const firstActivity = firstChapter?.activities?.[0] + + if (firstActivity) { + // Redirect to the first activity + router.push( + getUriWithOrg(orgslug, '') + + `/course/${courseuuid}/activity/${firstActivity.activity_uuid.replace('activity_', '')}` + ) + } else { + router.refresh() + } } + } catch (error) { + console.error('Failed to perform course action:', error) + } finally { + setIsActionLoading(false) } } if (isLoading) { - return
+ return
} - const author = course.authors[0] - const authorName = author.first_name && author.last_name - ? `${author.first_name} ${author.last_name}` - : `@${author.username}` + // Filter active authors and sort by role priority + const sortedAuthors = [...course.authors] + .filter(author => author.authorship_status === 'ACTIVE') + .sort((a, b) => { + const rolePriority: Record = { + 'CREATOR': 0, + 'MAINTAINER': 1, + 'CONTRIBUTOR': 2, + 'REPORTER': 3 + }; + return rolePriority[a.authorship] - rolePriority[b.authorship]; + }); return ( -
-
- -
- Author - {authorName} -
-
- -
+
+
+ + {linkedProducts.length > 0 ? ( - hasAccess ? ( - - ) : ( - <> - } - dialogTitle="Purchase Course" - dialogDescription="Select a payment option to access this course" - minWidth="sm" - /> +
+ {hasAccess ? ( +
+
+
+ You Own This Course +
+
+ ) : ( +
+
+ + Paid Course +
+
+ )} + + {hasAccess ? ( - - ) + ) : ( + <> + } + dialogTitle="Purchase Course" + dialogDescription="Select a payment option to access this course" + minWidth="sm" + /> + + + )} +
) : (