diff --git a/apps/web/components/Contexts/CourseContext.tsx b/apps/web/components/Contexts/CourseContext.tsx index a2f9f161..42c7c9bc 100644 --- a/apps/web/components/Contexts/CourseContext.tsx +++ b/apps/web/components/Contexts/CourseContext.tsx @@ -5,43 +5,40 @@ import React, { createContext, useContext, useEffect, useReducer } from 'react' import useSWR from 'swr' import { useLHSession } from '@components/Contexts/LHSessionContext' -export const CourseContext = createContext(null) as any -export const CourseDispatchContext = createContext(null) as any +export const CourseContext = createContext(null) +export const CourseDispatchContext = createContext(null) -export function CourseProvider({ - children, - courseuuid, -}: { - children: React.ReactNode - courseuuid: string -}) { +export function CourseProvider({ children, courseuuid }: any) { const session = useLHSession() as any; const access_token = session?.data?.tokens?.access_token; - const { data: courseStructureData } = useSWR( - `${getAPIUrl()}courses/${courseuuid}/meta`, - (url) => swrFetcher(url, access_token) - ) - const [courseStructure, dispatchCourseStructure] = useReducer(courseReducer, { - courseStructure: courseStructureData ? courseStructureData : {}, + + const { data: courseStructureData, error } = useSWR( + access_token ? `${getAPIUrl()}courses/${courseuuid}/meta` : null, + url => swrFetcher(url, access_token) + ); + + const initialState = { + courseStructure: {}, courseOrder: {}, isSaved: true, - }) + isLoading: true + }; + + const [state, dispatch] = useReducer(courseReducer, initialState) as any; - // When courseStructureData is loaded, update the state useEffect(() => { if (courseStructureData) { - dispatchCourseStructure({ - type: 'setCourseStructure', - payload: courseStructureData, - }) + dispatch({ type: 'setCourseStructure', payload: courseStructureData }); + dispatch({ type: 'setIsLoaded' }); } - }, [courseStructureData,session]) + }, [courseStructureData]); - if (!courseStructureData) return + if (error) return
Failed to load course structure
; + if (!courseStructureData) return
Loading...
; return ( - - + + {children} @@ -66,6 +63,8 @@ function courseReducer(state: any, action: any) { return { ...state, isSaved: true } case 'setIsNotSaved': return { ...state, isSaved: false } + case 'setIsLoaded': + return { ...state, isLoading: false } default: throw new Error(`Unhandled action type: ${action.type}`) } diff --git a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx b/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx index 6cb3ab10..38ec8b3c 100644 --- a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx @@ -21,9 +21,9 @@ function EditCourseAccess(props: EditCourseAccessProps) { const session = useLHSession() as any; const access_token = session?.data?.tokens?.access_token; - const course = useCourse() as any + const course = useCourse() as any; + const { isLoading, courseStructure } = course as any; const dispatchCourse = useCourseDispatch() as any - const courseStructure = course.courseStructure const { data: usergroups } = useSWR( courseStructure ? `${getAPIUrl()}usergroups/resource/${courseStructure.course_uuid}` : null, (url) => swrFetcher(url, access_token) @@ -33,7 +33,7 @@ function EditCourseAccess(props: EditCourseAccessProps) { React.useEffect(() => { // This code will run whenever form values are updated - if (isPublic !== courseStructure.public) { + if ((isPublic !== courseStructure.public) && isLoading) { dispatchCourse({ type: 'setIsNotSaved' }) const updatedCourse = { ...courseStructure, @@ -126,7 +126,7 @@ function UserGroupsSection({ usergroups }: { usergroups: any[] }) { const access_token = session?.data?.tokens?.access_token; const removeUserGroupLink = async (usergroup_id: number) => { - const res = await unLinkResourcesToUserGroup(usergroup_id, course.courseStructure.course_uuid,access_token) + const res = await unLinkResourcesToUserGroup(usergroup_id, course.courseStructure.course_uuid, access_token) if (res.status === 200) { toast.success('Successfully unliked from usergroup') mutate(`${getAPIUrl()}usergroups/resource/${course.courseStructure.course_uuid}`) diff --git a/apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx b/apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx index 1f6d5ddc..8535fd83 100644 --- a/apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx +++ b/apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx @@ -3,13 +3,13 @@ import FormLayout, { FormLabelAndMessage, Input, Textarea, -} from '@components/StyledElements/Form/Form' -import { useFormik } from 'formik' -import { AlertTriangle } from 'lucide-react' -import * as Form from '@radix-ui/react-form' -import React from 'react' -import { useCourse, useCourseDispatch } from '../../../Contexts/CourseContext' -import ThumbnailUpdate from './ThumbnailUpdate' +} from '@components/StyledElements/Form/Form'; +import { useFormik } from 'formik'; +import { AlertTriangle } from 'lucide-react'; +import * as Form from '@radix-ui/react-form'; +import React, { useEffect, useState } from 'react'; +import { useCourse, useCourseDispatch } from '../../../Contexts/CourseContext'; +import ThumbnailUpdate from './ThumbnailUpdate'; type EditCourseStructureProps = { orgslug: string @@ -17,74 +17,78 @@ type EditCourseStructureProps = { } const validate = (values: any) => { - const errors: any = {} + const errors = {} as any; if (!values.name) { - errors.name = 'Required' - } - - if (values.name.length > 100) { - errors.name = 'Must be 100 characters or less' + errors.name = 'Required'; + } else if (values.name.length > 100) { + errors.name = 'Must be 100 characters or less'; } if (!values.description) { - errors.description = 'Required' - } - - if (values.description.length > 1000) { - errors.description = 'Must be 1000 characters or less' + errors.description = 'Required'; + } else if (values.description.length > 1000) { + errors.description = 'Must be 1000 characters or less'; } if (!values.learnings) { - errors.learnings = 'Required' + errors.learnings = 'Required'; } - return errors -} + return errors; +}; function EditCourseGeneral(props: EditCourseStructureProps) { - const [error, setError] = React.useState('') - const course = useCourse() as any - const dispatchCourse = useCourseDispatch() as any + const [error, setError] = useState(''); + const course = useCourse(); + const dispatchCourse = useCourseDispatch() as any; + const { isLoading, courseStructure } = course as any; - const courseStructure = course.courseStructure const formik = useFormik({ initialValues: { - name: String(courseStructure.name), - description: String(courseStructure.description), - about: String(courseStructure.about), - learnings: String(courseStructure.learnings), - tags: String(courseStructure.tags), - public: String(courseStructure.public), + name: courseStructure?.name || '', + description: courseStructure?.description || '', + about: courseStructure?.about || '', + learnings: courseStructure?.learnings || '', + tags: courseStructure?.tags || '', + public: courseStructure?.public || '', }, validate, - onSubmit: async (values) => {}, - enableReinitialize: true, - }) - - React.useEffect(() => { - // This code will run whenever form values are updated - if (formik.values !== formik.initialValues) { - dispatchCourse({ type: 'setIsNotSaved' }) - const updatedCourse = { - ...courseStructure, - name: formik.values.name, - description: formik.values.description, - about: formik.values.about, - learnings: formik.values.learnings, - tags: formik.values.tags, - public: formik.values.public, + onSubmit: async values => { + try { + // Add your submission logic here + dispatchCourse({ type: 'setIsSaved' }); + } catch (e) { + setError('Failed to save course structure.'); + } + }, + enableReinitialize: true, + }) as any; + + useEffect(() => { + if (!isLoading) { + const formikValues = formik.values as any; + const initialValues = formik.initialValues as any; + const valuesChanged = Object.keys(formikValues).some( + key => formikValues[key] !== initialValues[key] + ); + + if (valuesChanged) { + dispatchCourse({ type: 'setIsNotSaved' }); + const updatedCourse = { + ...courseStructure, + ...formikValues, + }; + dispatchCourse({ type: 'setCourseStructure', payload: updatedCourse }); } - dispatchCourse({ type: 'setCourseStructure', payload: updatedCourse }) } - }, [course, formik.values, formik.initialValues]) + }, [formik.values, isLoading]); return (
- {' '}
- {course.courseStructure && ( + {courseStructure && (
{error && (
@@ -94,10 +98,7 @@ function EditCourseGeneral(props: EditCourseStructureProps) { )} - + - + - +