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={
-
- }
- />
-
- )}
-
-
- >
- );
-}
-
-
-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 && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-export default CourseEdition
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
index a4fb9ad3..cbde78ac 100644
--- a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
@@ -1,5 +1,5 @@
'use client';
-import BreadCrumbs from '@components/DashboardPages/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse';
import CourseThumbnail from '@components/Objects/Other/CourseThumbnail';
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
@@ -34,7 +34,7 @@ function CoursesHome(params: CourseProps) {
-
Courses
+
Courses
+
+
{children}
- >
+
+
);
}
diff --git a/apps/web/components/DashboardPages/CourseContext.tsx b/apps/web/components/Contexts/CourseContext.tsx
similarity index 100%
rename from apps/web/components/DashboardPages/CourseContext.tsx
rename to apps/web/components/Contexts/CourseContext.tsx
diff --git a/apps/web/components/Contexts/OrgContext.tsx b/apps/web/components/Contexts/OrgContext.tsx
new file mode 100644
index 00000000..a8ba575f
--- /dev/null
+++ b/apps/web/components/Contexts/OrgContext.tsx
@@ -0,0 +1,25 @@
+'use client';
+import { getAPIUrl } from '@services/config/config';
+import { swrFetcher } from '@services/utils/ts/requests';
+import React, { useContext, useEffect } from 'react'
+import useSWR from 'swr';
+import { createContext } from 'react';
+
+export const OrgContext = createContext({}) as any;
+
+export function OrgProvider({ children, orgslug }: { children: React.ReactNode, orgslug: string }) {
+ const { data: org } = useSWR(`${getAPIUrl()}orgs/slug/${orgslug}`, swrFetcher);
+ useEffect(() => {
+
+ }, [org]);
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function useOrg() {
+ return useContext(OrgContext);
+}
diff --git a/apps/web/components/DashboardPages/EditCourseGeneral/EditCourseGeneral.tsx b/apps/web/components/Dashboard/EditCourseGeneral/EditCourseGeneral.tsx
similarity index 98%
rename from apps/web/components/DashboardPages/EditCourseGeneral/EditCourseGeneral.tsx
rename to apps/web/components/Dashboard/EditCourseGeneral/EditCourseGeneral.tsx
index 7530353a..e9aba807 100644
--- a/apps/web/components/DashboardPages/EditCourseGeneral/EditCourseGeneral.tsx
+++ b/apps/web/components/Dashboard/EditCourseGeneral/EditCourseGeneral.tsx
@@ -4,7 +4,7 @@ import { AlertTriangle } from 'lucide-react'
import * as Switch from '@radix-ui/react-switch';
import * as Form from '@radix-ui/react-form';
import React from 'react'
-import { useCourse, useCourseDispatch } from '../CourseContext';
+import { useCourse, useCourseDispatch } from '../../Contexts/CourseContext';
type EditCourseStructureProps = {
diff --git a/apps/web/components/DashboardPages/EditCourseStructure/Buttons/NewActivityButton.tsx b/apps/web/components/Dashboard/EditCourseStructure/Buttons/NewActivityButton.tsx
similarity index 98%
rename from apps/web/components/DashboardPages/EditCourseStructure/Buttons/NewActivityButton.tsx
rename to apps/web/components/Dashboard/EditCourseStructure/Buttons/NewActivityButton.tsx
index f69498ce..fbc1bf8b 100644
--- a/apps/web/components/DashboardPages/EditCourseStructure/Buttons/NewActivityButton.tsx
+++ b/apps/web/components/Dashboard/EditCourseStructure/Buttons/NewActivityButton.tsx
@@ -1,4 +1,4 @@
-import { useCourse } from '@components/DashboardPages/CourseContext';
+import { useCourse } from '@components/Contexts/CourseContext';
import NewActivityModal from '@components/Objects/Modals/Activities/Create/NewActivity';
import Modal from '@components/StyledElements/Modal/Modal';
import { getAPIUrl } from '@services/config/config';
diff --git a/apps/web/components/DashboardPages/EditCourseStructure/DraggableElements/ActivityElement.tsx b/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ActivityElement.tsx
similarity index 100%
rename from apps/web/components/DashboardPages/EditCourseStructure/DraggableElements/ActivityElement.tsx
rename to apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ActivityElement.tsx
diff --git a/apps/web/components/DashboardPages/EditCourseStructure/DraggableElements/ChapterElement.tsx b/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ChapterElement.tsx
similarity index 100%
rename from apps/web/components/DashboardPages/EditCourseStructure/DraggableElements/ChapterElement.tsx
rename to apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ChapterElement.tsx
diff --git a/apps/web/components/DashboardPages/EditCourseStructure/EditCourseStructure.tsx b/apps/web/components/Dashboard/EditCourseStructure/EditCourseStructure.tsx
similarity index 98%
rename from apps/web/components/DashboardPages/EditCourseStructure/EditCourseStructure.tsx
rename to apps/web/components/Dashboard/EditCourseStructure/EditCourseStructure.tsx
index 0608c9c6..7169f0c3 100644
--- a/apps/web/components/DashboardPages/EditCourseStructure/EditCourseStructure.tsx
+++ b/apps/web/components/Dashboard/EditCourseStructure/EditCourseStructure.tsx
@@ -9,7 +9,7 @@ import PageLoading from '@components/Objects/Loaders/PageLoading';
import { createChapter, updateCourseOrderStructure } from '@services/courses/chapters';
import { useRouter } from 'next/navigation';
import { CourseStructureContext } from 'app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page';
-import { useCourse, useCourseDispatch } from '@components/DashboardPages/CourseContext';
+import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
import { Hexagon } from 'lucide-react';
import Modal from '@components/StyledElements/Modal/Modal';
import NewChapterModal from '@components/Objects/Modals/Chapters/NewChapter';
diff --git a/apps/web/components/DashboardPages/UI/BreadCrumbs.tsx b/apps/web/components/Dashboard/UI/BreadCrumbs.tsx
similarity index 94%
rename from apps/web/components/DashboardPages/UI/BreadCrumbs.tsx
rename to apps/web/components/Dashboard/UI/BreadCrumbs.tsx
index 48e2be50..664482d3 100644
--- a/apps/web/components/DashboardPages/UI/BreadCrumbs.tsx
+++ b/apps/web/components/Dashboard/UI/BreadCrumbs.tsx
@@ -1,4 +1,4 @@
-import { useCourse } from '@components/DashboardPages/CourseContext'
+import { useCourse } from '@components/Contexts/CourseContext'
import { Book, ChevronRight, User } from 'lucide-react'
import Link from 'next/link'
import React, { use, useEffect } from 'react'
diff --git a/apps/web/components/DashboardPages/UI/CourseOverviewTop.tsx b/apps/web/components/Dashboard/UI/CourseOverviewTop.tsx
similarity index 61%
rename from apps/web/components/DashboardPages/UI/CourseOverviewTop.tsx
rename to apps/web/components/Dashboard/UI/CourseOverviewTop.tsx
index c7e98093..1400b4d4 100644
--- a/apps/web/components/DashboardPages/UI/CourseOverviewTop.tsx
+++ b/apps/web/components/Dashboard/UI/CourseOverviewTop.tsx
@@ -1,21 +1,28 @@
-import { useCourse } from "@components/DashboardPages/CourseContext";
+import { useCourse } from "@components/Contexts/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";
+import { getUriWithOrg } from "@services/config/config";
+import { useOrg } from "@components/Contexts/OrgContext";
+import { getCourseThumbnailMediaDirectory } from "@services/media/media";
+import Link from "next/link";
export function CourseOverviewTop({ params }: { params: CourseOverviewParams }) {
const course = useCourse() as any;
+ const org = useOrg() as any;
useEffect(() => { }
- , [course])
+ , [course, org])
return (
<>
-
+
+

+
Course
{course.courseStructure.name}
diff --git a/apps/web/components/Dashboard/UI/LeftMenu.tsx b/apps/web/components/Dashboard/UI/LeftMenu.tsx
new file mode 100644
index 00000000..fc563d71
--- /dev/null
+++ b/apps/web/components/Dashboard/UI/LeftMenu.tsx
@@ -0,0 +1,39 @@
+'use client';
+import { useOrg } from '@components/Contexts/OrgContext';
+import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import { ArrowLeft, Book, Home } from 'lucide-react'
+import Link from 'next/link'
+import React, { use, useEffect } from 'react'
+
+function LeftMenu() {
+ const org = useOrg() as any;
+
+ useEffect(() => {
+
+ }
+ , [org])
+
+
+ return (
+
+ )
+}
+
+export default LeftMenu
+
diff --git a/apps/web/components/DashboardPages/UI/SaveState.tsx b/apps/web/components/Dashboard/UI/SaveState.tsx
similarity index 97%
rename from apps/web/components/DashboardPages/UI/SaveState.tsx
rename to apps/web/components/Dashboard/UI/SaveState.tsx
index 23fdbc7a..a13f6ef6 100644
--- a/apps/web/components/DashboardPages/UI/SaveState.tsx
+++ b/apps/web/components/Dashboard/UI/SaveState.tsx
@@ -2,7 +2,7 @@
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 { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext'
import { Check, SaveAllIcon, Timer } from 'lucide-react'
import { useRouter } from 'next/navigation';
import React, { useEffect } from 'react'
diff --git a/apps/web/components/DashboardPages/UI/LeftMenu.tsx b/apps/web/components/DashboardPages/UI/LeftMenu.tsx
deleted file mode 100644
index 99c335ef..00000000
--- a/apps/web/components/DashboardPages/UI/LeftMenu.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
-import { Book } from 'lucide-react'
-import Link from 'next/link'
-import React from 'react'
-
-function LeftMenu() {
- return (
-
- )
-}
-
-export default LeftMenu
-
diff --git a/apps/web/components/Objects/Editor/Editor.tsx b/apps/web/components/Objects/Editor/Editor.tsx
index c1268cc0..a7d6b319 100644
--- a/apps/web/components/Objects/Editor/Editor.tsx
+++ b/apps/web/components/Objects/Editor/Editor.tsx
@@ -45,6 +45,7 @@ interface Editor {
activity: any;
orgslug: string
course: any;
+ org: any;
setContent: (content: string) => void;
}
@@ -146,7 +147,7 @@ function Editor(props: Editor) {
-
+
{" "}
diff --git a/apps/web/components/Objects/Editor/EditorWrapper.tsx b/apps/web/components/Objects/Editor/EditorWrapper.tsx
index a82ea11d..8dccbbcc 100644
--- a/apps/web/components/Objects/Editor/EditorWrapper.tsx
+++ b/apps/web/components/Objects/Editor/EditorWrapper.tsx
@@ -11,6 +11,7 @@ interface EditorWrapperProps {
activity: any;
course: any
orgslug: string;
+ org: any;
}
function EditorWrapper(props: EditorWrapperProps): JSX.Element {
@@ -49,7 +50,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
} else {
return <>
- ;
+ ;
>
}
diff --git a/apps/web/components/Objects/Other/CollectionThumbnail.tsx b/apps/web/components/Objects/Other/CollectionThumbnail.tsx
index a7c23dac..242d935b 100644
--- a/apps/web/components/Objects/Other/CollectionThumbnail.tsx
+++ b/apps/web/components/Objects/Other/CollectionThumbnail.tsx
@@ -1,4 +1,5 @@
"use client";
+import { useOrg } from '@components/Contexts/OrgContext';
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
import { getUriWithOrg } from '@services/config/config'
@@ -21,6 +22,7 @@ const removeCollectionPrefix = (collectionid: string) => {
}
function CollectionThumbnail(props: PropsType) {
+ const org = useOrg() as any;
return (
@@ -28,7 +30,7 @@ function CollectionThumbnail(props: PropsType) {
{props.collection.courses.slice(0, 2).map((course: any) => (
<>
-
+
>
diff --git a/apps/web/components/Objects/Other/CourseThumbnail.tsx b/apps/web/components/Objects/Other/CourseThumbnail.tsx
index 4f578486..4d78c53c 100644
--- a/apps/web/components/Objects/Other/CourseThumbnail.tsx
+++ b/apps/web/components/Objects/Other/CourseThumbnail.tsx
@@ -1,4 +1,5 @@
"use client";
+import { useOrg } from '@components/Contexts/OrgContext';
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal';
import { getUriWithOrg } from '@services/config/config';
@@ -8,7 +9,7 @@ import { revalidateTags } from '@services/utils/ts/requests';
import { FileEdit, X } from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
-import React from 'react'
+import React, { use, useEffect } from 'react'
type PropsType = {
course: any,
@@ -22,6 +23,7 @@ function removeCoursePrefix(course_uuid: string) {
function CourseThumbnail(props: PropsType) {
const router = useRouter();
+ const org = useOrg() as any;
async function deleteCourses(course_uuid: any) {
await deleteCourseFromBackend(course_uuid);
@@ -30,12 +32,16 @@ function CourseThumbnail(props: PropsType) {
router.refresh();
}
+ useEffect(() => {
+
+ }, [org]);
+
return (
-
-
+
+
{props.course.name}
@@ -45,10 +51,10 @@ function CourseThumbnail(props: PropsType) {
const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => {
return (
-
+