diff --git a/src/components/cards/LearningModule.tsx b/src/components/cards/LearningModule.tsx index cd7c749c5..ae104ef6d 100644 --- a/src/components/cards/LearningModule.tsx +++ b/src/components/cards/LearningModule.tsx @@ -15,7 +15,7 @@ import { LearningModule } from "@/types/course"; /** * Component that displays related learning material with a title, description, and "Start now" button. */ -export function LearningModuleCard({ data }: { data: LearningModule }): JSX.Element { +export function LearningModuleCard({ data, url }: { data: LearningModule, url: string }): JSX.Element { const { t } = useTranslation() const { challenge, community, colors } = useMultiSelector({ challenge: (state: IRootState) => state.challenges.current, @@ -66,7 +66,7 @@ export function LearningModuleCard({ data }: { data: LearningModule }): JSX.Elem <>}
- + {t("communities.overview.challenge.learning.start")} diff --git a/src/components/sections/challenges/Learning.tsx b/src/components/sections/challenges/Learning.tsx index 3a48c0c23..7ddb877a2 100644 --- a/src/components/sections/challenges/Learning.tsx +++ b/src/components/sections/challenges/Learning.tsx @@ -2,7 +2,7 @@ import Accordion from "@/components/ui/accordion/Accordion"; import Section from "@/components/sections/communities/_partials/Section"; import { LearningModuleCard } from "@/components/cards/LearningModule"; import CourseCard from "@/components/cards/course"; -import { Course, LearningModule } from "@/types/course"; +import { Challenge } from "@/types/course"; import { Community } from "@/types/community"; import { useTranslation } from "next-i18next"; @@ -11,8 +11,11 @@ import { useTranslation } from "next-i18next"; * * @returns {JSX.Element} The Learning component JSX element. */ -export default function Learning({ courses, learningModules, community }: { courses: Course[]; learningModules: LearningModule[]; community: Community }): JSX.Element { +export default function Learning({ challenge, community }: { challenge: Challenge; community: Community }): JSX.Element { const { t } = useTranslation(); + const courses = challenge?.courses + const learningModules = challenge?.learningModules + return (
))}
diff --git a/src/components/sections/communities/overview/LearningMaterials.tsx b/src/components/sections/communities/overview/LearningMaterials.tsx index 674d9347d..bf0261e06 100644 --- a/src/components/sections/communities/overview/LearningMaterials.tsx +++ b/src/components/sections/communities/overview/LearningMaterials.tsx @@ -50,6 +50,7 @@ export default function LearningMaterialsOverview() { return })} diff --git a/src/pages/communities/[slug]/challenges/[challenge_id]/index.tsx b/src/pages/communities/[slug]/challenges/[challenge_id]/index.tsx index e4a4dd29d..a67bbbc35 100644 --- a/src/pages/communities/[slug]/challenges/[challenge_id]/index.tsx +++ b/src/pages/communities/[slug]/challenges/[challenge_id]/index.tsx @@ -130,7 +130,7 @@ export default function ChallengePage() { {challenge.isTeamChallenge && } - + diff --git a/src/pages/communities/[slug]/learning-modules/[id].tsx b/src/pages/communities/[slug]/learning-modules/[id].tsx new file mode 100644 index 000000000..ddaa00904 --- /dev/null +++ b/src/pages/communities/[slug]/learning-modules/[id].tsx @@ -0,0 +1,81 @@ +import DefaultLayout from "@/components/layout/Default"; +import PageNavigation from "@/components/sections/courses/PageNavigation"; +import Wrapper from "@/components/sections/courses/Wrapper"; +import LearningModuleSection from "@/components/sections/learning-modules"; +import Header from "@/components/sections/learning-modules/Header"; +import useNavigation from "@/hooks/useNavigation"; +import { useDispatch } from "@/hooks/useTypedDispatch"; +import { useMultiSelector } from "@/hooks/useTypedSelector"; +import { IRootState, wrapper } from "@/store"; +import { initLearningModuleNavigationMenu } from "@/store/feature/communities/navigation.slice"; +import { fetchCurrentCommunity } from "@/store/services/community.service"; +import { findLearningModule } from "@/store/services/learningModules.service"; +import { LearningModule } from "@/types/course"; +import { NotFoundError } from "@/utilities/errors/NotFoundError"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; +import { useRouter } from "next/router"; +import { ReactElement, useEffect, useMemo } from "react"; + +interface LearningModuleMultiselector { + learningModule: LearningModule, + loading: boolean +} + +export default function LearningModulePage(): ReactElement { + const dispatch = useDispatch(); + const navigation = useNavigation() + const { learningModule } = useMultiSelector({ + learningModule: (state: IRootState) => state.learningModules.current, + loading: (state: IRootState) => state.learningModules.loading + }) + + const router = useRouter() + const { query, locale } = router + const paths = useMemo(() => [learningModule?.title], [learningModule?.title]); + + useEffect(() => { + dispatch(initLearningModuleNavigationMenu(navigation.community)); + dispatch(findLearningModule({ id: query?.id as string, locale })) + }, [dispatch, locale]); + + return ( + +
+
+
+ + +
+
+
+ ) +} + +LearningModulePage.getLayout = function (page: ReactElement) { + return {page}; +}; + +export const getServerSideProps = wrapper.getServerSideProps((store) => async ({ params, locale }) => { + + const learningModuleId = params?.id as string + try { + const communitySlug = params?.slug as string; + const [{ data: community }, { data: learningModule }, translations] = await Promise.all([ + store.dispatch(fetchCurrentCommunity({ slug: communitySlug, locale })), + store.dispatch(findLearningModule({ id: learningModuleId, locale })), + serverSideTranslations(locale as string), + ]); + if (!community || !learningModule) throw new NotFoundError(); + return { + props: { + community, + learningModule, + ...translations, + }, + }; + } catch (error) { + return { + notFound: true, + }; + } +}); \ No newline at end of file diff --git a/src/store/feature/communities/navigation.slice.ts b/src/store/feature/communities/navigation.slice.ts index 533e3b596..8e41b05b6 100644 --- a/src/store/feature/communities/navigation.slice.ts +++ b/src/store/feature/communities/navigation.slice.ts @@ -1,6 +1,6 @@ import { createSlice, Dispatch, PayloadAction } from "@reduxjs/toolkit"; import { NextRouter } from "next/router"; -import { Challenge, Course } from "@/types/course"; +import { Challenge, Course, LearningModule } from "@/types/course"; import { Community } from "@/types/community"; import { List } from "@/utilities/CommunityNavigation"; import remarkParse from "remark-parse"; @@ -119,6 +119,23 @@ export const initCourseNavigationMenu = (navigation: any) => (dispatch: Dispatch dispatch(setNavigationList(menus)); }; + +/** + * Iniate the learning module page navigation found at communities/:slug/learning-module/:id + * + * @param {*} navigation + * @returns {(dispatch: Dispatch, getState: any) => void} + */ +export const initLearningModuleNavigationMenu = (navigation: any) => (dispatch: Dispatch, getState: any) => { + dispatch(setNavigationList([])) + const community = getState().communities.current as Community; + const learningModule = getState().learningModules.current as LearningModule + const menus: List[] = navigation.initForLearningModule({ + community, + learningModule + }) + dispatch(setNavigationList(menus)); +} /** * Hide navigation action * @date 4/20/2023 - 4:09:46 PM diff --git a/src/types/course.d.ts b/src/types/course.d.ts index b1b283608..93360e6dc 100644 --- a/src/types/course.d.ts +++ b/src/types/course.d.ts @@ -176,6 +176,7 @@ export type LearningModule = { interactiveModules: InteractiveModule[]; courses: Course[], level?: number, + challenges?: Challenge[] }; export type InteractiveModule = { diff --git a/src/utilities/CommunityNavigation.ts b/src/utilities/CommunityNavigation.ts index 5e897d47a..f16a31c25 100644 --- a/src/utilities/CommunityNavigation.ts +++ b/src/utilities/CommunityNavigation.ts @@ -1,5 +1,5 @@ import { Community } from "@/types/community"; -import { Challenge, Course } from "@/types/course"; +import { Challenge, Course, LearningModule } from "@/types/course"; import Slugger from "github-slugger"; type QueryRoute = { @@ -115,6 +115,18 @@ export default class CommunityNavigation { return this.cleanupUrl(this.coursePath(`learning-modules/${path}`, courseSlug, communitySlug)); } + + /** + * Path for a single learning module from the community found at communities/:slug/learning-module/:id + * + * @param {string} path + * @param {(string | undefined)} [communitySlug=this.params().slug] + * @returns {string} + */ + singleLearningModulePath(path: string, communitySlug: string | undefined = this.params().slug): string { + return this.cleanupUrl(this.communityPath(`learning-modules/${path}`, communitySlug)) + } + /** * Get the path for the learning module that if from the challenge * @date 8/8/2023 - 12:09:57 PM @@ -315,4 +327,76 @@ export default class CommunityNavigation { } return communityNavigationMenuList; } + + initForLearningModule({ community, learningModule }: { community: Community; learningModule: LearningModule }): List[] { + + const learningModuleNavigationMenuList: List[] = [ + { + id: "introduction", + title: "Introduction", + hideTitle: true, + items: [ + { + label: "communities.navigation.overview", + exact: true, + link: "", + }, + ], + }, + ]; + + const modules = { + id: "learning-modules", + title: "communities.navigation.learning-modules", + hideTitle: false, + items: [{ + id: learningModule.id, + label: `item ${learningModule.title}`, + link: this.singleLearningModulePath(learningModule.id, community?.slug), + exact: false, + subitems: [] + }], + }; + + const challenges = + learningModule?.challenges?.map((challenge) => { + return { + id: challenge?.id, + label: challenge.name, + link: this.challengePath(challenge?.id, community.slug), + exact: false, + }; + }) || []; + + const courses = learningModule?.courses?.map((course) => { + return { + id: course?.id, + label: course.name, + link: this.coursePath("", course.slug, community?.slug), + exact: false, + }; + }) || []; + + learningModuleNavigationMenuList.push(modules) + + // Add connected challenges + if (challenges.length) { + learningModuleNavigationMenuList.push({ + id: "challenges", + title: "communities.navigation.challenge", + items: challenges, + }); + } + + // Add related courses + if (courses.length) { + learningModuleNavigationMenuList.push({ + id: "courses", + title: "communities.overview.courses.title", + items: courses, + }); + } + + return learningModuleNavigationMenuList + } }