mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
fix: activitiy issues
This commit is contained in:
parent
669270441b
commit
53f40f3f34
20 changed files with 138 additions and 105 deletions
|
|
@ -63,6 +63,7 @@ class FullCourseReadWithTrail(CourseBase):
|
||||||
course_uuid: str
|
course_uuid: str
|
||||||
creation_date: str
|
creation_date: str
|
||||||
update_date: str
|
update_date: str
|
||||||
|
org_id: int = Field(default=None, foreign_key="organization.id")
|
||||||
authors: List[UserRead]
|
authors: List[UserRead]
|
||||||
# Chapters, Activities
|
# Chapters, Activities
|
||||||
chapters: List[ChapterRead]
|
chapters: List[ChapterRead]
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,11 @@ async def api_get_chapter_activities(
|
||||||
return await get_activities(request, chapter_id, current_user, db_session)
|
return await get_activities(request, chapter_id, current_user, db_session)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{activity_id}")
|
@router.put("/{activity_uuid}")
|
||||||
async def api_update_activity(
|
async def api_update_activity(
|
||||||
request: Request,
|
request: Request,
|
||||||
activity_object: ActivityUpdate,
|
activity_object: ActivityUpdate,
|
||||||
activity_id: int,
|
activity_uuid: str,
|
||||||
current_user: PublicUser = Depends(get_current_user),
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
db_session=Depends(get_db_session),
|
db_session=Depends(get_db_session),
|
||||||
) -> ActivityRead:
|
) -> ActivityRead:
|
||||||
|
|
@ -74,7 +74,7 @@ async def api_update_activity(
|
||||||
Update activity by activity_id
|
Update activity by activity_id
|
||||||
"""
|
"""
|
||||||
return await update_activity(
|
return await update_activity(
|
||||||
request, activity_object, activity_id, current_user, db_session
|
request, activity_object, activity_uuid, current_user, db_session
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,11 @@ async def create_activity(
|
||||||
|
|
||||||
async def get_activity(
|
async def get_activity(
|
||||||
request: Request,
|
request: Request,
|
||||||
activity_id: str,
|
activity_uuid: str,
|
||||||
current_user: PublicUser,
|
current_user: PublicUser,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
statement = select(Activity).where(Activity.id == activity_id)
|
statement = select(Activity).where(Activity.activity_uuid == activity_uuid)
|
||||||
activity = db_session.exec(statement).first()
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
if not activity:
|
if not activity:
|
||||||
|
|
@ -106,11 +106,11 @@ async def get_activity(
|
||||||
async def update_activity(
|
async def update_activity(
|
||||||
request: Request,
|
request: Request,
|
||||||
activity_object: ActivityUpdate,
|
activity_object: ActivityUpdate,
|
||||||
activity_id: int,
|
activity_uuid: str,
|
||||||
current_user: PublicUser | AnonymousUser,
|
current_user: PublicUser | AnonymousUser,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
statement = select(Activity).where(Activity.id == activity_id)
|
statement = select(Activity).where(Activity.activity_uuid == activity_uuid)
|
||||||
activity = db_session.exec(statement).first()
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
if not activity:
|
if not activity:
|
||||||
|
|
@ -140,11 +140,11 @@ async def update_activity(
|
||||||
|
|
||||||
async def delete_activity(
|
async def delete_activity(
|
||||||
request: Request,
|
request: Request,
|
||||||
activity_id: str,
|
activity_uuid: str,
|
||||||
current_user: PublicUser | AnonymousUser,
|
current_user: PublicUser | AnonymousUser,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
):
|
):
|
||||||
statement = select(Activity).where(Activity.id == activity_id)
|
statement = select(Activity).where(Activity.activity_uuid == activity_uuid)
|
||||||
activity = db_session.exec(statement).first()
|
activity = db_session.exec(statement).first()
|
||||||
|
|
||||||
if not activity:
|
if not activity:
|
||||||
|
|
@ -160,7 +160,7 @@ async def delete_activity(
|
||||||
|
|
||||||
# Delete activity from chapter
|
# Delete activity from chapter
|
||||||
statement = select(ChapterActivity).where(
|
statement = select(ChapterActivity).where(
|
||||||
ChapterActivity.activity_id == activity_id
|
ChapterActivity.activity_id == activity.id
|
||||||
)
|
)
|
||||||
activity_chapter = db_session.exec(statement).first()
|
activity_chapter = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
from src.db.courses import Course
|
||||||
|
from src.db.organizations import Organization
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src.security.rbac.rbac import (
|
from src.security.rbac.rbac import (
|
||||||
authorization_verify_based_on_roles_and_authorship,
|
authorization_verify_based_on_roles_and_authorship,
|
||||||
|
|
@ -53,6 +55,14 @@ async def create_documentpdf_activity(
|
||||||
# get org_id
|
# get org_id
|
||||||
org_id = coursechapter.org_id
|
org_id = coursechapter.org_id
|
||||||
|
|
||||||
|
# Get org_uuid
|
||||||
|
statement = select(Organization).where(Organization.id == coursechapter.org_id)
|
||||||
|
organization = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
# Get course_uuid
|
||||||
|
statement = select(Course).where(Course.id == coursechapter.course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
# create activity uuid
|
# create activity uuid
|
||||||
activity_uuid = f"activity_{uuid4()}"
|
activity_uuid = f"activity_{uuid4()}"
|
||||||
|
|
||||||
|
|
@ -113,7 +123,12 @@ async def create_documentpdf_activity(
|
||||||
# upload pdf
|
# upload pdf
|
||||||
if pdf_file:
|
if pdf_file:
|
||||||
# get pdffile format
|
# get pdffile format
|
||||||
await upload_pdf(pdf_file, activity.id, org_id, coursechapter.course_id)
|
await upload_pdf(
|
||||||
|
pdf_file,
|
||||||
|
activity.activity_uuid,
|
||||||
|
organization.org_uuid,
|
||||||
|
course.course_uuid,
|
||||||
|
)
|
||||||
|
|
||||||
# Insert ChapterActivity link in DB
|
# Insert ChapterActivity link in DB
|
||||||
db_session.add(activity_chapter)
|
db_session.add(activity_chapter)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
|
|
||||||
from src.services.utils.upload_content import upload_content
|
from src.services.utils.upload_content import upload_content
|
||||||
|
|
||||||
|
|
||||||
async def upload_pdf(pdf_file, activity_id, org_id, course_id):
|
async def upload_pdf(pdf_file, activity_uuid, org_uuid, course_uuid):
|
||||||
contents = pdf_file.file.read()
|
contents = pdf_file.file.read()
|
||||||
pdf_format = pdf_file.filename.split(".")[-1]
|
pdf_format = pdf_file.filename.split(".")[-1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await upload_content(
|
await upload_content(
|
||||||
f"courses/{course_id}/activities/{activity_id}/documentpdf",
|
f"courses/{course_uuid}/activities/{activity_uuid}/documentpdf",
|
||||||
org_id,
|
org_uuid,
|
||||||
contents,
|
contents,
|
||||||
f"documentpdf.{pdf_format}",
|
f"documentpdf.{pdf_format}",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
from src.services.utils.upload_content import upload_content
|
from src.services.utils.upload_content import upload_content
|
||||||
|
|
||||||
|
|
||||||
async def upload_video(video_file, activity_id, org_id, course_id):
|
async def upload_video(video_file, activity_uuid, org_uuid, course_uuid):
|
||||||
contents = video_file.file.read()
|
contents = video_file.file.read()
|
||||||
video_format = video_file.filename.split(".")[-1]
|
video_format = video_file.filename.split(".")[-1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await upload_content(
|
await upload_content(
|
||||||
f"courses/{course_id}/activities/{activity_id}/video",
|
f"courses/{course_uuid}/activities/{activity_uuid}/video",
|
||||||
org_id,
|
org_uuid,
|
||||||
contents,
|
contents,
|
||||||
f"video.{video_format}",
|
f"video.{video_format}",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
from src.db.courses import Course
|
||||||
|
from src.db.organizations import Organization
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
@ -52,6 +54,14 @@ async def create_video_activity(
|
||||||
detail="CourseChapter not found",
|
detail="CourseChapter not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get org_uuid
|
||||||
|
statement = select(Organization).where(Organization.id == coursechapter.org_id)
|
||||||
|
organization = db_session.exec(statement).first()
|
||||||
|
|
||||||
|
# Get course_uuid
|
||||||
|
statement = select(Course).where(Course.id == coursechapter.course_id)
|
||||||
|
course = db_session.exec(statement).first()
|
||||||
|
|
||||||
# generate activity_uuid
|
# generate activity_uuid
|
||||||
activity_uuid = str(f"activity_{uuid4()}")
|
activity_uuid = str(f"activity_{uuid4()}")
|
||||||
|
|
||||||
|
|
@ -104,7 +114,10 @@ async def create_video_activity(
|
||||||
if video_file:
|
if video_file:
|
||||||
# get videofile format
|
# get videofile format
|
||||||
await upload_video(
|
await upload_video(
|
||||||
video_file, activity.id, coursechapter.org_id, coursechapter.course_id
|
video_file,
|
||||||
|
activity.activity_uuid,
|
||||||
|
organization.org_uuid,
|
||||||
|
course.course_uuid,
|
||||||
)
|
)
|
||||||
|
|
||||||
# update chapter
|
# update chapter
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { cookies } from "next/headers";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import { getActivityWithAuthHeader } from "@services/courses/activities";
|
import { getActivityWithAuthHeader } from "@services/courses/activities";
|
||||||
import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth";
|
import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth";
|
||||||
import { getOrganizationContextInfo } from "@services/organizations/orgs";
|
import { getOrganizationContextInfo, getOrganizationContextInfoWithId } from "@services/organizations/orgs";
|
||||||
|
|
||||||
type MetadataProps = {
|
type MetadataProps = {
|
||||||
params: { orgslug: string, courseid: string, activityid: string };
|
params: { orgslug: string, courseid: string, activityid: string };
|
||||||
|
|
@ -22,27 +22,25 @@ export async function generateMetadata(
|
||||||
const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null)
|
const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `Edit - ${course_meta.course.name} Activity`,
|
title: `Edit - ${course_meta.name} Activity`,
|
||||||
description: course_meta.course.mini_description,
|
description: course_meta.mini_description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditActivity = async (params: any) => {
|
const EditActivity = async (params: any) => {
|
||||||
const cookieStore = cookies();
|
const cookieStore = cookies();
|
||||||
const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore)
|
const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore)
|
||||||
const activityid = params.params.activityid;
|
const activityuuid = params.params.activityuuid;
|
||||||
const courseid = params.params.courseid;
|
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 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)
|
const activity = await getActivityWithAuthHeader(activityuuid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null)
|
||||||
|
const org = await getOrganizationContextInfoWithId(courseInfo.org_id, { revalidate: 1800, tags: ['organizations'] });
|
||||||
|
console.log('courseInfo', courseInfo )
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<EditorWrapper org={org} orgslug={orgslug} course={courseInfo} activity={activity} content={activity.content}></EditorWrapper>
|
<EditorWrapper org={org} course={courseInfo} activity={activity} content={activity.content}></EditorWrapper>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -15,7 +15,7 @@ import { useOrg } from "@components/Contexts/OrgContext";
|
||||||
|
|
||||||
interface ActivityClientProps {
|
interface ActivityClientProps {
|
||||||
activityid: string;
|
activityid: string;
|
||||||
courseid: string;
|
courseuuid: string;
|
||||||
orgslug: string;
|
orgslug: string;
|
||||||
activity: any;
|
activity: any;
|
||||||
course: any;
|
course: any;
|
||||||
|
|
@ -24,7 +24,7 @@ interface ActivityClientProps {
|
||||||
|
|
||||||
function ActivityClient(props: ActivityClientProps) {
|
function ActivityClient(props: ActivityClientProps) {
|
||||||
const activityid = props.activityid;
|
const activityid = props.activityid;
|
||||||
const courseid = props.courseid;
|
const courseuuid = props.courseuuid;
|
||||||
const orgslug = props.orgslug;
|
const orgslug = props.orgslug;
|
||||||
const activity = props.activity;
|
const activity = props.activity;
|
||||||
const course = props.course;
|
const course = props.course;
|
||||||
|
|
@ -35,7 +35,7 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
course.chapters.forEach((chapter: any) => {
|
course.chapters.forEach((chapter: any) => {
|
||||||
if (chapter.id === chapterId) {
|
if (chapter.id === chapterId) {
|
||||||
chapterName = chapter.name;
|
chapterName = chapter.name;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return chapterName;
|
return chapterName;
|
||||||
}
|
}
|
||||||
|
|
@ -48,16 +48,16 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
<div className="space-y-4 pt-4">
|
<div className="space-y-4 pt-4">
|
||||||
<div className="flex space-x-6">
|
<div className="flex space-x-6">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Link href={getUriWithOrg(orgslug, "") + `/course/${courseid}`}>
|
<Link href={getUriWithOrg(orgslug, "") + `/course/${courseuuid}`}>
|
||||||
<img className="w-[100px] h-[57px] rounded-md drop-shadow-md" src={`${getCourseThumbnailMediaDirectory(org?.org_uuid, course.course.course_uuid, course.course.thumbnail_image)}`} alt="" />
|
<img className="w-[100px] h-[57px] rounded-md drop-shadow-md" src={`${getCourseThumbnailMediaDirectory(org?.org_uuid, course.course_uuid, course.thumbnail_image)}`} alt="" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col -space-y-1">
|
<div className="flex flex-col -space-y-1">
|
||||||
<p className="font-bold text-gray-700 text-md">Course </p>
|
<p className="font-bold text-gray-700 text-md">Course </p>
|
||||||
<h1 className="font-bold text-gray-950 text-2xl first-letter:uppercase" >{course.course.name}</h1>
|
<h1 className="font-bold text-gray-950 text-2xl first-letter:uppercase" >{course.name}</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ActivityIndicators course_uuid={courseid} current_activity={activityid} orgslug={orgslug} course={course} />
|
<ActivityIndicators course_uuid={courseuuid} current_activity={activityid} orgslug={orgslug} course={course} />
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex flex-col -space-y-1">
|
<div className="flex flex-col -space-y-1">
|
||||||
|
|
@ -66,19 +66,19 @@ function ActivityClient(props: ActivityClientProps) {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<AuthenticatedClientElement checkMethod="authentication">
|
<AuthenticatedClientElement checkMethod="authentication">
|
||||||
<MarkStatus activityid={activityid} course={course} orgslug={orgslug} courseid={courseid} />
|
<MarkStatus activityid={activityid} course={course} orgslug={orgslug} courseid={courseuuid} />
|
||||||
|
|
||||||
</AuthenticatedClientElement>
|
</AuthenticatedClientElement>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activity ? (
|
{activity ? (
|
||||||
<div className={`p-7 pt-4 drop-shadow-sm rounded-lg ${activity.type == 'dynamic' ? 'bg-white' : 'bg-zinc-950'}`}>
|
<div className={`p-7 pt-4 drop-shadow-sm rounded-lg ${activity.activity_type == 'TYPE_DYNAMIC' ? 'bg-white' : 'bg-zinc-950'}`}>
|
||||||
<div>
|
<div>
|
||||||
{activity.type == "dynamic" && <Canva content={activity.content} activity={activity} />}
|
{activity.activity_type == "TYPE_DYNAMIC" && <Canva content={activity.content} activity={activity} />}
|
||||||
{/* todo : use apis & streams instead of this */}
|
{/* todo : use apis & streams instead of this */}
|
||||||
{activity.type == "video" && <VideoActivity course={course} activity={activity} />}
|
{activity.activity_type == "TYPE_VIDEO" && <VideoActivity course={course} activity={activity} />}
|
||||||
{activity.type == "documentpdf" && <DocumentPdfActivity course={course} activity={activity} />}
|
{activity.activity_type == "TYPE_DOCUMENT" && <DocumentPdfActivity course={course} activity={activity} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (<div></div>)}
|
) : (<div></div>)}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshToke
|
||||||
|
|
||||||
|
|
||||||
type MetadataProps = {
|
type MetadataProps = {
|
||||||
params: { orgslug: string, courseid: string, activityid: string };
|
params: { orgslug: string, courseuuid: string, activityid: string };
|
||||||
searchParams: { [key: string]: string | string[] | undefined };
|
searchParams: { [key: string]: string | string[] | undefined };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -20,14 +20,14 @@ export async function generateMetadata(
|
||||||
|
|
||||||
// Get Org context information
|
// Get Org context information
|
||||||
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
|
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
|
||||||
const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null)
|
const course_meta = await getCourseMetadataWithAuthHeader(params.courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null)
|
||||||
const activity = await getActivityWithAuthHeader(params.activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null)
|
const activity = await getActivityWithAuthHeader(params.activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null)
|
||||||
|
|
||||||
// SEO
|
// SEO
|
||||||
return {
|
return {
|
||||||
title: activity.name + ` — ${course_meta.course.name} Course`,
|
title: activity.name + ` — ${course_meta.name} Course`,
|
||||||
description: course_meta.course.mini_description,
|
description: course_meta.description,
|
||||||
keywords: course_meta.course.learnings,
|
keywords: course_meta.learnings,
|
||||||
robots: {
|
robots: {
|
||||||
index: true,
|
index: true,
|
||||||
follow: true,
|
follow: true,
|
||||||
|
|
@ -39,11 +39,11 @@ export async function generateMetadata(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: activity.name + ` — ${course_meta.course.name} Course`,
|
title: activity.name + ` — ${course_meta.name} Course`,
|
||||||
description: course_meta.course.mini_description,
|
description: course_meta.description,
|
||||||
type: activity.type === 'video' ? 'video.other' : 'article',
|
type: activity.type === 'video' ? 'video.other' : 'article',
|
||||||
publishedTime: course_meta.course.creationDate,
|
publishedTime: course_meta.creation_date,
|
||||||
tags: course_meta.course.learnings,
|
tags: course_meta.learnings,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -52,16 +52,16 @@ const ActivityPage = async (params: any) => {
|
||||||
const cookieStore = cookies();
|
const cookieStore = cookies();
|
||||||
const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore)
|
const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore)
|
||||||
const activityid = params.params.activityid;
|
const activityid = params.params.activityid;
|
||||||
const courseid = params.params.courseid;
|
const courseuuid = params.params.courseuuid;
|
||||||
const orgslug = params.params.orgslug;
|
const orgslug = params.params.orgslug;
|
||||||
|
|
||||||
const course_meta = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null)
|
const course_meta = await getCourseMetadataWithAuthHeader(courseuuid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null)
|
||||||
const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null)
|
const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ActivityClient
|
<ActivityClient
|
||||||
activityid={activityid}
|
activityid={activityid}
|
||||||
courseid={courseid}
|
courseuuid={courseuuid}
|
||||||
orgslug={orgslug}
|
orgslug={orgslug}
|
||||||
activity={activity}
|
activity={activity}
|
||||||
course={course_meta}
|
course={course_meta}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export type CourseOverviewParams = {
|
||||||
subpage: string
|
subpage: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CourseStructureContext = createContext({}) as any;
|
|
||||||
|
|
||||||
function CourseOverviewPage({ params }: { params: CourseOverviewParams }) {
|
function CourseOverviewPage({ params }: { params: CourseOverviewParams }) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ function ActivityElement(props: ActivitiyElementProps) {
|
||||||
const [selectedActivity, setSelectedActivity] = React.useState<string | undefined>(undefined);
|
const [selectedActivity, setSelectedActivity] = React.useState<string | undefined>(undefined);
|
||||||
|
|
||||||
async function deleteActivityUI() {
|
async function deleteActivityUI() {
|
||||||
await deleteActivity(props.activity.id);
|
await deleteActivity(props.activity.activity_uuid);
|
||||||
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`);
|
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`);
|
||||||
await revalidateTags(['courses'], props.orgslug);
|
await revalidateTags(['courses'], props.orgslug);
|
||||||
router.refresh();
|
router.refresh();
|
||||||
|
|
@ -80,14 +80,14 @@ function ActivityElement(props: ActivitiyElementProps) {
|
||||||
<div className="flex flex-row space-x-2">
|
<div className="flex flex-row space-x-2">
|
||||||
{props.activity.activity_type === "TYPE_DYNAMIC" && <>
|
{props.activity.activity_type === "TYPE_DYNAMIC" && <>
|
||||||
<Link
|
<Link
|
||||||
href={''}
|
href={getUriWithOrg(props.orgslug, "") + `/course/${props.course_uuid.replace("course_", "")}/activity/${props.activity.activity_uuid.replace("activity_", "")}/edit`}
|
||||||
className=" hover:cursor-pointer p-1 px-3 bg-sky-700 rounded-md items-center"
|
className=" hover:cursor-pointer p-1 px-3 bg-sky-700 rounded-md items-center"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer">
|
||||||
<div className="text-sky-100 font-bold text-xs" >Edit </div>
|
<div className="text-sky-100 font-bold text-xs" >Edit </div>
|
||||||
</Link>
|
</Link>
|
||||||
</>}
|
</>}
|
||||||
<Link
|
<Link
|
||||||
href={''}
|
href={getUriWithOrg(props.orgslug, "") + `/course/${props.course_uuid.replace("course_", "")}/activity/${props.activity.activity_uuid.replace("activity_", "")}`}
|
||||||
className=" hover:cursor-pointer p-1 px-3 bg-gray-200 rounded-md"
|
className=" hover:cursor-pointer p-1 px-3 bg-gray-200 rounded-md"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer">
|
||||||
<Eye strokeWidth={2} size={15} className="text-gray-600" />
|
<Eye strokeWidth={2} size={15} className="text-gray-600" />
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import ChapterElement from './DraggableElements/ChapterElement';
|
||||||
import PageLoading from '@components/Objects/Loaders/PageLoading';
|
import PageLoading from '@components/Objects/Loaders/PageLoading';
|
||||||
import { createChapter, updateCourseOrderStructure } from '@services/courses/chapters';
|
import { createChapter, updateCourseOrderStructure } from '@services/courses/chapters';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { CourseStructureContext } from 'app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page';
|
|
||||||
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
|
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
|
||||||
import { Hexagon } from 'lucide-react';
|
import { Hexagon } from 'lucide-react';
|
||||||
import Modal from '@components/StyledElements/Modal/Modal';
|
import Modal from '@components/StyledElements/Modal/Modal';
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
|
import { useOrg } from "@components/Contexts/OrgContext";
|
||||||
import { getBackendUrl } from "@services/config/config";
|
import { getBackendUrl } from "@services/config/config";
|
||||||
import { getActivityMediaDirectory } from "@services/media/media";
|
import { getActivityMediaDirectory } from "@services/media/media";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
function DocumentPdfActivity({ activity, course }: { activity: any; course: any }) {
|
function DocumentPdfActivity({ activity, course }: { activity: any; course: any }) {
|
||||||
|
const org = useOrg() as any;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
console.log(activity);
|
||||||
|
}, [activity, org]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="m-8 bg-zinc-900 rounded-md mt-14">
|
<div className="m-8 bg-zinc-900 rounded-md mt-14">
|
||||||
<iframe
|
<iframe
|
||||||
className="rounded-lg w-full h-[900px]"
|
className="rounded-lg w-full h-[900px]"
|
||||||
src={getActivityMediaDirectory(activity.org_id, activity.course_uuid, activity.activity_id, activity.content.documentpdf.filename, 'documentpdf')}
|
src={getActivityMediaDirectory(org?.org_uuid, course?.course_uuid, activity.activity_uuid, activity.content.filename, 'documentpdf')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,11 @@ import React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import YouTube from 'react-youtube';
|
import YouTube from 'react-youtube';
|
||||||
import { getActivityMediaDirectory } from "@services/media/media";
|
import { getActivityMediaDirectory } from "@services/media/media";
|
||||||
|
import { useOrg } from "@components/Contexts/OrgContext";
|
||||||
|
|
||||||
function VideoActivity({ activity, course }: { activity: any; course: any }) {
|
function VideoActivity({ activity, course }: { activity: any; course: any }) {
|
||||||
|
const org = useOrg() as any;
|
||||||
const [videoId, setVideoId] = React.useState('');
|
const [videoId, setVideoId] = React.useState('');
|
||||||
const [videoType, setVideoType] = React.useState('');
|
|
||||||
|
|
||||||
function getYouTubeEmbed(url: any) {
|
function getYouTubeEmbed(url: any) {
|
||||||
// Extract video ID from the YouTube URL
|
// Extract video ID from the YouTube URL
|
||||||
|
|
@ -24,42 +25,38 @@ function VideoActivity({ activity, course }: { activity: any; course: any }) {
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (activity.content.video) {
|
console.log(activity);
|
||||||
setVideoType('video');
|
}, [activity, org]);
|
||||||
}
|
|
||||||
if (activity.content.external_video) {
|
|
||||||
setVideoType('external_video');
|
|
||||||
setVideoId(getYouTubeEmbed(activity.content.external_video.uri).videoId);
|
|
||||||
}
|
|
||||||
}, [activity]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{videoType === 'video' && (
|
{activity &&
|
||||||
<div className="m-8 bg-zinc-900 rounded-md mt-14">
|
<>
|
||||||
<video className="rounded-lg w-full h-[500px]" controls
|
{activity.activity_sub_type === 'SUBTYPE_VIDEO_HOSTED' && (
|
||||||
src={getActivityMediaDirectory(activity.org_id, activity.course_uuid, activity.activity_id, activity.content.video.filename, 'video')}
|
<div className="m-8 bg-zinc-900 rounded-md mt-14">
|
||||||
></video>
|
<video className="rounded-lg w-full h-[500px]" controls
|
||||||
|
src={getActivityMediaDirectory(org?.org_uuid, course?.course_uuid, activity.activity_uuid, activity.content?.filename, 'video')}
|
||||||
|
></video>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{videoType === 'external_video' && (
|
{activity.activity_sub_type === 'SUBTYPE_VIDEO_YOUTUBE' && (
|
||||||
<div>
|
<div>
|
||||||
<YouTube
|
<YouTube
|
||||||
className="rounded-md overflow-hidden m-8 bg-zinc-900 mt-14"
|
className="rounded-md overflow-hidden m-8 bg-zinc-900 mt-14"
|
||||||
opts={
|
opts={
|
||||||
{
|
{
|
||||||
width: '1300',
|
width: '1300',
|
||||||
height: '500',
|
height: '500',
|
||||||
playerVars: {
|
playerVars: {
|
||||||
autoplay: 0,
|
autoplay: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
videoId={videoId} />
|
videoId={videoId} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}</>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ interface Editor {
|
||||||
ydoc: any;
|
ydoc: any;
|
||||||
provider: any;
|
provider: any;
|
||||||
activity: any;
|
activity: any;
|
||||||
orgslug: string
|
|
||||||
course: any;
|
course: any;
|
||||||
org: any;
|
org: any;
|
||||||
setContent: (content: string) => void;
|
setContent: (content: string) => void;
|
||||||
|
|
@ -52,10 +51,10 @@ interface Editor {
|
||||||
function Editor(props: Editor) {
|
function Editor(props: Editor) {
|
||||||
const auth: any = React.useContext(AuthContext);
|
const auth: any = React.useContext(AuthContext);
|
||||||
// remove course_ from course_uuid
|
// remove course_ from course_uuid
|
||||||
const course_uuid = props.course.course.course_uuid.substring(7);
|
const course_uuid = props.course.course_uuid.substring(7);
|
||||||
|
|
||||||
// remove activity_ from activity_id
|
// remove activity_ from activity_uuid
|
||||||
const activity_id = props.activity.activity_id.substring(9);
|
const activity_uuid = props.activity.activity_uuid.substring(9);
|
||||||
|
|
||||||
// Code Block Languages for Lowlight
|
// Code Block Languages for Lowlight
|
||||||
lowlight.register('html', html)
|
lowlight.register('html', html)
|
||||||
|
|
@ -147,11 +146,11 @@ function Editor(props: Editor) {
|
||||||
<EditorInfoLearnHouseLogo width={25} height={25} src={learnhouseIcon} alt="" />
|
<EditorInfoLearnHouseLogo width={25} height={25} src={learnhouseIcon} alt="" />
|
||||||
</Link>
|
</Link>
|
||||||
<Link target="_blank" href={`/course/${course_uuid}/edit`}>
|
<Link target="_blank" href={`/course/${course_uuid}/edit`}>
|
||||||
<EditorInfoThumbnail src={`${getCourseThumbnailMediaDirectory(props.org?.org_uuid, props.course.course.course_uuid, props.course.course.thumbnail_image)}`} alt=""></EditorInfoThumbnail>
|
<EditorInfoThumbnail src={`${getCourseThumbnailMediaDirectory(props.org?.org_uuid, props.course.course_uuid, props.course.thumbnail_image)}`} alt=""></EditorInfoThumbnail>
|
||||||
</Link>
|
</Link>
|
||||||
<EditorInfoDocName>
|
<EditorInfoDocName>
|
||||||
{" "}
|
{" "}
|
||||||
<b>{props.course.course.name}</b> <SlashIcon /> {props.activity.name}{" "}
|
<b>{props.course.name}</b> <SlashIcon /> {props.activity.name}{" "}
|
||||||
</EditorInfoDocName>
|
</EditorInfoDocName>
|
||||||
|
|
||||||
</EditorInfoWrapper>
|
</EditorInfoWrapper>
|
||||||
|
|
@ -162,13 +161,13 @@ function Editor(props: Editor) {
|
||||||
<EditorUsersSection>
|
<EditorUsersSection>
|
||||||
<EditorUserProfileWrapper>
|
<EditorUserProfileWrapper>
|
||||||
{!auth.isAuthenticated && <span>Loading</span>}
|
{!auth.isAuthenticated && <span>Loading</span>}
|
||||||
{auth.isAuthenticated && <Avvvatars value={auth.userInfo.user_object.user_id} style="shape" />}
|
{auth.isAuthenticated && <Avvvatars value={auth.userInfo.user_uuid} style="shape" />}
|
||||||
</EditorUserProfileWrapper>
|
</EditorUserProfileWrapper>
|
||||||
<DividerVerticalIcon style={{ marginTop: "auto", marginBottom: "auto", color: "grey", opacity: '0.5' }} />
|
<DividerVerticalIcon style={{ marginTop: "auto", marginBottom: "auto", color: "grey", opacity: '0.5' }} />
|
||||||
<EditorLeftOptionsSection className="space-x-2 pl-2 pr-3">
|
<EditorLeftOptionsSection className="space-x-2 pl-2 pr-3">
|
||||||
<div className="bg-sky-600 hover:bg-sky-700 transition-all ease-linear px-3 py-2 font-black text-sm shadow text-teal-100 rounded-lg hover:cursor-pointer" onClick={() => props.setContent(editor.getJSON())}> Save </div>
|
<div className="bg-sky-600 hover:bg-sky-700 transition-all ease-linear px-3 py-2 font-black text-sm shadow text-teal-100 rounded-lg hover:cursor-pointer" onClick={() => props.setContent(editor.getJSON())}> Save </div>
|
||||||
<ToolTip content="Preview">
|
<ToolTip content="Preview">
|
||||||
<Link target="_blank" href={`/course/${course_uuid}/activity/${activity_id}`}>
|
<Link target="_blank" href={`/course/${course_uuid}/activity/${activity_uuid}`}>
|
||||||
<div className="flex bg-neutral-600 hover:bg-neutral-700 transition-all ease-linear h-9 px-3 py-2 font-black justify-center items-center text-sm shadow text-neutral-100 rounded-lg hover:cursor-pointer">
|
<div className="flex bg-neutral-600 hover:bg-neutral-700 transition-all ease-linear h-9 px-3 py-2 font-black justify-center items-center text-sm shadow text-neutral-100 rounded-lg hover:cursor-pointer">
|
||||||
<Eye className="mx-auto items-center" size={15} />
|
<Eye className="mx-auto items-center" size={15} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ interface EditorWrapperProps {
|
||||||
content: string;
|
content: string;
|
||||||
activity: any;
|
activity: any;
|
||||||
course: any
|
course: any
|
||||||
orgslug: string;
|
|
||||||
org: any;
|
org: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +26,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
// setProviderState(provider);
|
// setProviderState(provider);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
activity.content = content;
|
activity.content = content;
|
||||||
|
|
||||||
toast.promise(
|
toast.promise(
|
||||||
updateActivity(activity, activity.activity_id),
|
updateActivity(activity, activity.activity_uuid),
|
||||||
{
|
{
|
||||||
loading: 'Saving...',
|
loading: 'Saving...',
|
||||||
success: <b>Activity saved!</b>,
|
success: <b>Activity saved!</b>,
|
||||||
|
|
@ -50,7 +50,7 @@ function EditorWrapper(props: EditorWrapperProps): JSX.Element {
|
||||||
} else {
|
} else {
|
||||||
return <>
|
return <>
|
||||||
<Toast></Toast>
|
<Toast></Toast>
|
||||||
<Editor org={props.org} orgslug={props.orgslug} course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
<Editor org={props.org} course={props.course} activity={props.activity} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
||||||
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@ export async function deleteActivity(activity_id: any) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getActivityWithAuthHeader(activity_id: any, next: any, access_token: string) {
|
export async function getActivityWithAuthHeader(activity_uuid: any, next: any, access_token: string) {
|
||||||
const result = await fetch(`${getAPIUrl()}activities/activity_${activity_id}`, RequestBodyWithAuthHeader("GET", null, next, access_token));
|
const result = await fetch(`${getAPIUrl()}activities/activity_${activity_uuid}`, RequestBodyWithAuthHeader("GET", null, next, access_token));
|
||||||
const res = await result.json();
|
const res = await result.json();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,16 @@ export async function getOrganizationContextInfo(org_slug: any, next: any) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getOrganizationContextInfoWithId(org_id: any, next: any) {
|
||||||
|
const result = await fetch(`${getAPIUrl()}orgs/${org_id}`, RequestBody("GET", null, next));
|
||||||
|
const res = await errorHandling(result);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getOrganizationContextInfoWithoutCredentials(org_slug: any, next: any) {
|
export async function getOrganizationContextInfoWithoutCredentials(org_slug: any, next: any) {
|
||||||
let HeadersConfig = new Headers({ "Content-Type": "application/json" });
|
let HeadersConfig = new Headers({ "Content-Type": "application/json" });
|
||||||
let options: any = {
|
let options: any = {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: HeadersConfig,
|
headers: HeadersConfig,
|
||||||
redirect: "follow",
|
redirect: "follow",
|
||||||
// Next.js
|
// Next.js
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue