From b582eaacfcded0ef14db36e2a9db9690176587c6 Mon Sep 17 00:00:00 2001 From: swve Date: Mon, 21 Aug 2023 18:35:48 +0200 Subject: [PATCH] feat: implement course edition --- .../[courseid]/edit/[[...subpage]]/edit.tsx | 56 ++++++--- .../edit/subpages/CourseEdition.tsx | 111 +++++++++++++++++- front/services/courses/courses.ts | 6 + 3 files changed, 154 insertions(+), 19 deletions(-) diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/[[...subpage]]/edit.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/[[...subpage]]/edit.tsx index 2a12fea7..c290d344 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/[[...subpage]]/edit.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/[[...subpage]]/edit.tsx @@ -2,7 +2,7 @@ import React, { FC, use, useEffect, useReducer } from 'react' import { swrFetcher } from "@services/utils/ts/requests"; import { getAPIUrl, getUriWithOrg } from '@services/config/config'; -import useSWR from 'swr'; +import useSWR, { mutate } from 'swr'; import { getCourseThumbnailMediaDirectory } from '@services/media/media'; import Link from 'next/link'; import CourseEdition from '../subpages/CourseEdition'; @@ -10,14 +10,18 @@ import CourseContentEdition from '../subpages/CourseContentEdition'; import ErrorUI from '@components/StyledElements/Error/Error'; import { updateChaptersMetadata } from '@services/courses/chapters'; import { Check, SaveAllIcon, Timer } from 'lucide-react'; +import Loading from '../../loading'; +import { updateCourse } from '@services/courses/courses'; function CourseEditClient({ courseid, subpage, params }: { courseid: string, subpage: string, params: any }) { const { data: chapters_meta, error: chapters_meta_error, isLoading: chapters_meta_isloading } = useSWR(`${getAPIUrl()}chapters/meta/course_${courseid}`, swrFetcher); - const { data: course_meta, error: course_meta_error, isLoading: course_meta_isloading } = useSWR(`${getAPIUrl()}courses/meta/course_${courseid}`, swrFetcher); + const { data: course, error: course_error, isLoading: course_isloading } = useSWR(`${getAPIUrl()}courses/course_${courseid}`, swrFetcher); const [courseChaptersMetadata, dispatchCourseChaptersMetadata] = useReducer(courseChaptersReducer, {}); + const [courseState, dispatchCourseMetadata] = useReducer(courseReducer, {}); const [savedContent, dispatchSavedContent] = useReducer(savedContentReducer, true); + function courseChaptersReducer(state: any, action: any) { switch (action.type) { case 'updated_chapter': @@ -28,6 +32,16 @@ function CourseEditClient({ courseid, subpage, params }: { courseid: string, sub } } + function courseReducer(state: any, action: any) { + switch (action.type) { + case 'updated_course': + // action will contain the entire state, just update the entire state + return action.payload; + default: + throw new Error(); + } + } + function savedContentReducer(state: any, action: any) { switch (action.type) { case 'saved_content': @@ -39,13 +53,16 @@ function CourseEditClient({ courseid, subpage, params }: { courseid: string, sub } } - function saveCourse() { + async function saveCourse() { if (subpage.toString() === 'content') { - updateChaptersMetadata(courseid, courseChaptersMetadata) + await updateChaptersMetadata(courseid, courseChaptersMetadata) dispatchSavedContent({ type: 'saved_content' }) + await mutate(`${getAPIUrl()}chapters/meta/course_${courseid}`) } else if (subpage.toString() === 'general') { - console.log('general') + await updateCourse(courseid, courseState) + dispatchSavedContent({ type: 'saved_content' }) + await mutate(`${getAPIUrl()}courses/course_${courseid}`) } } @@ -54,23 +71,27 @@ function CourseEditClient({ courseid, subpage, params }: { courseid: string, sub dispatchCourseChaptersMetadata({ type: 'updated_chapter', payload: chapters_meta }) dispatchSavedContent({ type: 'saved_content' }) } - }, [chapters_meta]) + if (course) { + dispatchCourseMetadata({ type: 'updated_course', payload: course }) + dispatchSavedContent({ type: 'saved_content' }) + } + }, [chapters_meta, course]) return ( <>
- {course_meta_isloading &&
Loading...
} - {course_meta && <> + {course_isloading &&
Loading...
} + {course && <>
- - + +
Edit Course
-
{course_meta.course.name}
+
{course.name}
@@ -100,19 +121,22 @@ function CourseEditClient({ courseid, subpage, params }: { courseid: string, sub
- + ) } -const CoursePageViewer = ({ subpage, courseid, orgslug, dispatchCourseChaptersMetadata, courseChaptersMetadata, dispatchSavedContent }: { subpage: string, courseid: string, orgslug: string, dispatchCourseChaptersMetadata: React.Dispatch, dispatchSavedContent: React.Dispatch, courseChaptersMetadata: any }) => { - if (subpage.toString() === 'general') { - return +const CoursePageViewer = ({ subpage, courseid, orgslug, dispatchCourseMetadata, dispatchCourseChaptersMetadata, courseChaptersMetadata, dispatchSavedContent, courseState }: { subpage: string, courseid: string, orgslug: string, dispatchCourseChaptersMetadata: React.Dispatch, dispatchCourseMetadata: React.Dispatch, dispatchSavedContent: React.Dispatch, courseChaptersMetadata: any, courseState: any }) => { + if (subpage.toString() === 'general' && Object.keys(courseState).length !== 0) { + return } - else if (subpage.toString() === 'content') { + else if (subpage.toString() === 'content' && Object.keys(courseChaptersMetadata).length !== 0) { return } + else if (subpage.toString() === 'content' || subpage.toString() === 'general') { + return + } else { return } diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseEdition.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseEdition.tsx index a5c6d2ac..23b77f85 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseEdition.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseEdition.tsx @@ -1,9 +1,114 @@ -import React from 'react' +"use client"; +import FormLayout, { ButtonBlack, FormField, FormLabel, FormLabelAndMessage, FormMessage, Input, Textarea } from '@components/StyledElements/Form/Form' +import * as Form from '@radix-ui/react-form'; +import { useFormik } from 'formik'; +import { AlertTriangle } from "lucide-react"; +import React from "react"; + +const validate = (values: any) => { + const errors: any = {}; + + if (!values.name) { + errors.name = 'Required'; + } + + if (values.name.length > 100) { + errors.name = 'Must be 80 characters or less'; + } + + if (!values.mini_description) { + errors.mini_description = 'Required'; + } + + if (values.mini_description.length > 200) { + errors.mini_description = 'Must be 200 characters or less'; + } + + if (!values.description) { + errors.description = 'Required'; + + } + + if (values.description.length > 1000) { + errors.description = 'Must be 1000 characters or less'; + } + + + if (!values.learnings) { + errors.learnings = 'Required'; + } + + return errors; +}; + +function CourseEdition(props: any) { + const [error, setError] = React.useState(''); + const formik = useFormik({ + initialValues: { + name: String(props.data.name), + mini_description: String(props.data.mini_description), + description: String(props.data.description), + learnings: String(props.data.learnings), + }, + validate, + onSubmit: async values => { + }, + }); + + + React.useEffect(() => { + // This code will run whenever form values are updated + if (formik.values !== formik.initialValues) { + props.dispatchSavedContent({ type: 'unsaved_content' }); + const updatedCourse = { + ...props.data, + name: formik.values.name, + mini_description: formik.values.mini_description, + description: formik.values.description, + learnings: formik.values.learnings.split(", "), + }; + props.dispatchCourseMetadata({ type: 'updated_course', payload: updatedCourse }); + } + }, [formik.values, formik.initialValues]); + -function CourseEdition() { return (
- Course Edition +
+ {error && ( +
+ +
{error}
+
+ )} + + + + + + + + + + + + + + + + +