diff --git a/apps/api/src/services/courses/courses.py b/apps/api/src/services/courses/courses.py index 3cc05dff..d7366503 100644 --- a/apps/api/src/services/courses/courses.py +++ b/apps/api/src/services/courses/courses.py @@ -49,7 +49,7 @@ async def get_course( .where(ResourceAuthor.resource_uuid == course.course_uuid) ) authors = db_session.exec(authors_statement).all() - + # convert from User to UserRead authors = [UserRead.from_orm(author) for author in authors] @@ -124,6 +124,11 @@ async def create_course( # Complete course object course.org_id = course.org_id + + # Get org uuid + org_statement = select(Organization).where(Organization.id == org_id) + org = db_session.exec(org_statement).first() + course.course_uuid = str(f"course_{uuid4()}") course.creation_date = str(datetime.now()) course.update_date = str(datetime.now()) @@ -132,9 +137,9 @@ async def create_course( if thumbnail_file and thumbnail_file.filename: name_in_disk = f"{course.course_uuid}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}" await upload_thumbnail( - thumbnail_file, name_in_disk, org_id, course.course_uuid + thumbnail_file, name_in_disk, org.org_uuid, course.course_uuid ) - course_object.thumbnail_image = name_in_disk + course.thumbnail_image = name_in_disk # Insert course db_session.add(course) @@ -192,11 +197,15 @@ async def update_course_thumbnail( # RBAC check await rbac_check(request, course.course_uuid, current_user, "update", db_session) + # Get org uuid + org_statement = select(Organization).where(Organization.id == course.org_id) + org = db_session.exec(org_statement).first() + # Upload thumbnail if thumbnail_file and thumbnail_file.filename: name_in_disk = f"{course_uuid}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}" await upload_thumbnail( - thumbnail_file, name_in_disk, course.org_id, course.course_uuid + thumbnail_file, name_in_disk, org.org_uuid, course.course_uuid ) # Update course @@ -223,8 +232,6 @@ async def update_course_thumbnail( ) authors = db_session.exec(authors_statement).all() - - # convert from User to UserRead authors = [UserRead.from_orm(author) for author in authors] @@ -331,7 +338,7 @@ async def get_courses_orgslug( courses = db_session.exec(statement) - courses = [CourseRead(**course.dict(),authors=[]) for course in courses] + courses = [CourseRead(**course.dict(), authors=[]) for course in courses] # for every course, get the authors for course in courses: diff --git a/apps/api/src/services/orgs/logos.py b/apps/api/src/services/orgs/logos.py index 46108cf9..09fe048a 100644 --- a/apps/api/src/services/orgs/logos.py +++ b/apps/api/src/services/orgs/logos.py @@ -3,13 +3,13 @@ from uuid import uuid4 from src.services.utils.upload_content import upload_content -async def upload_org_logo(logo_file, org_id): +async def upload_org_logo(logo_file, org_uuid): contents = logo_file.file.read() name_in_disk = f"{uuid4()}.{logo_file.filename.split('.')[-1]}" await upload_content( "logos", - org_id, + org_uuid, contents, name_in_disk, ) diff --git a/apps/api/src/services/orgs/orgs.py b/apps/api/src/services/orgs/orgs.py index c151b1b9..78afa4fb 100644 --- a/apps/api/src/services/orgs/orgs.py +++ b/apps/api/src/services/orgs/orgs.py @@ -187,7 +187,7 @@ async def update_org_logo( await rbac_check(request, org.org_uuid, current_user, "update", db_session) # Upload logo - name_in_disk = await upload_org_logo(logo_file, org_id) + name_in_disk = await upload_org_logo(logo_file, org.org_uuid) # Update org org.logo_image = name_in_disk diff --git a/apps/api/src/services/utils/upload_content.py b/apps/api/src/services/utils/upload_content.py index 0776fe94..33200231 100644 --- a/apps/api/src/services/utils/upload_content.py +++ b/apps/api/src/services/utils/upload_content.py @@ -6,7 +6,7 @@ from config.config import get_learnhouse_config async def upload_content( - directory: str, org_id: str, file_binary: bytes, file_and_format: str + directory: str, org_uuid: str, file_binary: bytes, file_and_format: str ): # Get Learnhouse Config learnhouse_config = get_learnhouse_config() @@ -16,12 +16,12 @@ async def upload_content( if content_delivery == "filesystem": # create folder for activity - if not os.path.exists(f"content/{org_id}/{directory}"): + if not os.path.exists(f"content/{org_uuid}/{directory}"): # create folder for activity - os.makedirs(f"content/{org_id}/{directory}") + os.makedirs(f"content/{org_uuid}/{directory}") # upload file to server with open( - f"content/{org_id}/{directory}/{file_and_format}", + f"content/{org_uuid}/{directory}/{file_and_format}", "wb", ) as f: f.write(file_binary) @@ -37,13 +37,13 @@ async def upload_content( ) # Create folder for activity - if not os.path.exists(f"content/{org_id}/{directory}"): + if not os.path.exists(f"content/{org_uuid}/{directory}"): # create folder for activity - os.makedirs(f"content/{org_id}/{directory}") + os.makedirs(f"content/{org_uuid}/{directory}") # Upload file to server with open( - f"content/{org_id}/{directory}/{file_and_format}", + f"content/{org_uuid}/{directory}/{file_and_format}", "wb", ) as f: f.write(file_binary) @@ -52,9 +52,9 @@ async def upload_content( print("Uploading to s3 using boto3...") try: s3.upload_file( - f"content/{org_id}/{directory}/{file_and_format}", + f"content/{org_uuid}/{directory}/{file_and_format}", "learnhouse-media", - f"content/{org_id}/{directory}/{file_and_format}", + f"content/{org_uuid}/{directory}/{file_and_format}", ) except ClientError as e: print(e) @@ -63,7 +63,7 @@ async def upload_content( try: s3.head_object( Bucket="learnhouse-media", - Key=f"content/{org_id}/{directory}/{file_and_format}", + Key=f"content/{org_uuid}/{directory}/{file_and_format}", ) print("File upload successful!") except Exception as e: diff --git a/apps/web/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx b/apps/web/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx index d89a99cf..8a799264 100644 --- a/apps/web/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx +++ b/apps/web/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx @@ -6,6 +6,7 @@ import { cookies } from "next/headers"; import { Metadata } from "next"; import { getActivityWithAuthHeader } from "@services/courses/activities"; import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; +import { getOrganizationContextInfo } from "@services/organizations/orgs"; type MetadataProps = { params: { orgslug: string, courseid: string, activityid: string }; @@ -32,6 +33,7 @@ const EditActivity = async (params: any) => { const activityid = params.params.activityid; const courseid = params.params.courseid; const orgslug = params.params.orgslug; + const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); const courseInfo = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null) @@ -40,7 +42,7 @@ const EditActivity = async (params: any) => { return (
- +
); diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx index c51601ba..95b61d7e 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx @@ -48,6 +48,7 @@ export async function generateMetadata( const CollectionPage = async (params: any) => { const cookieStore = cookies(); const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const org = await getOrganizationContextInfo(params.params.orgslug, { revalidate: 1800, tags: ['organizations'] }); const orgslug = params.params.orgslug; const col = await getCollectionByIdWithAuthHeader(params.params.collectionid, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] }); @@ -64,7 +65,7 @@ const CollectionPage = async (params: any) => { {col.courses.map((course: any) => (
-
+

{course.name}

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 c076e09c..a7cfd137 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 @@ -11,6 +11,7 @@ import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWra import { useRouter } from "next/navigation"; import AuthenticatedClientElement from "@components/Security/AuthenticatedClientElement"; import { getCourseThumbnailMediaDirectory } from "@services/media/media"; +import { useOrg } from "@components/Contexts/OrgContext"; interface ActivityClientProps { activityid: string; @@ -27,6 +28,7 @@ function ActivityClient(props: ActivityClientProps) { const orgslug = props.orgslug; const activity = props.activity; const course = props.course; + const org = useOrg() as any; function getChapterName(chapterId: string) { let chapterName = ""; @@ -47,7 +49,7 @@ function ActivityClient(props: ActivityClientProps) {
- +
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 a73654d4..752641b8 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx @@ -12,6 +12,7 @@ import { getCourseThumbnailMediaDirectory } from "@services/media/media"; import { ArrowRight, Check, File, Sparkles, Star, Video } from "lucide-react"; import Avvvatars from "avvvatars-react"; import { getUser } from "@services/users/users"; +import { useOrg } from "@components/Contexts/OrgContext"; const CourseClient = (props: any) => { const [user, setUser] = useState({}); @@ -19,6 +20,7 @@ const CourseClient = (props: any) => { const courseuuid = props.courseuuid; const orgslug = props.orgslug; const course = props.course; + const org = useOrg() as any; const router = useRouter(); function getLearningTags() { @@ -28,7 +30,6 @@ const CourseClient = (props: any) => { } - console.log(course); async function startCourseUI() { // Create activity @@ -57,7 +58,7 @@ const CourseClient = (props: any) => { useEffect(() => { } - , []); + , [org]); return ( <> @@ -73,10 +74,10 @@ const CourseClient = (props: any) => {
-
+
- +
@@ -198,7 +199,7 @@ const CourseClient = (props: any) => { } {console.log(course)} - {isCourseStarted() ? ( + {isCourseStarted() ? ( diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/[[...subpage]]/edit.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/[[...subpage]]/edit.tsx deleted file mode 100644 index 294400c6..00000000 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/[[...subpage]]/edit.tsx +++ /dev/null @@ -1,181 +0,0 @@ -"use client"; -import React, { FC, useEffect, useReducer } from 'react' -import { revalidateTags, swrFetcher } from "@services/utils/ts/requests"; -import { getAPIUrl, getUriWithOrg } from '@services/config/config'; -import useSWR, { mutate } from 'swr'; -import { getCourseThumbnailMediaDirectory } from '@services/media/media'; -import Link from 'next/link'; -import CourseEdition from '../subpages/CourseEdition'; -import CourseContentEdition from '../subpages/CourseContentEdition'; -import ErrorUI from '@components/StyledElements/Error/Error'; -import { updateChaptersMetadata } from '@services/courses/chapters'; -import { Check, SaveAllIcon, Timer } from 'lucide-react'; -import Loading from '../../loading'; -import { updateCourse } from '@services/courses/courses'; -import { useRouter } from 'next/navigation'; - -function CourseEditClient({ courseuuid, courseid, subpage, params }: { courseid: any, courseuuid: string, subpage: string, params: any }) { - const { data: chapters_meta, error: chapters_meta_error, isLoading: chapters_meta_isloading } = useSWR(`${getAPIUrl()}chapters/course/course_${courseuuid}/meta`, swrFetcher); - const { data: course, error: course_error, isLoading: course_isloading } = useSWR(`${getAPIUrl()}courses/course_${courseuuid}/meta`, swrFetcher); - const [courseChaptersMetadata, dispatchCourseChaptersMetadata] = useReducer(courseChaptersReducer, {}); - const [courseState, dispatchCourseMetadata] = useReducer(courseReducer, {}); - const [savedContent, dispatchSavedContent] = useReducer(savedContentReducer, true); - const router = useRouter(); - - - // This function is a quick fix to transform the payload object from what was used before to the new and improved format - // The entire course edition frontend code will be remade in the future in a proper way. - const ConvertToNewAPIOrderUpdatePayload = (courseChaptersMetadata: any) => { - const old_format = courseChaptersMetadata - console.log() - - // Convert originalObject to the desired format - const convertedObject = { - "chapter_order_by_ids": old_format.chapterOrder.map((chapterId: string | number, chapterIndex: any) => { - const chapter = old_format.chapters[chapterId]; - return { - "chapter_id": chapter.id, - "activities_order_by_ids": chapter.activityIds.map((activityId: any, activityIndex: any) => { - return { - "activity_id": activityIndex - }; - }) - }; - }) - }; - - return convertedObject - } - - - - function courseChaptersReducer(state: any, action: any) { - switch (action.type) { - case 'updated_chapter': - // action will contain the entire state, just update the entire state - return action.payload; - default: - throw new Error(); - } - } - - function courseReducer(state: any, action: any) { - switch (action.type) { - case 'updated_course': - // action will contain the entire state, just update the entire state - return action.payload; - default: - throw new Error(); - } - } - - function savedContentReducer(state: any, action: any) { - switch (action.type) { - case 'saved_content': - return true; - case 'unsaved_content': - return false; - default: - throw new Error(); - } - } - - async function saveCourse() { - if (subpage.toString() === 'content') { - let payload = ConvertToNewAPIOrderUpdatePayload(courseChaptersMetadata) - await updateChaptersMetadata(courseuuid, payload) - dispatchSavedContent({ type: 'saved_content' }) - await mutate(`${getAPIUrl()}chapters/course/course_${courseuuid}/meta`) - await revalidateTags(['courses'], params.params.orgslug) - router.refresh() - } - else if (subpage.toString() === 'general') { - await updateCourse(courseuuid, courseState) - dispatchSavedContent({ type: 'saved_content' }) - await mutate(`${getAPIUrl()}courses/course_${courseuuid}`) - await mutate(`${getAPIUrl()}chapters/course/course_${courseuuid}/meta`) - await revalidateTags(['courses'], params.params.orgslug) - router.refresh() - } - } - - useEffect(() => { - - if (chapters_meta) { - dispatchCourseChaptersMetadata({ type: 'updated_chapter', payload: chapters_meta }) - dispatchSavedContent({ type: 'saved_content' }) - } - if (course) { - dispatchCourseMetadata({ type: 'updated_course', payload: course }) - dispatchSavedContent({ type: 'saved_content' }) - } - }, [chapters_meta, course]) - - return ( - <> -
-
- {course_isloading &&
Loading...
} - {course && <> -
-
- - - -
-
-
Edit Course
-
{course.name}
-
-
-
- {savedContent ? <> :
- -
- Unsaved changes -
- -
} -
- - {savedContent ? : } - {savedContent ?
Saved
:
Save
} -
-
-
- } -
- -
General
- - -
Content
- -
-
-
- - - - ) -} - -const CoursePageViewer = ({ subpage, course, orgslug, dispatchCourseMetadata, dispatchCourseChaptersMetadata, courseChaptersMetadata, dispatchSavedContent, courseState }: { subpage: string, courseuuid: string, orgslug: string, dispatchCourseChaptersMetadata: React.Dispatch, dispatchCourseMetadata: React.Dispatch, dispatchSavedContent: React.Dispatch, courseChaptersMetadata: any, courseState: any, course: any }) => { - - if (subpage.toString() === 'general' && Object.keys(courseState).length !== 0 && course) { - return - } - else if (subpage.toString() === 'content' && Object.keys(courseChaptersMetadata).length !== 0 && course) { - return - } - else if (subpage.toString() === 'content' || subpage.toString() === 'general') { - return - } - else { - return - } - -} - -export default CourseEditClient \ No newline at end of file diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/[[...subpage]]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/[[...subpage]]/page.tsx deleted file mode 100644 index bc2fb786..00000000 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/[[...subpage]]/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { getOrganizationContextInfo } from "@services/organizations/orgs"; -import CourseEditClient from "./edit"; -import { getCourseMetadataWithAuthHeader } from "@services/courses/courses"; -import { cookies } from "next/headers"; -import { Metadata } from 'next'; -import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; - -type MetadataProps = { - params: { orgslug: string, courseuuid: string }; - searchParams: { [key: string]: string | string[] | undefined }; -}; - -export async function generateMetadata( - { params }: MetadataProps, -): Promise { - const cookieStore = cookies(); - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) - - - // Get Org context information - const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); - const course_meta = await getCourseMetadataWithAuthHeader(params.courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) - - return { - title: `Edit Course - ` + course_meta.name, - description: course_meta.mini_description, - }; -} - - -async function CourseEdit(params: any) { - const cookieStore = cookies(); - const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) - let subpage = params.params.subpage ? params.params.subpage : 'general'; - const course_meta = await getCourseMetadataWithAuthHeader(params.params.courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) - return ( - <> - - - ); -} - - -export default CourseEdit; diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/subpages/CourseContentEdition.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/subpages/CourseContentEdition.tsx deleted file mode 100644 index a5269054..00000000 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/subpages/CourseContentEdition.tsx +++ /dev/null @@ -1,320 +0,0 @@ -"use client"; -import React from "react"; -import { useState, useEffect } from "react"; -import { DragDropContext, Droppable } from "react-beautiful-dnd"; -import Chapter from "@components/Pages/CourseEdit/Draggables/Chapter"; -import { createChapter, deleteChapter, getCourseChaptersMetadata, updateChaptersMetadata } from "@services/courses/chapters"; -import { useRouter } from "next/navigation"; -import NewChapterModal from "@components/Objects/Modals/Chapters/NewChapter"; -import NewActivityModal from "@components/Objects/Modals/Activities/Create/NewActivity"; -import { createActivity, createFileActivity, createExternalVideoActivity } from "@services/courses/activities"; -import { getOrganizationContextInfo, getOrganizationContextInfoWithoutCredentials } from "@services/organizations/orgs"; -import Modal from "@components/StyledElements/Modal/Modal"; -import { denyAccessToUser } from "@services/utils/react/middlewares/views"; -import { Folders, Hexagon, SaveIcon } from "lucide-react"; -import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; -import { revalidateTags, swrFetcher } from "@services/utils/ts/requests"; -import { getAPIUrl } from "@services/config/config"; -import { mutate } from "swr"; - -function CourseContentEdition(props: any) { - const router = useRouter(); - // Initial Course Chapters State - const course_chapters_with_orders_and_activities = props.course_chapters_with_orders_and_activities; - - // New Chapter Modal State - const [newChapterModal, setNewChapterModal] = useState(false) as any; - // New Activity Modal State - const [newActivityModal, setNewActivityModal] = useState(false) as any; - const [selectedChapterToAddActivityTo, setSelectedChapterToAddActivityTo] = useState("") as any; - - // Check window availability - const [winReady, setwinReady] = useState(false); - const course = props.course; - const course_uuid = props.course ? props.course.course_uuid : '' - const orgslug = props.orgslug; - - // - - - - useEffect(() => { - setwinReady(true); - }, [course_uuid, orgslug]); - - // get a list of chapters order by chapter order - const getChapters = () => { - const chapterOrder = course_chapters_with_orders_and_activities.chapterOrder ? course_chapters_with_orders_and_activities.chapterOrder : []; - return chapterOrder.map((chapterId: any) => { - const chapter = course_chapters_with_orders_and_activities.chapters[chapterId]; - let activities = []; - if (course_chapters_with_orders_and_activities.activities) { - activities = chapter.activityIds.map((activityId: any) => course_chapters_with_orders_and_activities.activities[activityId]) - ? chapter.activityIds.map((activityId: any) => course_chapters_with_orders_and_activities.activities[activityId]) - : []; - } - return { - list: { - chapter: chapter, - activities: activities, - }, - }; - }); - }; - - // Submit new chapter - const submitChapter = async (chapter: any) => { - await createChapter(chapter); - - mutate(`${getAPIUrl()}chapters/course/${course_uuid}/meta`,true); - await revalidateTags(['courses'], orgslug); - router.refresh(); - setNewChapterModal(false); - }; - - // Submit new activity - const submitActivity = async (activity: any) => { - let org = await getOrganizationContextInfoWithoutCredentials(orgslug, { revalidate: 1800 }); - await createActivity(activity, activity.chapterId, org.org_id); - mutate(`${getAPIUrl()}chapters/course/${course_uuid}/meta`); - // await getCourseChapters(); - setNewActivityModal(false); - await revalidateTags(['courses'], orgslug); - router.refresh(); - }; - - - - // Submit File Upload - const submitFileActivity = async (file: any, type: any, activity: any, chapterId: string) => { - //await updateChaptersMetadata(course_uuid, course_chapters_with_orders_and_activities); - await createFileActivity(file, type, activity, chapterId); - mutate(`${getAPIUrl()}chapters/course/${course_uuid}/meta`); - // await getCourseChapters(); - setNewActivityModal(false); - await revalidateTags(['courses'], orgslug); - router.refresh(); - }; - - // Submit YouTube Video Upload - const submitExternalVideo = async (external_video_data: any, activity: any, chapterId: string) => { - //await updateChaptersMetadata(course_uuid, course_chapters_with_orders_and_activities); - await createExternalVideoActivity(external_video_data, activity, chapterId); - mutate(`${getAPIUrl()}chapters/course/${course_uuid}/meta`); - // await getCourseChapters(); - setNewActivityModal(false); - await revalidateTags(['courses'], orgslug); - router.refresh(); - }; - - const deleteChapterUI = async (chapterId: any) => { - await deleteChapter(chapterId); - - mutate(`${getAPIUrl()}chapters/course/${course_uuid}/meta`,true); - // await getCourseChapters(); - await revalidateTags(['courses'], orgslug); - router.refresh(); - }; - - - - /* - Modals - */ - - const openNewActivityModal = async (chapterId: any) => { - setNewActivityModal(true); - setSelectedChapterToAddActivityTo(chapterId); - }; - - // Close new chapter modal - const closeNewChapterModal = () => { - setNewChapterModal(false); - }; - - const closeNewActivityModal = () => { - setNewActivityModal(false); - }; - - /* - Drag and drop functions - - */ - const onDragEnd = async (result: any) => { - const { destination, source, draggableId, type } = result; - - - // check if the activity is dropped outside the droppable area - if (!destination) { - return; - } - - // check if the activity is dropped in the same place - if (destination.droppableId === source.droppableId && destination.index === source.index) { - return; - } - //////////////////////////// CHAPTERS //////////////////////////// - if (type === "chapter") { - const newChapterOrder = Array.from(course_chapters_with_orders_and_activities.chapterOrder); - newChapterOrder.splice(source.index, 1); - newChapterOrder.splice(destination.index, 0, draggableId); - - const newState = { - ...course_chapters_with_orders_and_activities, - chapterOrder: newChapterOrder, - }; - - props.dispatchCourseChaptersMetadata({ type: 'updated_chapter', payload: newState }) - props.dispatchSavedContent({ type: 'unsaved_content' }) - //setData(newState); - return; - } - - //////////////////////// ACTIVITIES IN SAME CHAPTERS //////////////////////////// - // check if the activity is dropped in the same chapter - const start = course_chapters_with_orders_and_activities.chapters[source.droppableId]; - const finish = course_chapters_with_orders_and_activities.chapters[destination.droppableId]; - - // check if the activity is dropped in the same chapter - if (start === finish) { - // create new arrays for chapters and activities - const chapter = course_chapters_with_orders_and_activities.chapters[source.droppableId]; - const newActivityIds = Array.from(chapter.activityIds); - - // remove the activity from the old position - newActivityIds.splice(source.index, 1); - - // add the activity to the new position - newActivityIds.splice(destination.index, 0, draggableId); - - const newChapter = { - ...chapter, - activityIds: newActivityIds, - }; - - const newState = { - ...course_chapters_with_orders_and_activities, - chapters: { - ...course_chapters_with_orders_and_activities.chapters, - [newChapter.id]: newChapter, - }, - }; - props.dispatchCourseChaptersMetadata({ type: 'updated_chapter', payload: newState }) - props.dispatchSavedContent({ type: 'unsaved_content' }) - //setData(newState); - return; - } - - //////////////////////// ACTIVITIES IN DIFF CHAPTERS //////////////////////////// - // check if the activity is dropped in a different chapter - if (start !== finish) { - // create new arrays for chapters and activities - const startChapterActivityIds = Array.from(start.activityIds); - - // remove the activity from the old position - startChapterActivityIds.splice(source.index, 1); - const newStart = { - ...start, - activityIds: startChapterActivityIds, - }; - - // add the activity to the new position within the chapter - const finishChapterActivityIds = Array.from(finish.activityIds); - finishChapterActivityIds.splice(destination.index, 0, draggableId); - const newFinish = { - ...finish, - activityIds: finishChapterActivityIds, - }; - - const newState = { - ...course_chapters_with_orders_and_activities, - chapters: { - ...course_chapters_with_orders_and_activities.chapters, - [newStart.id]: newStart, - [newFinish.id]: newFinish, - }, - }; - - props.dispatchCourseChaptersMetadata({ type: 'updated_chapter', payload: newState }) - props.dispatchSavedContent({ type: 'unsaved_content' }) - //setData(newState); - return; - } - }; - - return ( - <> -
- - } - dialogTitle="Create Activity" - dialogDescription="Choose between types of activities to add to the course" - - /> - {winReady && ( -
- - - {(provided) => ( - <> -
- {getChapters().map((info: any, index: any) => ( - <> - - - ))} - {provided.placeholder} -
- - )} -
-
- } - dialogTitle="Create chapter" - dialogDescription="Add a new chapter to the course" - dialogTrigger={ -
- -
Add chapter +
-
- } - /> -
- )} -
-
- - ); -} - - -export default CourseContentEdition; \ No newline at end of file diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/subpages/CourseEdition.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/subpages/CourseEdition.tsx deleted file mode 100644 index 3684daab..00000000 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/edit/subpages/CourseEdition.tsx +++ /dev/null @@ -1,110 +0,0 @@ -"use client"; -import FormLayout, { ButtonBlack, FormField, FormLabel, FormLabelAndMessage, FormMessage, Input, Textarea } from '@components/StyledElements/Form/Form' -import * as Form from '@radix-ui/react-form'; -import { useFormik } from 'formik'; -import { AlertTriangle } from "lucide-react"; -import React from "react"; - -const validate = (values: any) => { - const errors: any = {}; - - if (!values.name) { - errors.name = 'Required'; - } - - if (values.name.length > 100) { - errors.name = 'Must be 80 characters or less'; - } - - if (!values.mini_description) { - errors.mini_description = 'Required'; - } - - if (values.mini_description.length > 200) { - errors.mini_description = 'Must be 200 characters or less'; - } - - if (!values.description) { - errors.description = 'Required'; - - } - - if (values.description.length > 1000) { - errors.description = 'Must be 1000 characters or less'; - } - - - if (!values.learnings) { - errors.learnings = 'Required'; - } - - return errors; -}; - -function CourseEdition(props: any) { - const [error, setError] = React.useState(''); - const formik = useFormik({ - initialValues: { - name: String(props.course_chapters_with_orders_and_activities.name), - mini_description: String(props.course_chapters_with_orders_and_activities.mini_description), - description: String(props.course_chapters_with_orders_and_activities.description), - learnings: String(props.course_chapters_with_orders_and_activities.learnings), - }, - validate, - onSubmit: async values => { - }, - }); - - - React.useEffect(() => { - // This code will run whenever form values are updated - if (formik.values !== formik.initialValues) { - props.dispatchSavedContent({ type: 'unsaved_content' }); - const updatedCourse = { - ...props.course_chapters_with_orders_and_activities, - name: formik.values.name, - description: formik.values.description, - learnings: formik.values.learnings, - }; - props.dispatchCourseMetadata({ type: 'updated_course', payload: updatedCourse }); - } - }, [formik.values, formik.initialValues]); - - - return ( -
-
- {error && ( -
- -
{error}
-
- )} - - - - - - - - - - - -