mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: courses dashboard
This commit is contained in:
parent
8d35085908
commit
c39d9d5340
22 changed files with 611 additions and 67 deletions
30
apps/web/components/DashboardPages/UI/BreadCrumbs.tsx
Normal file
30
apps/web/components/DashboardPages/UI/BreadCrumbs.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { useCourse } from '@components/DashboardPages/CourseContext'
|
||||
import { Book, ChevronRight, User } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import React, { use, useEffect } from 'react'
|
||||
|
||||
type BreadCrumbsProps = {
|
||||
type: 'courses' | 'users'
|
||||
last_breadcrumb?: string
|
||||
}
|
||||
|
||||
function BreadCrumbs(props: BreadCrumbsProps) {
|
||||
const course = useCourse() as any;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='h-7'></div>
|
||||
<div className='text-gray-400 tracking-tight font-medium text-sm flex space-x-1'>
|
||||
<div className='flex items-center space-x-1'>
|
||||
{props.type == 'courses' ? <div className='flex space-x-2 items-center'> <Book className='text-gray' size={14}></Book><Link href='/dash/courses'>Courses</Link></div> : ''}
|
||||
{props.type == 'users' ? <div> <User size={14}></User><Link href='/dash/users'>Users</Link></div> : ''}
|
||||
<div className='flex items-center space-x-1 first-letter:uppercase'>
|
||||
{props.last_breadcrumb ? <ChevronRight size={17} /> : ''}
|
||||
<div className='first-letter:uppercase'> {props.last_breadcrumb}</div>
|
||||
</div></div></div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BreadCrumbs
|
||||
31
apps/web/components/DashboardPages/UI/CourseOverviewTop.tsx
Normal file
31
apps/web/components/DashboardPages/UI/CourseOverviewTop.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { useCourse } from "@components/DashboardPages/CourseContext";
|
||||
import { useEffect } from "react";
|
||||
import BreadCrumbs from "./BreadCrumbs";
|
||||
import SaveState from "./SaveState";
|
||||
import { CourseOverviewParams } from "app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page";
|
||||
|
||||
export function CourseOverviewTop({ params }: { params: CourseOverviewParams }) {
|
||||
const course = useCourse() as any;
|
||||
|
||||
useEffect(() => { }
|
||||
, [course])
|
||||
|
||||
return (
|
||||
<>
|
||||
<BreadCrumbs type='courses' last_breadcrumb={course.courseStructure.name} ></BreadCrumbs>
|
||||
<div className='flex'>
|
||||
<div className='flex py-5 grow items-center'>
|
||||
<div className="image rounded-lg shadow-md bg-gray-900 w-28 h-14"></div>
|
||||
<div className="flex flex-col course_metadata justify-center pl-5">
|
||||
<div className='text-gray-400 font-semibold text-sm'>Course</div>
|
||||
<div className='text-black font-bold text-xl -mt-1 first-letter:uppercase'>{course.courseStructure.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<SaveState orgslug={params.orgslug} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
25
apps/web/components/DashboardPages/UI/LeftMenu.tsx
Normal file
25
apps/web/components/DashboardPages/UI/LeftMenu.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
|
||||
import { Book } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
function LeftMenu() {
|
||||
return (
|
||||
<div
|
||||
style={{ background: "linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), radial-gradient(271.56% 105.16% at 50% -5.16%, rgba(255, 255, 255, 0.18) 0%, rgba(0, 0, 0, 0.00) 100%), #2E2D2D" }}
|
||||
className='flex flex-col w-20 justifiy-center bg-black h-screen justify-center text-white'>
|
||||
|
||||
<div className='flex items-center mx-auto'>
|
||||
<ToolTip content={"Courses"} slateBlack sideOffset={8} side='right' >
|
||||
<Link className='bg-white/5 rounded-lg p-2 hover:bg-white/10 transition-all ease-linear' href={`/dash/courses`} ><Book size={18} /></Link>
|
||||
</ToolTip>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LeftMenu
|
||||
|
||||
114
apps/web/components/DashboardPages/UI/SaveState.tsx
Normal file
114
apps/web/components/DashboardPages/UI/SaveState.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
'use client';
|
||||
import { getAPIUrl } from '@services/config/config';
|
||||
import { updateCourseOrderStructure } from '@services/courses/chapters';
|
||||
import { revalidateTags } from '@services/utils/ts/requests';
|
||||
import { useCourse, useCourseDispatch } from '@components/DashboardPages/CourseContext'
|
||||
import { Check, SaveAllIcon, Timer } from 'lucide-react'
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useEffect } from 'react'
|
||||
import { mutate } from 'swr';
|
||||
import { updateCourse } from '@services/courses/courses';
|
||||
|
||||
function SaveState(props: { orgslug: string }) {
|
||||
const course = useCourse() as any;
|
||||
const router = useRouter();
|
||||
const saved = course ? course.isSaved : true;
|
||||
const dispatchCourse = useCourseDispatch() as any;
|
||||
const course_structure = course.courseStructure;
|
||||
|
||||
const saveCourseState = async () => {
|
||||
// Course order
|
||||
if (saved) return;
|
||||
await changeOrderBackend();
|
||||
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
|
||||
// Course metadata
|
||||
await changeMetadataBackend();
|
||||
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
|
||||
await revalidateTags(['courses'], props.orgslug)
|
||||
dispatchCourse({ type: 'setIsSaved' })
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Course Order
|
||||
const changeOrderBackend = async () => {
|
||||
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
|
||||
await updateCourseOrderStructure(course.courseStructure.course_uuid, course.courseOrder);
|
||||
await revalidateTags(['courses'], props.orgslug)
|
||||
router.refresh();
|
||||
dispatchCourse({ type: 'setIsSaved' })
|
||||
}
|
||||
|
||||
// Course metadata
|
||||
const changeMetadataBackend = async () => {
|
||||
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
|
||||
await updateCourse(course.courseStructure.course_uuid, course.courseStructure);
|
||||
await revalidateTags(['courses'], props.orgslug)
|
||||
router.refresh();
|
||||
dispatchCourse({ type: 'setIsSaved' })
|
||||
}
|
||||
|
||||
|
||||
|
||||
const handleCourseOrder = (course_structure: any) => {
|
||||
const chapters = course_structure.chapters;
|
||||
const chapter_order_by_ids = chapters.map((chapter: any) => {
|
||||
return {
|
||||
chapter_id: chapter.id,
|
||||
activities_order_by_ids: chapter.activities.map((activity: any) => {
|
||||
return {
|
||||
activity_id: activity.id
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
dispatchCourse({ type: 'setCourseOrder', payload: { chapter_order_by_ids: chapter_order_by_ids } })
|
||||
dispatchCourse({ type: 'setIsNotSaved' })
|
||||
}
|
||||
|
||||
const initOrderPayload = () => {
|
||||
if (course_structure && course_structure.chapters) {
|
||||
handleCourseOrder(course_structure);
|
||||
dispatchCourse({ type: 'setIsSaved' })
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const changeOrderPayload = () => {
|
||||
if (course_structure && course_structure.chapters) {
|
||||
handleCourseOrder(course_structure);
|
||||
dispatchCourse({ type: 'setIsNotSaved' })
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (course_structure?.chapters) {
|
||||
initOrderPayload();
|
||||
}
|
||||
if (course_structure?.chapters && !saved) {
|
||||
changeOrderPayload();
|
||||
}
|
||||
}, [course_structure]); // This effect depends on the `course_structure` variable
|
||||
|
||||
return (
|
||||
<div className='flex space-x-4'>
|
||||
{saved ? <></> : <div className='text-gray-600 flex space-x-2 items-center antialiased'>
|
||||
<Timer size={15} />
|
||||
<div>
|
||||
Unsaved changes
|
||||
</div>
|
||||
|
||||
</div>}
|
||||
<div className={`px-4 py-2 rounded-lg drop-shadow-md cursor-pointer flex space-x-2 items-center font-bold antialiased transition-all ease-linear ` + (saved ? 'bg-gray-600 text-white' : 'bg-black text-white border hover:bg-gray-900 ')
|
||||
} onClick={saveCourseState}>
|
||||
|
||||
{saved ? <Check size={20} /> : <SaveAllIcon size={20} />}
|
||||
{saved ? <div className=''>Saved</div> : <div className=''>Save</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default SaveState
|
||||
Loading…
Add table
Add a link
Reference in a new issue