chore: merge with dev

This commit is contained in:
Chris Holland 2024-10-23 20:06:17 -07:00
parent b678ac86e8
commit 3430cfdb3a
No known key found for this signature in database
GPG key ID: 68B0A864B1B0A0D2
34 changed files with 3515 additions and 2600 deletions

View file

@ -74,87 +74,84 @@ const CollectionsPage = async (params: any) => {
return (
<GeneralWrapperStyled>
<div className="flex justify-between">
<TypeOfContentTitle title="Collections" type="col" />
<AuthenticatedClientElement
ressourceType="collections"
action="create"
checkMethod="roles"
orgId={org_id}
>
<Link
className="flex justify-center"
href={getUriWithOrg(orgslug, '/collections/new')}
<div className="flex flex-col space-y-4 mb-8">
<div className="flex items-center justify-between">
<TypeOfContentTitle title="Collections" type="col" />
<AuthenticatedClientElement
ressourceType="collections"
action="create"
checkMethod="roles"
orgId={org_id}
>
<NewCollectionButton />
</Link>
</AuthenticatedClientElement>
</div>
<div className="home_collections flex flex-wrap">
{collections.map((collection: any) => (
<div
className="flex flex-col py-1 px-3"
key={collection.collection_uuid}
>
<CollectionThumbnail
collection={collection}
orgslug={orgslug}
org_id={org_id}
/>
</div>
))}
{collections.length == 0 && (
<div className="flex mx-auto h-[400px]">
<div className="flex flex-col justify-center text-center items-center space-y-5">
<div className="mx-auto">
<svg
width="120"
height="120"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
opacity="0.51"
x="10"
y="10"
width="275"
height="275"
rx="75"
stroke="#4B5564"
strokeOpacity="0.15"
strokeWidth="20"
/>
<path
d="M135.8 200.8V130L122.2 114.6L135.8 110.4V102.8L122.2 87.4L159.8 76V200.8L174.6 218H121L135.8 200.8Z"
fill="#4B5564"
fillOpacity="0.08"
/>
</svg>
</div>
<div className="space-y-0">
<h1 className="text-3xl font-bold text-gray-600">
<Link href={getUriWithOrg(orgslug, '/collections/new')}>
<NewCollectionButton />
</Link>
</AuthenticatedClientElement>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{collections.map((collection: any) => (
<div key={collection.collection_uuid} className="p-3">
<CollectionThumbnail
collection={collection}
orgslug={orgslug}
org_id={org_id}
/>
</div>
))}
{collections.length === 0 && (
<div className="col-span-full flex justify-center items-center py-8">
<div className="text-center">
<div className="mb-4">
<svg
width="50"
height="50"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mx-auto"
>
<rect
opacity="0.51"
x="10"
y="10"
width="275"
height="275"
rx="75"
stroke="#4B5564"
strokeOpacity="0.15"
strokeWidth="20"
/>
<path
d="M135.8 200.8V130L122.2 114.6L135.8 110.4V102.8L122.2 87.4L159.8 76V200.8L174.6 218H121L135.8 200.8Z"
fill="#4B5564"
fillOpacity="0.08"
/>
</svg>
</div>
<h1 className="text-xl font-bold text-gray-600 mb-2">
No collections yet
</h1>
<p className="text-lg text-gray-400">
<p className="text-md text-gray-400">
<ContentPlaceHolderIfUserIsNotAdmin
text="Create a collection to add content"
/>
</p>
<div className="mt-4">
<AuthenticatedClientElement
checkMethod="roles"
ressourceType="collections"
action="create"
orgId={org_id}
>
<Link href={getUriWithOrg(orgslug, '/collections/new')}>
<NewCollectionButton />
</Link>
</AuthenticatedClientElement>
</div>
</div>
<AuthenticatedClientElement
checkMethod="roles"
ressourceType="collections"
action="create"
orgId={org_id}
>
<Link href={getUriWithOrg(orgslug, '/collections/new')}>
<NewCollectionButton />
</Link>
</AuthenticatedClientElement>
</div>
</div>
)}
)}
</div>
</div>
</GeneralWrapperStyled>
)

View file

@ -25,6 +25,7 @@ import AssignmentSubmissionProvider, { useAssignmentSubmission } from '@compone
import toast from 'react-hot-toast'
import { mutate } from 'swr'
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
import { useMediaQuery } from 'usehooks-ts'
interface ActivityClientProps {
activityid: string
@ -47,6 +48,7 @@ function ActivityClient(props: ActivityClientProps) {
const [bgColor, setBgColor] = React.useState('bg-white')
const [assignment, setAssignment] = React.useState(null) as any;
const [markStatusButtonActive, setMarkStatusButtonActive] = React.useState(false);
function getChapterNameByActivityId(course: any, activity_id: any) {
for (let i = 0; i < course.chapters.length; i++) {
@ -223,7 +225,7 @@ export function MarkStatus(props: {
}) {
const router = useRouter()
const session = useLHSession() as any;
const isMobile = useMediaQuery('(max-width: 768px)')
async function markActivityAsCompleteFront() {
const trail = await markActivityAsComplete(
props.orgslug,
@ -263,7 +265,7 @@ export function MarkStatus(props: {
<i>
<Check size={17}></Check>
</i>{' '}
<i className="not-italic text-xs font-bold">Mark as complete</i>
{!isMobile && <i className="not-italic text-xs font-bold">Mark as complete</i>}
</div>
)}
</>

View file

@ -18,6 +18,7 @@ import UserAvatar from '@components/Objects/UserAvatar'
import CourseUpdates from '@components/Objects/CourseUpdates/CourseUpdates'
import { CourseProvider } from '@components/Contexts/CourseContext'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useMediaQuery } from 'usehooks-ts'
const CourseClient = (props: any) => {
const [user, setUser] = useState<any>({})
@ -28,6 +29,7 @@ const CourseClient = (props: any) => {
const course = props.course
const org = useOrg() as any
const router = useRouter()
const isMobile = useMediaQuery('(max-width: 768px)')
function getLearningTags() {
// create array of learnings from a string object (comma separated)
@ -72,21 +74,21 @@ const CourseClient = (props: any) => {
<PageLoading></PageLoading>
) : (
<GeneralWrapperStyled>
<div className="pb-3 flex justify-between items-center">
<div className="pb-3 flex flex-col md:flex-row justify-between items-start md:items-center">
<div>
<p className="text-md font-bold text-gray-400 pb-2">Course</p>
<h1 className="text-3xl -mt-3 font-bold">{course.name}</h1>
<h1 className="text-3xl md:text-3xl -mt-3 font-bold">{course.name}</h1>
</div>
<div>
<CourseProvider courseuuid={course.course_uuid}>
<div className="mt-4 md:mt-0">
{!isMobile && <CourseProvider courseuuid={course.course_uuid}>
<CourseUpdates />
</CourseProvider>
</CourseProvider>}
</div>
</div>
{props.course?.thumbnail_image && org ? (
<div
className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-auto h-[400px] bg-cover bg-center mb-4"
className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-auto h-[200px] md:h-[400px] bg-cover bg-center mb-4"
style={{
backgroundImage: `url(${getCourseThumbnailMediaDirectory(
org?.org_uuid,
@ -111,7 +113,7 @@ const CourseClient = (props: any) => {
course={course}
/>
<div className="flex flex-row pt-10">
<div className="flex flex-col md:flex-row pt-10">
<div className="course_metadata_left grow space-y-2">
<h2 className="py-3 text-2xl font-bold">Description</h2>
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden">
@ -141,7 +143,7 @@ const CourseClient = (props: any) => {
</div>
)}
<h2 className="py-3 text-2xl font-bold">Course Lessons</h2>
<h2 className="py-3 text-xl md:text-2xl font-bold">Course Lessons</h2>
<div className="bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden">
{course.chapters.map((chapter: any) => {
return (
@ -303,20 +305,20 @@ const CourseClient = (props: any) => {
})}
</div>
</div>
<div className="course_metadata_right space-y-3 w-72 antialiased flex flex-col ml-10 h-fit p-3 py-5 bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden">
<div className="course_metadata_right space-y-3 w-full md:w-72 antialiased flex flex-col md:ml-10 h-fit p-3 py-5 bg-white shadow-md shadow-gray-300/25 outline outline-1 outline-neutral-200/40 rounded-lg overflow-hidden mt-6 md:mt-0">
{user && (
<div className="flex flex-col mx-auto space-y-3 px-2 py-2 items-center">
<div className="flex flex-row md:flex-col mx-auto space-y-0 md:space-y-3 space-x-4 md:space-x-0 px-2 py-2 items-center">
<UserAvatar
border="border-8"
avatar_url={course.authors[0].avatar_image ? getUserAvatarMediaDirectory(course.authors[0].user_uuid, course.authors[0].avatar_image) : ''}
predefined_avatar={course.authors[0].avatar_image ? undefined : 'empty'}
width={100}
width={isMobile ? 60 : 100}
/>
<div className="-space-y-2 ">
<div className="md:-space-y-2">
<div className="text-[12px] text-neutral-400 font-semibold">
Author
</div>
<div className="text-xl font-bold text-neutral-800">
<div className="text-lg md:text-xl font-bold text-neutral-800">
{course.authors[0].first_name &&
course.authors[0].last_name && (
<div className="flex space-x-2 items-center">
@ -344,14 +346,14 @@ const CourseClient = (props: any) => {
{isCourseStarted() ? (
<button
className="py-2 px-5 mx-auto rounded-xl text-white font-bold h-12 w-[200px] drop-shadow-md bg-red-600 hover:bg-red-700 hover:cursor-pointer"
className="py-2 px-5 mx-auto rounded-xl text-white font-bold h-12 w-full md:w-[200px] drop-shadow-md bg-red-600 hover:bg-red-700 hover:cursor-pointer"
onClick={quitCourse}
>
Quit Course
</button>
) : (
<button
className="py-2 px-5 mx-auto rounded-xl text-white font-bold h-12 w-[200px] drop-shadow-md bg-black hover:bg-gray-900 hover:cursor-pointer"
className="py-2 px-5 mx-auto rounded-xl text-white font-bold h-12 w-full md:w-[200px] drop-shadow-md bg-black hover:bg-gray-900 hover:cursor-pointer"
onClick={startCourseUI}
>
Start Course

View file

@ -29,110 +29,102 @@ function Courses(props: CourseProps) {
}
return (
<div>
<div className="w-full">
<GeneralWrapperStyled>
<div className="flex flex-wrap justify-between">
<TypeOfContentTitle title="Courses" type="cou" />
<AuthenticatedClientElement
checkMethod="roles"
action="create"
ressourceType="courses"
orgId={props.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
></CreateCourseModal>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
<div className="flex flex-col space-y-2 mb-2">
<div className="flex items-center justify-between">
<TypeOfContentTitle title="Courses" type="cou" />
<AuthenticatedClientElement
checkMethod="roles"
action="create"
ressourceType="courses"
orgId={props.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
/>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
<div className="flex flex-wrap">
{courses.map((course: any) => (
<div className="px-3" key={course.course_uuid}>
<CourseThumbnail course={course} orgslug={orgslug} />
</div>
))}
{courses.length == 0 && (
<div className="flex mx-auto h-[400px]">
<div className="flex flex-col justify-center text-center items-center space-y-5">
<div className="mx-auto">
<svg
width="120"
height="120"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
opacity="0.51"
x="10"
y="10"
width="275"
height="275"
rx="75"
stroke="#4B5564"
strokeOpacity="0.15"
strokeWidth="20"
/>
<path
d="M135.8 200.8V130L122.2 114.6L135.8 110.4V102.8L122.2 87.4L159.8 76V200.8L174.6 218H121L135.8 200.8Z"
fill="#4B5564"
fillOpacity="0.08"
/>
</svg>
</div>
<div className="space-y-0">
<h1 className="text-3xl font-bold text-gray-600">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{courses.map((course: any) => (
<div key={course.course_uuid} className="p-3">
<CourseThumbnail course={course} orgslug={orgslug} />
</div>
))}
{courses.length === 0 && (
<div className="col-span-full flex justify-center items-center py-8">
<div className="text-center">
<div className="mb-4">
<svg
width="50"
height="50"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mx-auto"
>
{/* ... SVG content ... */}
</svg>
</div>
<h1 className="text-xl font-bold text-gray-600 mb-2">
No courses yet
</h1>
{isUserAdmin ? (<p className="text-lg text-gray-400">
Create a course to add content
</p>) : (<p className="text-lg text-gray-400">
No courses available yet
</p>)}
<p className="text-md text-gray-400">
{isUserAdmin ? (
"Create a course to add content"
) : (
"No courses available yet"
)}
</p>
{isUserAdmin && (
<div className="mt-4">
<AuthenticatedClientElement
action="create"
ressourceType="courses"
checkMethod="roles"
orgId={props.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
/>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
)}
</div>
<AuthenticatedClientElement
action="create"
ressourceType="courses"
checkMethod="roles"
orgId={props.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
></CreateCourseModal>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
</div>
)}
)}
</div>
</div>
</GeneralWrapperStyled>
</div>

View file

@ -82,48 +82,44 @@ const OrgHomePage = async (params: any) => {
)
return (
<div>
<div className="w-full">
<GeneralWrapperStyled>
{/* Collections */}
<div className="flex items-center ">
<div className="flex grow">
<div className="flex flex-col space-y-4 mb-8">
<div className="flex items-center justify-between">
<TypeOfContentTitle title="Collections" type="col" />
</div>
<AuthenticatedClientElement
checkMethod="roles"
ressourceType="collections"
action="create"
orgId={org_id}
>
<Link href={getUriWithOrg(orgslug, '/collections/new')}>
<NewCollectionButton />
</Link>
</AuthenticatedClientElement>
</div>
<div className="home_collections flex flex-wrap">
{collections.map((collection: any) => (
<div
className="flex flex-col py-3 px-3"
key={collection.collection_id}
<AuthenticatedClientElement
checkMethod="roles"
ressourceType="collections"
action="create"
orgId={org_id}
>
<CollectionThumbnail
collection={collection}
orgslug={orgslug}
org_id={org.org_id}
/>
</div>
))}
{collections.length == 0 && (
<div className="flex mx-auto h-[100px]">
<div className="flex flex-col justify-center text-center items-center space-y-3">
<div className="flex flex-col space-y-3">
<div className="mx-auto">
<Link href={getUriWithOrg(orgslug, '/collections/new')}>
<NewCollectionButton />
</Link>
</AuthenticatedClientElement>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{collections.map((collection: any) => (
<div key={collection.collection_id} className="flex flex-col p-3">
<CollectionThumbnail
collection={collection}
orgslug={orgslug}
org_id={org.org_id}
/>
</div>
))}
{collections.length === 0 && (
<div className="col-span-full flex justify-center items-center py-8">
<div className="text-center">
<div className="mb-4">
<svg
width="50"
height="50"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mx-auto"
>
<rect
opacity="0.51"
@ -143,53 +139,49 @@ const OrgHomePage = async (params: any) => {
/>
</svg>
</div>
<div className="space-y-0">
<h1 className="text-xl font-bold text-gray-600">
No collections yet
</h1>
<p className="text-md text-gray-400">
<ContentPlaceHolderIfUserIsNotAdmin
text="Create collections to group courses together"
/>
</p>
</div>
<h1 className="text-xl font-bold text-gray-600 mb-2">
No collections yet
</h1>
<p className="text-md text-gray-400">
<ContentPlaceHolderIfUserIsNotAdmin
text="Create collections to group courses together"
/>
</p>
</div>
</div>
</div>
)}
)}
</div>
</div>
{/* Courses */}
<div className="h-5"></div>
<div className="flex items-center ">
<div className="flex grow">
<div className="flex flex-col space-y-4">
<div className="flex items-center justify-between">
<TypeOfContentTitle title="Courses" type="cou" />
<AuthenticatedClientElement
ressourceType="courses"
action="create"
checkMethod="roles"
orgId={org_id}
>
<Link href={getUriWithOrg(orgslug, '/courses?new=true')}>
<NewCourseButton />
</Link>
</AuthenticatedClientElement>
</div>
<AuthenticatedClientElement
ressourceType="courses"
action="create"
checkMethod="roles"
orgId={org_id}
>
<Link href={getUriWithOrg(orgslug, '/courses?new=true')}>
<NewCourseButton />
</Link>
</AuthenticatedClientElement>
</div>
<div className="home_courses flex flex-wrap">
{courses.map((course: any) => (
<div className="py-3 px-3" key={course.course_uuid}>
<CourseThumbnail course={course} orgslug={orgslug} />
</div>
))}
{courses.length == 0 && (
<div className="flex mx-auto h-[300px]">
<div className="flex flex-col justify-center text-center items-center space-y-3">
<div className="flex flex-col space-y-3">
<div className="mx-auto">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{courses.map((course: any) => (
<div key={course.course_uuid} className="p-3">
<CourseThumbnail course={course} orgslug={orgslug} />
</div>
))}
{courses.length === 0 && (
<div className="col-span-full flex justify-center items-center py-8">
<div className="text-center">
<div className="mb-4 ">
<svg
width="50"
height="50"
className="mx-auto"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@ -212,18 +204,16 @@ const OrgHomePage = async (params: any) => {
/>
</svg>
</div>
<div className="space-y-0">
<h1 className="text-xl font-bold text-gray-600">
No courses yet
</h1>
<p className="text-md text-gray-400">
<ContentPlaceHolderIfUserIsNotAdmin text='Create courses to add content' />
</p>
</div>
<h1 className="text-xl font-bold text-gray-600 mb-2">
No courses yet
</h1>
<p className="text-md text-gray-400">
<ContentPlaceHolderIfUserIsNotAdmin text='Create courses to add content' />
</p>
</div>
</div>
</div>
)}
)}
</div>
</div>
</GeneralWrapperStyled>
</div>

View file

@ -1,8 +1,10 @@
'use client';
import LeftMenu from '@components/Dashboard/UI/LeftMenu'
import DashLeftMenu from '@components/Dashboard/UI/DashLeftMenu'
import DashMobileMenu from '@components/Dashboard/UI/DashMobileMenu'
import AdminAuthorization from '@components/Security/AdminAuthorization'
import { SessionProvider } from 'next-auth/react'
import React from 'react'
import React, { useState, useEffect } from 'react'
import { useMediaQuery } from 'usehooks-ts';
function ClientAdminLayout({
children,
@ -11,11 +13,17 @@ function ClientAdminLayout({
children: React.ReactNode
params: any
}) {
const isMobile = useMediaQuery('(max-width: 768px)')
return (
<SessionProvider>
<AdminAuthorization authorizationMode="page">
<div className="flex">
<LeftMenu />
<div className="flex flex-col md:flex-row">
{isMobile ? (
<DashMobileMenu />
) : (
<DashLeftMenu />
)}
<div className="flex w-full">{children}</div>
</div>
</AdminAuthorization>
@ -23,4 +31,4 @@ function ClientAdminLayout({
)
}
export default ClientAdminLayout
export default ClientAdminLayout

View file

@ -1,6 +1,6 @@
'use client';
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
import { BookOpen, BookX, EllipsisVertical, Eye, Layers2, UserRoundPen } from 'lucide-react'
import { BookOpen, BookX, EllipsisVertical, Eye, Layers2, Monitor, UserRoundPen } from 'lucide-react'
import React, { useEffect } from 'react'
import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
@ -15,12 +15,29 @@ import { updateActivity } from '@services/courses/activities';
// Lazy Loading
import dynamic from 'next/dynamic';
import AssignmentEditorSubPage from './subpages/AssignmentEditorSubPage';
import { useMediaQuery } from 'usehooks-ts';
const AssignmentSubmissionsSubPage = dynamic(() => import('./subpages/AssignmentSubmissionsSubPage'))
function AssignmentEdit() {
const params = useParams<{ assignmentuuid: string; }>()
const searchParams = useSearchParams()
const [selectedSubPage, setSelectedSubPage] = React.useState(searchParams.get('subpage') || 'editor')
const isMobile = useMediaQuery('(max-width: 767px)')
if (isMobile) {
// TODO: Work on a better mobile experience
return (
<div className="h-screen w-full bg-[#f8f8f8] flex items-center justify-center p-4">
<div className="bg-white p-6 rounded-lg shadow-md text-center">
<h2 className="text-xl font-bold mb-4">Desktop Only</h2>
<Monitor className='mx-auto my-5' size={60} />
<p>This page is only accessible from a desktop device.</p>
<p>Please switch to a desktop to view and manage the assignment.</p>
</div>
</div>
)
}
return (
<div className='flex w-full flex-col'>
<AssignmentProvider assignment_uuid={'assignment_' + params.assignmentuuid}>

View file

@ -46,19 +46,19 @@ function AssignmentsHome() {
return (
<div className='flex w-full'>
<div className='pl-10 mr-10 tracking-tighter flex flex-col space-y-5 w-full'>
<div className='pl-4 sm:pl-10 mr-4 sm:mr-10 tracking-tighter flex flex-col space-y-5 w-full'>
<div className='flex flex-col space-y-2'>
<BreadCrumbs type="assignments" />
<h1 className="pt-3 flex font-bold text-4xl">Assignments</h1>
</div>
<div className='flex flex-col space-y-3 w-full'>
{courseAssignments.map((assignments: any, index: number) => (
<div key={index} className='flex flex-col space-y-2 bg-white nice-shadow p-4 rounded-xl w-full'>
<div key={index} className='flex flex-col space-y-2 bg-white nice-shadow p-3 sm:p-4 rounded-xl w-full'>
<div>
<div className='flex space-x-2 items-center justify-between w-full'>
<div className='flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 items-start sm:items-center justify-between w-full'>
<div className='flex space-x-2 items-center'>
<MiniThumbnail course={courses[index]} />
<div className='flex flex-col font-bold text-lg '>
<div className='flex flex-col font-bold text-lg'>
<p className='bg-gray-200 text-gray-700 px-2 text-xs py-0.5 rounded-full w-fit'>Course</p>
<p>{courses[index].name}</p>
</div>
@ -75,10 +75,9 @@ function AssignmentsHome() {
</Link>
</div>
{assignments && assignments.map((assignment: any) => (
<div key={assignment.assignment_uuid} className='flex mt-3 p-3 rounded flex-row space-x-2 w-full light-shadow justify-between bg-gray-50 items-center'>
<div className='flex flex-row items-center space-x-2 '>
<div key={assignment.assignment_uuid} className='flex mt-3 p-2 sm:p-3 rounded flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2 w-full light-shadow justify-between bg-gray-50 items-start sm:items-center'>
<div className='flex flex-col sm:flex-row items-start sm:items-center space-y-1 sm:space-y-0 sm:space-x-2'>
<div className='flex text-xs font-bold bg-gray-200 text-gray-700 px-2 py-0.5 rounded-full h-fit'>
<p>Assignment</p>
</div>
@ -86,7 +85,6 @@ function AssignmentsHome() {
<div className='flex font-semibold text-gray-600 px-2 py-0.5 rounded outline outline-gray-200/70'>{assignment.description}</div>
</div>
<div className='flex space-x-2 font-bold text-sm items-center'>
<EllipsisVertical className='text-gray-500' size={17} />
<Link
href={{
@ -103,7 +101,6 @@ function AssignmentsHome() {
pathname: getUriWithOrg(org.slug, `/dash/assignments/${removeAssignmentPrefix(assignment.assignment_uuid)}`),
query: { subpage: 'submissions' }
}}
prefetch
className='bg-white rounded-full flex space-x-2 nice-shadow items-center px-3 py-0.5'>
<UserRoundPen size={15} />
@ -124,10 +121,8 @@ function AssignmentsHome() {
</div>
</div>
))}
</div>
</div>
</div>
)
}
@ -172,4 +167,4 @@ const MiniThumbnail = (props: { course: any }) => {
}
export default AssignmentsHome
export default AssignmentsHome

View file

@ -1,12 +1,13 @@
'use client'
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse'
import CourseThumbnail from '@components/Objects/Thumbnails/CourseThumbnail'
import CourseThumbnail, { removeCoursePrefix } from '@components/Objects/Thumbnails/CourseThumbnail'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton'
import Modal from '@components/StyledElements/Modal/Modal'
import { useSearchParams } from 'next/navigation'
import React from 'react'
import useAdminStatus from '@components/Hooks/useAdminStatus'
type CourseProps = {
orgslug: string
@ -20,114 +21,106 @@ function CoursesHome(params: CourseProps) {
const [newCourseModal, setNewCourseModal] = React.useState(isCreatingCourse)
const orgslug = params.orgslug
const courses = params.courses
const isUserAdmin = useAdminStatus() as any
async function closeNewCourseModal() {
setNewCourseModal(false)
}
return (
<div className="h-full w-full bg-[#f8f8f8]">
<div>
<div className="pl-10 mr-10 tracking-tighter">
<BreadCrumbs type="courses" />
<div className="w-100 flex justify-between">
<div className="pt-3 flex font-bold text-4xl">Courses</div>
<AuthenticatedClientElement
checkMethod="roles"
action="create"
ressourceType="courses"
orgId={params.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
></CreateCourseModal>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
<div className="h-full w-full bg-[#f8f8f8] pl-10 pr-10">
<div className="mb-6">
<BreadCrumbs type="courses" />
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mt-4">
<h1 className="text-3xl font-bold mb-4 sm:mb-0">Courses</h1>
<AuthenticatedClientElement
checkMethod="roles"
action="create"
ressourceType="courses"
orgId={params.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
/>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
</div>
<div className="flex flex-wrap mx-8 mt-7">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{courses.map((course: any) => (
<div className="px-3" key={course.course_uuid}>
<CourseThumbnail course={course} orgslug={orgslug} />
<div key={course.course_uuid}>
<CourseThumbnail customLink={`/dash/courses/course/${removeCoursePrefix(course.course_uuid)}/general`} course={course} orgslug={orgslug} />
</div>
))}
{courses.length == 0 && (
<div className="flex mx-auto h-[400px]">
<div className="flex flex-col justify-center text-center items-center space-y-5">
<div className="mx-auto">
{courses.length === 0 && (
<div className="col-span-full flex justify-center items-center py-8">
<div className="text-center">
<div className="mb-4">
<svg
width="120"
height="120"
viewBox="0 0 295 295"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="mx-auto"
>
<rect
opacity="0.51"
x="10"
y="10"
width="275"
height="275"
rx="75"
stroke="#4B5564"
strokeOpacity="0.15"
strokeWidth="20"
/>
<path
d="M135.8 200.8V130L122.2 114.6L135.8 110.4V102.8L122.2 87.4L159.8 76V200.8L174.6 218H121L135.8 200.8Z"
fill="#4B5564"
fillOpacity="0.08"
/>
{/* ... SVG content ... */}
</svg>
</div>
<div className="space-y-0">
<h1 className="text-3xl font-bold text-gray-600">
No courses yet
</h1>
<p className="text-lg text-gray-400">
Create a course to add content
</p>
</div>
<AuthenticatedClientElement
action="create"
ressourceType="courses"
checkMethod="roles"
orgId={params.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
></CreateCourseModal>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
<h2 className="text-2xl font-bold text-gray-600 mb-2">
No courses yet
</h2>
<p className="text-lg text-gray-400">
{isUserAdmin ? (
"Create a course to add content"
) : (
"No courses available yet"
)}
</p>
{isUserAdmin && (
<div className="mt-6">
<AuthenticatedClientElement
action="create"
ressourceType="courses"
checkMethod="roles"
orgId={params.org_id}
>
<Modal
isDialogOpen={newCourseModal}
onOpenChange={setNewCourseModal}
minHeight="md"
dialogContent={
<CreateCourseModal
closeModal={closeNewCourseModal}
orgslug={orgslug}
/>
}
dialogTitle="Create Course"
dialogDescription="Create a new course"
dialogTrigger={
<button>
<NewCourseButton />
</button>
}
/>
</AuthenticatedClientElement>
</div>
)}
</div>
</div>
)}

View file

@ -7,84 +7,68 @@ import AdminAuthorization from '@components/Security/AdminAuthorization'
function DashboardHome() {
return (
<div className="flex items-center justify-center mx-auto min-h-screen flex-col space-x-3">
<div className="mx-auto pb-10">
<div className="flex items-center justify-center mx-auto min-h-screen flex-col p-4 sm:mb-0 mb-16">
<div className="mx-auto pb-6 sm:pb-10">
<Image
alt="learnhouse logo"
width={230}
src={learnhousetextlogo}
></Image>
className="w-48 sm:w-auto"
/>
</div>
<AdminAuthorization authorizationMode="component">
<div className="flex space-x-10">
<Link
href={`/dash/courses`}
className="flex bg-white shadow-lg p-[35px] w-[250px] rounded-lg items-center mx-auto hover:scale-105 transition-all ease-linear cursor-pointer"
>
<div className="flex flex-col mx-auto space-y-2">
<BookCopy className="mx-auto text-gray-500" size={50}></BookCopy>
<div className="text-center font-bold text-gray-500">Courses</div>
<p className="text-center text-sm text-gray-400">
Create and manage courses, chapters and ativities{' '}
</p>
</div>
</Link>
<Link
href={`/dash/org/settings/general`}
className="flex bg-white shadow-lg p-[35px] w-[250px] rounded-lg items-center mx-auto hover:scale-105 transition-all ease-linear cursor-pointer"
>
<div className="flex flex-col mx-auto space-y-2">
<School className="mx-auto text-gray-500" size={50}></School>
<div className="text-center font-bold text-gray-500">
Organization
</div>
<p className="text-center text-sm text-gray-400">
Configure your Organization general settings{' '}
</p>
</div>
</Link>
<Link
href={`/dash/users/settings/users`}
className="flex bg-white shadow-lg p-[35px] w-[250px] rounded-lg items-center mx-auto hover:scale-105 transition-all ease-linear cursor-pointer"
>
<div className="flex flex-col mx-auto space-y-2">
<Users className="mx-auto text-gray-500" size={50}></Users>
<div className="text-center font-bold text-gray-500">Users</div>
<p className="text-center text-sm text-gray-400">
Manage your Organization's users, roles{' '}
</p>
</div>
</Link>
<div className="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 lg:space-x-10">
{/* Card components */}
<DashboardCard
href="/dash/courses"
icon={<BookCopy className="mx-auto text-gray-500" size={50} />}
title="Courses"
description="Create and manage courses, chapters and activities"
/>
<DashboardCard
href="/dash/org/settings/general"
icon={<School className="mx-auto text-gray-500" size={50} />}
title="Organization"
description="Configure your Organization general settings"
/>
<DashboardCard
href="/dash/users/settings/users"
icon={<Users className="mx-auto text-gray-500" size={50} />}
title="Users"
description="Manage your Organization's users, roles"
/>
</div>
</AdminAuthorization>
<div className="flex flex-col space-y-10 ">
<div className="flex flex-col space-y-6 sm:space-y-10 mt-6 sm:mt-10">
<AdminAuthorization authorizationMode="component">
<div className="h-1 w-[100px] bg-neutral-200 rounded-full mx-auto"></div>
<div className="flex justify-center items-center">
<Link
href={'https://university.learnhouse.io/'}
target='_blank'
className="flex mt-[40px] bg-black space-x-2 items-center py-3 px-7 rounded-lg shadow-lg hover:scale-105 transition-all ease-linear cursor-pointer"
className="flex mt-4 sm:mt-[40px] bg-black space-x-2 items-center py-3 px-7 rounded-lg shadow-lg hover:scale-105 transition-all ease-linear cursor-pointer"
>
<University className=" text-gray-100" size={20}></University>
<div className=" text-sm font-bold text-gray-100">
<University className="text-gray-100" size={20} />
<div className="text-sm font-bold text-gray-100">
LearnHouse University
</div>
</Link>
</div>
<div className="mx-auto mt-[40px] w-28 h-1 bg-neutral-200 rounded-full"></div>
<div className="mx-auto mt-4 sm:mt-[40px] w-28 h-1 bg-neutral-200 rounded-full"></div>
</AdminAuthorization>
<Link
href={'/dash/user-account/settings/general'}
className="flex bg-white shadow-lg p-[15px] items-center rounded-lg mx-auto hover:scale-105 transition-all ease-linear cursor-pointer"
className="flex bg-white shadow-lg p-4 items-center rounded-lg mx-auto hover:scale-105 transition-all ease-linear cursor-pointer max-w-md"
>
<div className="flex flex-row mx-auto space-x-3 items-center">
<Settings className=" text-gray-500" size={20}></Settings>
<div className=" font-bold text-gray-500">Account Settings</div>
<p className=" text-sm text-gray-400">
Configure your personal settings, passwords, email
</p>
<div className="flex flex-col sm:flex-row mx-auto space-y-2 sm:space-y-0 sm:space-x-3 items-center text-center sm:text-left">
<Settings className="text-gray-500" size={20} />
<div>
<div className="font-bold text-gray-500">Account Settings</div>
<p className="text-sm text-gray-400">
Configure your personal settings, passwords, email
</p>
</div>
</div>
</Link>
</div>
@ -92,4 +76,20 @@ function DashboardHome() {
)
}
// New component for dashboard cards
function DashboardCard({ href, icon, title, description }: { href: string, icon: React.ReactNode, title: string, description: string }) {
return (
<Link
href={href}
className="flex bg-white shadow-lg p-6 w-full sm:w-[250px] rounded-lg items-center mx-auto hover:scale-105 transition-all ease-linear cursor-pointer"
>
<div className="flex flex-col mx-auto space-y-2">
{icon}
<div className="text-center font-bold text-gray-500">{title}</div>
<p className="text-center text-sm text-gray-400">{description}</p>
</div>
</Link>
)
}
export default DashboardHome

View file

@ -2,8 +2,9 @@
import React, { useEffect } from 'react'
import { motion } from 'framer-motion'
import Link from 'next/link'
import { useMediaQuery } from 'usehooks-ts'
import { getUriWithOrg } from '@services/config/config'
import { ScanEye, SquareUserRound, UserPlus, Users } from 'lucide-react'
import { Monitor, ScanEye, SquareUserRound, UserPlus, Users } from 'lucide-react'
import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
@ -22,6 +23,7 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
const org = useOrg() as any
const [H1Label, setH1Label] = React.useState('')
const [H2Label, setH2Label] = React.useState('')
const isMobile = useMediaQuery('(max-width: 767px)')
function handleLabels() {
if (params.subpage == 'users') {
@ -46,6 +48,20 @@ function UsersSettingsPage({ params }: { params: SettingsParams }) {
handleLabels()
}, [session, org, params.subpage, params])
if (isMobile) {
// TODO: Work on a better mobile experience
return (
<div className="h-screen w-full bg-[#f8f8f8] flex items-center justify-center p-4">
<div className="bg-white p-6 rounded-lg shadow-md text-center">
<h2 className="text-xl font-bold mb-4">Desktop Only</h2>
<Monitor className='mx-auto my-5' size={60} />
<p>This page is only accessible from a desktop device.</p>
<p>Please switch to a desktop to view and manage user settings.</p>
</div>
</div>
)
}
return (
<div className="h-screen w-full bg-[#f8f8f8] grid grid-rows-[auto,1fr]">
<div className="pl-10 pr-10 tracking-tight bg-[#fcfbfc] z-10 shadow-[0px_4px_16px_rgba(0,0,0,0.06)]">