From 548de5da7e96bc2d66d0179ffa923b93b27e271c Mon Sep 17 00:00:00 2001 From: swve Date: Thu, 22 Jun 2023 22:09:11 +0200 Subject: [PATCH 1/5] fix: trail issues --- src/services/trail.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/services/trail.py b/src/services/trail.py index bb4f88eb..91634629 100644 --- a/src/services/trail.py +++ b/src/services/trail.py @@ -105,9 +105,22 @@ async def get_user_trail_with_orgslug(request: Request, user: PublicUser, org_sl org = await orgs.find_one({"slug": org_slug}) trail = await trails.find_one({"user_id": user.user_id, "org_id": org["org_id"]}) + if not trail: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + + # Check if these courses still exist in the database + for course in trail["courses"]: + + course_id = course["course_id"] + course_object = await courses_mongo.find_one({"course_id": course_id}, {"_id": 0}) + if not course_object: + trail["courses"].remove(course) + continue + + course["course_object"] = course_object + for courses in trail["courses"]: course_id = courses["course_id"] From 6b0df500ef76281de576c0303cbe333c93098484 Mon Sep 17 00:00:00 2001 From: swve Date: Fri, 23 Jun 2023 19:27:52 +0200 Subject: [PATCH 2/5] fix: logo upload issue --- src/services/orgs/logos.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/orgs/logos.py b/src/services/orgs/logos.py index e2e4bff8..3faa6539 100644 --- a/src/services/orgs/logos.py +++ b/src/services/orgs/logos.py @@ -1,5 +1,6 @@ import os from uuid import uuid4 +from fastapi import HTTPException, status async def upload_org_logo(logo_file): @@ -15,7 +16,10 @@ async def upload_org_logo(logo_file): f.close() except Exception: - return {"message": "There was an error uploading the file"} + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="There was an error uploading the file", + ) finally: logo_file.file.close() From bfd3f078372cdc5f2c8e8ac1d384ccddb192fa46 Mon Sep 17 00:00:00 2001 From: swve Date: Sun, 25 Jun 2023 17:30:04 +0200 Subject: [PATCH 3/5] feat: redesign trail + add quit course button --- .../activity/[activityid]/activity.tsx | 2 +- .../(withmenu)/course/[courseid]/course.tsx | 3 +- .../orgs/[orgslug]/(withmenu)/trail/page.tsx | 125 ++++-------------- .../orgs/[orgslug]/(withmenu)/trail/trail.tsx | 38 ++++++ .../Objects/Loaders/PageLoading.tsx | 4 +- front/components/Objects/Menu/Menu.tsx | 4 +- .../Pages/Courses/ActivityIndicators.tsx | 3 +- .../Pages/Trail/TrailCourseElement.tsx | 60 +++++++++ 8 files changed, 133 insertions(+), 106 deletions(-) create mode 100644 front/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx create mode 100644 front/components/Pages/Trail/TrailCourseElement.tsx diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx index de2fb637..dc6a218f 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/activity.tsx @@ -56,7 +56,7 @@ function ActivityClient(props: ActivityClientProps) {

{course.course.name}

- +
diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/course.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/course.tsx index fc323f5c..b8227993 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/course.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/course.tsx @@ -34,7 +34,6 @@ const CourseClient = (props: any) => { router.refresh(); } - console.log(course); return ( <> @@ -54,7 +53,7 @@ const CourseClient = (props: any) => {
- +
diff --git a/front/app/orgs/[orgslug]/(withmenu)/trail/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/trail/page.tsx index b32c1463..2eea9a82 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/trail/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/trail/page.tsx @@ -1,104 +1,33 @@ -"use client"; -import PageLoading from "@components/Objects/Loaders/PageLoading"; -import TypeOfContentTitle from "@components/StyledElements/Titles/TypeOfContentTitle"; -import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; -import { getAPIUrl, getBackendUrl } from "@services/config/config"; -import { swrFetcher } from "@services/utils/ts/requests"; import React from "react"; -import { styled } from "styled-components"; -import useSWR from "swr"; +import { Metadata } from "next"; +import { getOrganizationContextInfo } from "@services/organizations/orgs"; +import Trail from "./trail"; -function Trail(params: any) { - let orgslug = params.params.orgslug; - const { data: trail, error: error } = useSWR(`${getAPIUrl()}trail/org_slug/${orgslug}/trail`, swrFetcher); - - - return ( - - - {!trail ? ( - - ) : ( -
- {trail.courses.map((course: any) => ( - - - - - - -

Course

-

{course.course_object.name}

-
-
- -
- ))} -
- )} -
- ); +type MetadataProps = { + params: { orgslug: string }; + searchParams: { [key: string]: string | string[] | undefined }; +}; + +export async function generateMetadata( + { params }: MetadataProps, +): Promise { + + // Get Org context information + const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); + return { + title: "Trail — " + org.name, + description: 'Check your progress using trail and easily navigate through your courses.', + }; } -export default Trail; +const TrailPage = async (params: any) => { + let orgslug = params.params.orgslug; + return ( +
+ +
+ ); +}; - -const TrailMetadata = styled.div` - display: flex; - flex-direction: row; - width: 100%; - height: 100%; -`; -const TrailBox = styled.div` - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - margin-top: 20px; - margin-bottom: 20px; - padding: 15px; - border-radius: 7px; - box-shadow: 0px 13px 33px -13px rgba(0, 0, 0, 0.206); - background: #ffffff; -`; - -const TrailThumbnail = styled.div` - padding-right: 30px; - height: 100%; - border-radius: 7px 0px 0px 7px; - - img { - width: 60px; - border-radius: 7px; - } -`; - -const TrailInfo = styled.div` - width: 100%; - height: 100%; - background: #ffffff; - border-radius: 0px 7px 7px 0px; - - h2 { - font-size: 12px; - color: #2b2b2b; - padding: 0; - margin: 0; - } - - h3 { - font-size: 23px; - color: #2b2b2b; - padding: 0; - margin: 0; - } -`; - -const TrailProgress = styled.div` - margin-top: 10px; - border-radius: 20px; - height: 10px; - width: ${(props: any) => props.progress + "%"}; - background: #06a487; -`; +export default TrailPage; \ No newline at end of file diff --git a/front/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx b/front/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx new file mode 100644 index 00000000..38015cce --- /dev/null +++ b/front/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx @@ -0,0 +1,38 @@ +"use client"; +import PageLoading from "@components/Objects/Loaders/PageLoading"; +import TrailCourseElement from "@components/Pages/Trail/TrailCourseElement"; +import TypeOfContentTitle from "@components/StyledElements/Titles/TypeOfContentTitle"; +import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; +import { getAPIUrl } from "@services/config/config"; +import { removeCourse } from "@services/courses/activity"; +import { revalidateTags, swrFetcher } from "@services/utils/ts/requests"; +import React from "react"; +import useSWR, { mutate } from "swr"; + +function Trail(params: any) { + let orgslug = params.orgslug; + const { data: trail, error: error } = useSWR(`${getAPIUrl()}trail/org_slug/${orgslug}/trail`, swrFetcher); + + + return ( + + + {!trail ? ( + + ) : ( +
+ {trail.courses.map((course: any) => ( + + ))} + +
+ )} +
+ ); +} + +export default Trail; + + + + diff --git a/front/components/Objects/Loaders/PageLoading.tsx b/front/components/Objects/Loaders/PageLoading.tsx index 52fa3a61..809b2b80 100644 --- a/front/components/Objects/Loaders/PageLoading.tsx +++ b/front/components/Objects/Loaders/PageLoading.tsx @@ -5,8 +5,8 @@ function PageLoading() {
- - + +
diff --git a/front/components/Objects/Menu/Menu.tsx b/front/components/Objects/Menu/Menu.tsx index 4baf3e1d..78478a8d 100644 --- a/front/components/Objects/Menu/Menu.tsx +++ b/front/components/Objects/Menu/Menu.tsx @@ -9,7 +9,7 @@ import { HeaderProfileBox } from "@components/Security/HeaderProfileBox"; export const Menu = async (props: any) => { const orgslug = props.orgslug; const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); - + return ( <> @@ -88,7 +88,7 @@ const LearnHouseLogo = () => { - + diff --git a/front/components/Pages/Courses/ActivityIndicators.tsx b/front/components/Pages/Courses/ActivityIndicators.tsx index 5749afda..92e8188a 100644 --- a/front/components/Pages/Courses/ActivityIndicators.tsx +++ b/front/components/Pages/Courses/ActivityIndicators.tsx @@ -7,13 +7,14 @@ import React from 'react' interface Props { course: any orgslug: string + course_id: string current_activity?: any } function ActivityIndicators(props: Props) { const course = props.course const orgslug = props.orgslug - const courseid = course.course.course_id.replace("course_", "") + const courseid = props.course_id.replace("course_", "") const done_activity_style = 'bg-teal-600 hover:bg-teal-700' const black_activity_style = 'bg-black hover:bg-gray-700' diff --git a/front/components/Pages/Trail/TrailCourseElement.tsx b/front/components/Pages/Trail/TrailCourseElement.tsx new file mode 100644 index 00000000..dd32c5c8 --- /dev/null +++ b/front/components/Pages/Trail/TrailCourseElement.tsx @@ -0,0 +1,60 @@ +'use client'; +import { getAPIUrl, getBackendUrl, getUriWithOrg } from '@services/config/config'; +import { removeCourse } from '@services/courses/activity'; +import { revalidateTags } from '@services/utils/ts/requests'; +import Link from 'next/link'; +import { mutate } from 'swr'; + +interface TrailCourseElementProps { + course: any + orgslug: string +} + +function TrailCourseElement(props: TrailCourseElementProps) { + const courseid = props.course.course_id.replace("course_", "") + const course = props.course + + async function quitCourse(course_id: string) { + // Close activity + let activity = await removeCourse(course_id, props.orgslug); + // Mutate course + revalidateTags(['courses']); + + // Mutate + mutate(`${getAPIUrl()}trail/org_slug/${props.orgslug}/trail`); + } + + return ( +
+ + +
+ +
+
+
+
+

Course

+
+

{course.course_object.name}

+
+

{course.progress}%

+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+ ) +} + +export default TrailCourseElement \ No newline at end of file From a748b398d0e473d3cc15cf54f6c15b1947d1ff3b Mon Sep 17 00:00:00 2001 From: swve Date: Sun, 25 Jun 2023 17:45:21 +0200 Subject: [PATCH 4/5] feat: disable trail link for non-auth users --- front/components/Objects/Menu/Menu.tsx | 49 ++++--------------- front/components/Objects/Menu/MenuLinks.tsx | 52 +++++++++++++++++++++ 2 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 front/components/Objects/Menu/MenuLinks.tsx diff --git a/front/components/Objects/Menu/Menu.tsx b/front/components/Objects/Menu/Menu.tsx index 78478a8d..98a7b41f 100644 --- a/front/components/Objects/Menu/Menu.tsx +++ b/front/components/Objects/Menu/Menu.tsx @@ -1,16 +1,18 @@ - +'use client'; import React from "react"; +import useSWR from "swr"; import Link from "next/link"; -import { getBackendUrl, getUriWithOrg } from "@services/config/config"; +import { getAPIUrl, getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getOrganizationContextInfo } from "@services/organizations/orgs"; import ClientComponentSkeleton from "@components/Utils/ClientComp"; import { HeaderProfileBox } from "@components/Security/HeaderProfileBox"; +import { swrFetcher } from "@services/utils/ts/requests"; +import MenuLinks from "./MenuLinks"; export const Menu = async (props: any) => { const orgslug = props.orgslug; const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); - return ( <>
@@ -32,11 +34,10 @@ export const Menu = async (props: any) => {
-
    - - - -
+ + + +
@@ -48,39 +49,7 @@ export const Menu = async (props: any) => { ); }; -const LinkItem = (props: any) => { - const link = props.link; - const orgslug = props.orgslug; - return ( - -
  • - {props.type == 'courses' && - <> - - - - Courses - } - {props.type == 'collections' && - <> - - - - Collections - } - - {props.type == 'trail' && - <> - - - - Trail - } -
  • - - ) -} const LearnHouseLogo = () => { diff --git a/front/components/Objects/Menu/MenuLinks.tsx b/front/components/Objects/Menu/MenuLinks.tsx new file mode 100644 index 00000000..c5576d96 --- /dev/null +++ b/front/components/Objects/Menu/MenuLinks.tsx @@ -0,0 +1,52 @@ +import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; +import { getUriWithOrg } from '@services/config/config'; +import Link from 'next/link'; +import React from 'react' + +function MenuLinks(props: { orgslug: string }) { + return ( +
    +
      + + + + + +
    +
    + ) +} +const LinkItem = (props: any) => { + const link = props.link; + const orgslug = props.orgslug; + return ( + +
  • + {props.type == 'courses' && + <> + + + + Courses + } + + {props.type == 'collections' && + <> + + + + Collections + } + + {props.type == 'trail' && + <> + + + + Trail + } +
  • + + ) +} +export default MenuLinks \ No newline at end of file From a2401661e80163d838cbe6b57647f58706015420 Mon Sep 17 00:00:00 2001 From: swve Date: Sun, 25 Jun 2023 18:42:05 +0200 Subject: [PATCH 5/5] feat: fix trail bugs --- src/services/trail.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/services/trail.py b/src/services/trail.py index 91634629..99705c3c 100644 --- a/src/services/trail.py +++ b/src/services/trail.py @@ -107,8 +107,7 @@ async def get_user_trail_with_orgslug(request: Request, user: PublicUser, org_sl trail = await trails.find_one({"user_id": user.user_id, "org_id": org["org_id"]}) if not trail: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + return Trail(masked=False, courses=[]) # Check if these courses still exist in the database for course in trail["courses"]: @@ -156,8 +155,7 @@ async def add_activity_to_trail(request: Request, user: PublicUser, course_id: trail = await trails.find_one({"user_id": user.user_id, "courses.course_id": courseid , "org_id": org_id}) if not trail: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Trail not found") + return Trail(masked=False, courses=[]) # if a trail has course_id in the courses array, then add the activity_id to the activities_marked_complete array for element in trail["courses"]: @@ -176,7 +174,7 @@ async def add_activity_to_trail(request: Request, user: PublicUser, course_id: # modify trail object await trails.replace_one({"trail_id": trail["trail_id"]}, trail) - return Trail(**trail) + return Trail(**trail.dict()) async def add_course_to_trail(request: Request, user: PublicUser, orgslug: str, course_id: str) -> Trail: @@ -186,19 +184,18 @@ async def add_course_to_trail(request: Request, user: PublicUser, orgslug: str, org = await orgs.find_one({"slug": orgslug}) - org = PublicOrganization(**org) - - + org = PublicOrganization(**org) trail = await trails.find_one( {"user_id": user.user_id, "org_id": org["org_id"]}) - - + if not trail: trail_to_insert = TrailInDB(trail_id=f"trail_{uuid4()}", user_id=user.user_id, org_id=org["org_id"], courses=[]) trail_to_insert = await trails.insert_one(trail_to_insert.dict()) + trail = await trails.find_one({"_id": trail_to_insert.inserted_id}) + # check if course is already present in the trail for element in trail["courses"]: if element["course_id"] == course_id: