diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx index b899fab3..8c8546d4 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx @@ -5,7 +5,7 @@ import { BookOpenCheck, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRig import { markActivityAsComplete, unmarkActivityAsComplete } from '@services/courses/activity' import { usePathname, useRouter } from 'next/navigation' import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement' -import { getCourseThumbnailMediaDirectory } from '@services/media/media' +import { getCourseThumbnailMediaDirectory, getUserAvatarMediaDirectory } from '@services/media/media' import { useOrg } from '@components/Contexts/OrgContext' import { CourseProvider } from '@components/Contexts/CourseContext' import { useLHSession } from '@components/Contexts/LHSessionContext' @@ -31,6 +31,7 @@ import MiniInfoTooltip from '@components/Objects/MiniInfoTooltip' import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper' import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators' import { revalidateTags } from '@services/utils/ts/requests' +import UserAvatar from '@components/Objects/UserAvatar' // Lazy load heavy components const Canva = lazy(() => import('@components/Objects/Activities/DynamicCanva/DynamicCanva')) @@ -132,6 +133,26 @@ function ActivityActions({ activity, activityid, course, orgslug, assignment, sh ); } +function getRelativeTime(date: Date): string { + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const seconds = Math.floor(diff / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + const weeks = Math.floor(days / 7); + const months = Math.floor(days / 30); + const years = Math.floor(days / 365); + + if (years > 0) return `${years} year${years > 1 ? 's' : ''} ago`; + if (months > 0) return `${months} month${months > 1 ? 's' : ''} ago`; + if (weeks > 0) return `${weeks} week${weeks > 1 ? 's' : ''} ago`; + if (days > 0) return `${days} day${days > 1 ? 's' : ''} ago`; + if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago`; + if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; + return 'just now'; +} + function ActivityClient(props: ActivityClientProps) { const activityid = props.activityid const courseuuid = props.courseuuid @@ -508,7 +529,7 @@ function ActivityClient(props: ActivityClientProps) {

Course

-

+

{course.name}

@@ -550,6 +571,80 @@ function ActivityClient(props: ActivityClientProps) {

{activity.name}

+ {/* Authors and Dates Section */} +
+ {/* Avatars */} + {course.authors && course.authors.length > 0 && ( +
+ {course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').slice(0, 3).map((author: any, idx: number) => ( +
+ +
+ ))} + {course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').length > 3 && ( +
+ +{course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').length - 3} +
+ )} +
+ )} + {/* Author names */} + {course.authors && course.authors.length > 0 && ( +
+ {course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').length > 1 && ( + Co-created by + )} + {course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').slice(0, 2).map((author: any, idx: number, arr: any[]) => ( + + {author.user.first_name && author.user.last_name + ? `${author.user.first_name} ${author.user.last_name}` + : `@${author.user.username}`} + {idx === 0 && arr.length > 1 ? ' & ' : ''} + + ))} + {course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').length > 2 && ( + + {course.authors + .filter((a: any) => a.authorship_status === 'ACTIVE') + .slice(2) + .map((author: any) => ( +
+ {author.user.first_name && author.user.last_name + ? `${author.user.first_name} ${author.user.last_name}` + : `@${author.user.username}`} +
+ ))} +
+ } + > +
+ +{course.authors.filter((a: any) => a.authorship_status === 'ACTIVE').length - 2} +
+ + )} +
+ )} + {/* Dates */} +
+ + Created on {new Date(course.creation_date).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })} + + + + Last updated {getRelativeTime(new Date(course.updated_at || course.last_updated || course.creation_date))} + +
+
@@ -598,7 +693,7 @@ function ActivityClient(props: ActivityClientProps) {
{isOpen && ( -
+

Course Content