diff --git a/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx b/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx index ba9adc3f..50f0e1d2 100644 --- a/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx +++ b/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx @@ -66,16 +66,16 @@ function CourseEdit() { // Submit new chapter const submitChapter = async (chapter: any) => { await createChapter(chapter, courseid); - getCourseChapters(); + await getCourseChapters(); setNewChapterModal(false); }; // Submit new element const submitElement = async (element: any) => { console.log("submitElement", element); - updateChaptersMetadata(courseid, data); + await updateChaptersMetadata(courseid, data); await createElement(element, element.chapterId); - getCourseChapters(); + await getCourseChapters(); setNewElementModal(false); }; diff --git a/front/pages/org/[orgslug]/course/[courseid]/element/[elementid]/index.tsx b/front/pages/org/[orgslug]/course/[courseid]/element/[elementid]/index.tsx index 690a70f1..cbe3fdd9 100644 --- a/front/pages/org/[orgslug]/course/[courseid]/element/[elementid]/index.tsx +++ b/front/pages/org/[orgslug]/course/[courseid]/element/[elementid]/index.tsx @@ -31,9 +31,25 @@ function ElementPage() { const output = useMemo(() => { if (router.isReady) { - console.log("element", element.content); + console.log( "el",element.content); + + let content = Object.keys(element.content).length > 0 ? element.content : { + "type": "doc", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Hello world, this is a example Canva ⚡️" + } + ] + } + ] + } + console.log("element", content); - return generateHTML(element.content, [Document, StarterKit, Paragraph, Text, Bold]); + return generateHTML(content, [Document, StarterKit, Paragraph, Text, Bold]); } }, [element.content]); @@ -44,7 +60,7 @@ function ElementPage() { ) : (

element

-

{element.name}

+

{element.name}


diff --git a/front/pages/org/[orgslug]/course/[courseid]/index.tsx b/front/pages/org/[orgslug]/course/[courseid]/index.tsx index 0ad18355..f4d96afc 100644 --- a/front/pages/org/[orgslug]/course/[courseid]/index.tsx +++ b/front/pages/org/[orgslug]/course/[courseid]/index.tsx @@ -1,23 +1,25 @@ +import { EyeOpenIcon, Pencil2Icon } from "@radix-ui/react-icons"; +import Link from "next/link"; import { useRouter } from "next/router"; import React from "react"; import styled from "styled-components"; import Layout from "../../../../../components/ui/Layout"; import { getAPIUrl, getBackendUrl } from "../../../../../services/config"; -import { getCourse } from "../../../../../services/courses/courses"; +import { getCourse, getCourseMetadata } from "../../../../../services/courses/courses"; import { getOrganizationContextInfo } from "../../../../../services/orgs"; const CourseIdPage = () => { const router = useRouter(); - const { courseid } = router.query; + const { courseid, orgslug } = router.query; const [isLoading, setIsLoading] = React.useState(true); const [courseInfo, setCourseInfo] = React.useState({}) as any; async function fetchCourseInfo() { - const course = await getCourse("course_" + courseid); + const course = await getCourseMetadata("course_" + courseid); setCourseInfo(course); - console.log("courseinfo" , courseInfo); + setIsLoading(false); } @@ -27,7 +29,7 @@ const CourseIdPage = () => { fetchCourseInfo(); } return () => {}; - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady]); return ( @@ -35,22 +37,82 @@ const CourseIdPage = () => { {isLoading ? (
Loading...
) : ( -
+

-

{courseInfo.name}

- - - -
+

Course

+

+ {courseInfo.course.name}{" "} + + +   + + {" "} +

+
+ + {courseInfo.chapters.map((chapter: any) => { + return ( + <> + {chapter.elements.map((element: any) => { + return ( + <> + + + + + {" "} + + ); + })} +      + + ); + })} + + + + + + +

Description

+

{courseInfo.course.description}

+ +

What you will learn

+

{courseInfo.course.learnings == ![] ? "no data" : courseInfo.course.learnings}

+ +

Course Lessons

+ + {courseInfo.chapters.map((chapter: any) => { + return ( + <> +

Chapter : {chapter.name}

+ {chapter.elements.map((element: any) => { + return ( + <> +

+ Element {element.name} + + +   + + {" "} +

+ + ); + })} +      + + ); + })} + )} ); }; -const CourseWrapper = styled.div` +const CourseThumbnailWrapper = styled.div` display: flex; img { - position: absolute; width: 794px; height: 224.28px; object-fit: cover; @@ -62,5 +124,29 @@ const CourseWrapper = styled.div` border-radius: 7px; } `; +const CoursePageLayout = styled.div` + margin-left: 40px; + margin-right: 40px; +`; + +const ChaptersWrapper = styled.div` + display: flex; +`; +const ChapterIndicator = styled.div` + border-radius: 20px; + height: 5px; + background: #151515; + border-radius: 3px; + width: 40px; + background-color: black; + margin: 10px; + margin-left: 0px; + transition: all 0.2s ease; + + &:hover { + width: 50px; + cursor: pointer; + } +`; export default CourseIdPage; diff --git a/front/services/courses/courses.ts b/front/services/courses/courses.ts index bed9ba75..2bd0861e 100644 --- a/front/services/courses/courses.ts +++ b/front/services/courses/courses.ts @@ -15,7 +15,7 @@ export async function getOrgCourses(org_id: number) { .catch((error) => console.log("error", error)); } -export async function getCourse(course_id: any) { +export async function getCourse(course_id: string) { const HeadersConfig = new Headers({ "Content-Type": "application/json" }); const requestOptions: any = { @@ -31,6 +31,23 @@ export async function getCourse(course_id: any) { .catch((error) => console.log("error", error)); } +export async function getCourseMetadata(course_id: string) { + const HeadersConfig = new Headers({ "Content-Type": "application/json" }); + + const requestOptions: any = { + method: "GET", + headers: HeadersConfig, + redirect: "follow", + credentials: "include", + }; + + // todo : add course id to url + return fetch(`${getAPIUrl()}courses/meta/${course_id}`, requestOptions) + .then((result) => result.json()) + .catch((error) => console.log("error", error)); + +} + export async function createNewCourse(org_id: string, course_body: any, thumbnail: any) { const HeadersConfig = new Headers(); diff --git a/src/routers/courses/courses.py b/src/routers/courses/courses.py index a4eae909..450d63ad 100644 --- a/src/routers/courses/courses.py +++ b/src/routers/courses/courses.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, UploadFile, Form from src.dependencies.auth import get_current_user -from src.services.courses.courses import Course, create_course, get_course, get_courses, update_course, delete_course, update_course_thumbnail +from src.services.courses.courses import Course, create_course, get_course, get_course_meta, get_courses, update_course, delete_course, update_course_thumbnail from src.services.users import PublicUser @@ -34,6 +34,14 @@ async def api_get_course(course_id: str, current_user: PublicUser = Depends(get return await get_course(course_id, current_user=current_user) +@router.get("/meta/{course_id}") +async def api_get_course_meta(course_id: str, current_user: PublicUser = Depends(get_current_user)): + """ + Get single Course Metadata (chapters, elements) by course_id + """ + return await get_course_meta(course_id, current_user=current_user) + + @router.get("/{org_id}/page/{page}/limit/{limit}") async def api_get_course_by(page: int, limit: int, org_id: str): """ @@ -56,4 +64,4 @@ async def api_delete_course(course_id: str, current_user: PublicUser = Depends(g Delete Course by ID """ - return await delete_course(course_id, current_user) \ No newline at end of file + return await delete_course(course_id, current_user) diff --git a/src/services/courses/courses.py b/src/services/courses/courses.py index 5f15b1af..15178904 100644 --- a/src/services/courses/courses.py +++ b/src/services/courses/courses.py @@ -3,6 +3,7 @@ import os from typing import List from uuid import uuid4 from pydantic import BaseModel +from src.services.courses.elements import ElementInDB from src.services.uploads import upload_thumbnail from src.services.users import PublicUser, User from src.services.database import create_config_collection, check_database, create_database, learnhouseDB @@ -30,6 +31,21 @@ class CourseInDB(Course): updateDate: str authors: List[str] + +# TODO : wow terrible, fix this +# those models need to be available only in the chapters service +class CourseChapter(BaseModel): + name: str + description: str + elements: list + + +class CourseChapterInDB(CourseChapter): + coursechapter_id: str + course_id: str + creationDate: str + updateDate: str + #### Classes #################################################### # TODO : Add courses photo & cover upload and delete @@ -56,6 +72,61 @@ async def get_course(course_id: str, current_user: PublicUser): return course +async def get_course_meta(course_id: str, current_user: PublicUser): + await check_database() + courses = learnhouseDB["courses"] + coursechapters = learnhouseDB["coursechapters"] + course = courses.find_one({"course_id": course_id}) + elements = learnhouseDB["elements"] + + + # verify course rights + await verify_rights(course_id, current_user, "read") + + if not course: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Course does not exist") + + + coursechapters = coursechapters.find( + {"course_id": course_id}).sort("name", 1) + + # elements + coursechapter_elementIds_global = [] + + # chapters + chapters = {} + for coursechapter in coursechapters: + coursechapter = CourseChapterInDB(**coursechapter) + coursechapter_elementIds = [] + + for element in coursechapter.elements: + coursechapter_elementIds.append(element) + coursechapter_elementIds_global.append(element) + + chapters[coursechapter.coursechapter_id] = { + "id": coursechapter.coursechapter_id, "name": coursechapter.name, "elementIds": coursechapter_elementIds + } + + # elements + elements_list = {} + for element in elements.find({"element_id": {"$in": coursechapter_elementIds_global}}): + element = ElementInDB(**element) + elements_list[element.element_id] = { + "id": element.element_id, "name": element.name, "type": element.type, "content": element.content + } + + chapters_list_with_elements = [] + for chapter in chapters: + chapters_list_with_elements.append( + {"id": chapters[chapter]["id"], "name": chapters[chapter]["name"], "elements": [elements_list[element] for element in chapters[chapter]["elementIds"]]}) + course = Course(**course) + return { + "course": course, + "chapters": chapters_list_with_elements, + } + + async def create_course(course_object: Course, org_id: str, current_user: PublicUser, thumbnail_file: UploadFile | None = None): await check_database() courses = learnhouseDB["courses"]