fix: course edition bugs

This commit is contained in:
swve 2024-06-08 11:39:30 +01:00
parent ddbd413539
commit 9968fe23fa
3 changed files with 91 additions and 105 deletions

View file

@ -5,43 +5,40 @@ import React, { createContext, useContext, useEffect, useReducer } from 'react'
import useSWR from 'swr' import useSWR from 'swr'
import { useLHSession } from '@components/Contexts/LHSessionContext' import { useLHSession } from '@components/Contexts/LHSessionContext'
export const CourseContext = createContext(null) as any export const CourseContext = createContext(null)
export const CourseDispatchContext = createContext(null) as any export const CourseDispatchContext = createContext(null)
export function CourseProvider({ export function CourseProvider({ children, courseuuid }: any) {
children,
courseuuid,
}: {
children: React.ReactNode
courseuuid: string
}) {
const session = useLHSession() as any; const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token; const access_token = session?.data?.tokens?.access_token;
const { data: courseStructureData } = useSWR(
`${getAPIUrl()}courses/${courseuuid}/meta`, const { data: courseStructureData, error } = useSWR(
(url) => swrFetcher(url, access_token) access_token ? `${getAPIUrl()}courses/${courseuuid}/meta` : null,
) url => swrFetcher(url, access_token)
const [courseStructure, dispatchCourseStructure] = useReducer(courseReducer, { );
courseStructure: courseStructureData ? courseStructureData : {},
const initialState = {
courseStructure: {},
courseOrder: {}, courseOrder: {},
isSaved: true, isSaved: true,
}) isLoading: true
};
const [state, dispatch] = useReducer(courseReducer, initialState) as any;
// When courseStructureData is loaded, update the state
useEffect(() => { useEffect(() => {
if (courseStructureData) { if (courseStructureData) {
dispatchCourseStructure({ dispatch({ type: 'setCourseStructure', payload: courseStructureData });
type: 'setCourseStructure', dispatch({ type: 'setIsLoaded' });
payload: courseStructureData,
})
} }
}, [courseStructureData,session]) }, [courseStructureData]);
if (!courseStructureData) return if (error) return <div>Failed to load course structure</div>;
if (!courseStructureData) return <div>Loading...</div>;
return ( return (
<CourseContext.Provider value={courseStructure}> <CourseContext.Provider value={state}>
<CourseDispatchContext.Provider value={dispatchCourseStructure}> <CourseDispatchContext.Provider value={dispatch}>
{children} {children}
</CourseDispatchContext.Provider> </CourseDispatchContext.Provider>
</CourseContext.Provider> </CourseContext.Provider>
@ -66,6 +63,8 @@ function courseReducer(state: any, action: any) {
return { ...state, isSaved: true } return { ...state, isSaved: true }
case 'setIsNotSaved': case 'setIsNotSaved':
return { ...state, isSaved: false } return { ...state, isSaved: false }
case 'setIsLoaded':
return { ...state, isLoading: false }
default: default:
throw new Error(`Unhandled action type: ${action.type}`) throw new Error(`Unhandled action type: ${action.type}`)
} }

View file

@ -21,9 +21,9 @@ function EditCourseAccess(props: EditCourseAccessProps) {
const session = useLHSession() as any; const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token; 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 dispatchCourse = useCourseDispatch() as any
const courseStructure = course.courseStructure
const { data: usergroups } = useSWR( const { data: usergroups } = useSWR(
courseStructure ? `${getAPIUrl()}usergroups/resource/${courseStructure.course_uuid}` : null, courseStructure ? `${getAPIUrl()}usergroups/resource/${courseStructure.course_uuid}` : null,
(url) => swrFetcher(url, access_token) (url) => swrFetcher(url, access_token)
@ -33,7 +33,7 @@ function EditCourseAccess(props: EditCourseAccessProps) {
React.useEffect(() => { React.useEffect(() => {
// This code will run whenever form values are updated // This code will run whenever form values are updated
if (isPublic !== courseStructure.public) { if ((isPublic !== courseStructure.public) && isLoading) {
dispatchCourse({ type: 'setIsNotSaved' }) dispatchCourse({ type: 'setIsNotSaved' })
const updatedCourse = { const updatedCourse = {
...courseStructure, ...courseStructure,

View file

@ -3,13 +3,13 @@ import FormLayout, {
FormLabelAndMessage, FormLabelAndMessage,
Input, Input,
Textarea, Textarea,
} from '@components/StyledElements/Form/Form' } from '@components/StyledElements/Form/Form';
import { useFormik } from 'formik' import { useFormik } from 'formik';
import { AlertTriangle } from 'lucide-react' import { AlertTriangle } from 'lucide-react';
import * as Form from '@radix-ui/react-form' import * as Form from '@radix-ui/react-form';
import React from 'react' import React, { useEffect, useState } from 'react';
import { useCourse, useCourseDispatch } from '../../../Contexts/CourseContext' import { useCourse, useCourseDispatch } from '../../../Contexts/CourseContext';
import ThumbnailUpdate from './ThumbnailUpdate' import ThumbnailUpdate from './ThumbnailUpdate';
type EditCourseStructureProps = { type EditCourseStructureProps = {
orgslug: string orgslug: string
@ -17,74 +17,78 @@ type EditCourseStructureProps = {
} }
const validate = (values: any) => { const validate = (values: any) => {
const errors: any = {} const errors = {} as any;
if (!values.name) { if (!values.name) {
errors.name = 'Required' errors.name = 'Required';
} } else if (values.name.length > 100) {
errors.name = 'Must be 100 characters or less';
if (values.name.length > 100) {
errors.name = 'Must be 100 characters or less'
} }
if (!values.description) { if (!values.description) {
errors.description = 'Required' errors.description = 'Required';
} } else if (values.description.length > 1000) {
errors.description = 'Must be 1000 characters or less';
if (values.description.length > 1000) {
errors.description = 'Must be 1000 characters or less'
} }
if (!values.learnings) { if (!values.learnings) {
errors.learnings = 'Required' errors.learnings = 'Required';
} }
return errors return errors;
} };
function EditCourseGeneral(props: EditCourseStructureProps) { function EditCourseGeneral(props: EditCourseStructureProps) {
const [error, setError] = React.useState('') const [error, setError] = useState('');
const course = useCourse() as any const course = useCourse();
const dispatchCourse = useCourseDispatch() as any const dispatchCourse = useCourseDispatch() as any;
const { isLoading, courseStructure } = course as any;
const courseStructure = course.courseStructure
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
name: String(courseStructure.name), name: courseStructure?.name || '',
description: String(courseStructure.description), description: courseStructure?.description || '',
about: String(courseStructure.about), about: courseStructure?.about || '',
learnings: String(courseStructure.learnings), learnings: courseStructure?.learnings || '',
tags: String(courseStructure.tags), tags: courseStructure?.tags || '',
public: String(courseStructure.public), public: courseStructure?.public || '',
}, },
validate, validate,
onSubmit: async (values) => {}, onSubmit: async values => {
try {
// Add your submission logic here
dispatchCourse({ type: 'setIsSaved' });
} catch (e) {
setError('Failed to save course structure.');
}
},
enableReinitialize: true, enableReinitialize: true,
}) }) as any;
React.useEffect(() => { useEffect(() => {
// This code will run whenever form values are updated if (!isLoading) {
if (formik.values !== formik.initialValues) { const formikValues = formik.values as any;
dispatchCourse({ type: 'setIsNotSaved' }) const initialValues = formik.initialValues as any;
const valuesChanged = Object.keys(formikValues).some(
key => formikValues[key] !== initialValues[key]
);
if (valuesChanged) {
dispatchCourse({ type: 'setIsNotSaved' });
const updatedCourse = { const updatedCourse = {
...courseStructure, ...courseStructure,
name: formik.values.name, ...formikValues,
description: formik.values.description, };
about: formik.values.about, dispatchCourse({ type: 'setCourseStructure', payload: updatedCourse });
learnings: formik.values.learnings,
tags: formik.values.tags,
public: formik.values.public,
} }
dispatchCourse({ type: 'setCourseStructure', payload: updatedCourse })
} }
}, [course, formik.values, formik.initialValues]) }, [formik.values, isLoading]);
return ( return (
<div> <div>
{' '}
<div className="h-6"></div> <div className="h-6"></div>
<div className="ml-10 mr-10 mx-auto bg-white rounded-xl shadow-sm px-6 py-5"> <div className="ml-10 mr-10 mx-auto bg-white rounded-xl shadow-sm px-6 py-5">
{course.courseStructure && ( {courseStructure && (
<div className="editcourse-form"> <div className="editcourse-form">
{error && ( {error && (
<div className="flex justify-center bg-red-200 rounded-md text-red-950 space-x-2 items-center p-4 transition-all shadow-sm"> <div className="flex justify-center bg-red-200 rounded-md text-red-950 space-x-2 items-center p-4 transition-all shadow-sm">
@ -94,10 +98,7 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
)} )}
<FormLayout onSubmit={formik.handleSubmit}> <FormLayout onSubmit={formik.handleSubmit}>
<FormField name="name"> <FormField name="name">
<FormLabelAndMessage <FormLabelAndMessage label="Name" message={formik.errors.name} />
label="Name"
message={formik.errors.name}
/>
<Form.Control asChild> <Form.Control asChild>
<Input <Input
style={{ backgroundColor: 'white' }} style={{ backgroundColor: 'white' }}
@ -110,26 +111,20 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
</FormField> </FormField>
<FormField name="description"> <FormField name="description">
<FormLabelAndMessage <FormLabelAndMessage label="Description" message={formik.errors.description} />
label="Description"
message={formik.errors.description}
/>
<Form.Control asChild> <Form.Control asChild>
<Input <Input
style={{ backgroundColor: 'white' }} style={{ backgroundColor: 'white' }}
onChange={formik.handleChange} onChange={formik.handleChange}
value={formik.values.description} value={formik.values.description}
type='text' type="text"
required required
/> />
</Form.Control> </Form.Control>
</FormField> </FormField>
<FormField name="about"> <FormField name="about">
<FormLabelAndMessage <FormLabelAndMessage label="About" message={formik.errors.about} />
label="About"
message={formik.errors.about}
/>
<Form.Control asChild> <Form.Control asChild>
<Textarea <Textarea
style={{ backgroundColor: 'white' }} style={{ backgroundColor: 'white' }}
@ -141,10 +136,7 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
</FormField> </FormField>
<FormField name="learnings"> <FormField name="learnings">
<FormLabelAndMessage <FormLabelAndMessage label="Learnings" message={formik.errors.learnings} />
label="Learnings"
message={formik.errors.learnings}
/>
<Form.Control asChild> <Form.Control asChild>
<Textarea <Textarea
style={{ backgroundColor: 'white' }} style={{ backgroundColor: 'white' }}
@ -156,10 +148,7 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
</FormField> </FormField>
<FormField name="tags"> <FormField name="tags">
<FormLabelAndMessage <FormLabelAndMessage label="Tags" message={formik.errors.tags} />
label="Tags"
message={formik.errors.tags}
/>
<Form.Control asChild> <Form.Control asChild>
<Textarea <Textarea
style={{ backgroundColor: 'white' }} style={{ backgroundColor: 'white' }}
@ -176,14 +165,12 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
<ThumbnailUpdate /> <ThumbnailUpdate />
</Form.Control> </Form.Control>
</FormField> </FormField>
</FormLayout> </FormLayout>
</div> </div>
)} )}
</div> </div>
</div> </div>
) );
} }
export default EditCourseGeneral export default EditCourseGeneral;