From 8a2ccb253437f30641b32ffd5a79a834ab7d6ff0 Mon Sep 17 00:00:00 2001 From: swve Date: Wed, 17 Apr 2024 21:26:21 +0200 Subject: [PATCH] feat: add updates functionality to app --- apps/api/src/routers/courses/courses.py | 2 +- apps/api/src/services/courses/updates.py | 9 +- .../(withmenu)/course/[courseuuid]/course.tsx | 5 +- .../Objects/CourseUpdates/CourseUpdates.tsx | 159 ++++++++++++------ .../ConfirmationModal/ConfirmationModal.tsx | 2 + apps/web/services/courses/updates.ts | 23 +++ 6 files changed, 147 insertions(+), 53 deletions(-) create mode 100644 apps/web/services/courses/updates.ts diff --git a/apps/api/src/routers/courses/courses.py b/apps/api/src/routers/courses/courses.py index cd145ac3..38057069 100644 --- a/apps/api/src/routers/courses/courses.py +++ b/apps/api/src/routers/courses/courses.py @@ -167,7 +167,7 @@ async def api_get_course_updates( return await get_updates_by_course_uuid(request, course_uuid, current_user, db_session) -@router.post("/{course_uuid}/update") +@router.post("/{course_uuid}/updates") async def api_create_course_update( request: Request, course_uuid: str, diff --git a/apps/api/src/services/courses/updates.py b/apps/api/src/services/courses/updates.py index 5ce33e55..b4460664 100644 --- a/apps/api/src/services/courses/updates.py +++ b/apps/api/src/services/courses/updates.py @@ -2,7 +2,7 @@ from datetime import datetime from typing import List from uuid import uuid4 from fastapi import HTTPException, Request, status -from sqlmodel import Session, select +from sqlmodel import Session, col, select from src.db.course_updates import ( CourseUpdate, CourseUpdateCreate, @@ -88,7 +88,6 @@ async def update_update( for key, value in update_object.model_dump().items(): if value is not None: setattr(update, key, value) - db_session.add(update) @@ -142,7 +141,11 @@ async def get_updates_by_course_uuid( status_code=status.HTTP_409_CONFLICT, detail="Course does not exist" ) - statement = select(CourseUpdate).where(CourseUpdate.course_id == course.id) + statement = ( + select(CourseUpdate) + .where(CourseUpdate.course_id == course.id) + .order_by(col(CourseUpdate.creation_date).desc()) + ) # https://sqlmodel.tiangolo.com/tutorial/where/#type-annotations-and-errors updates = db_session.exec(statement).all() return [CourseUpdateRead(**update.model_dump()) for update in updates] diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx index c51f96cd..f0985ddd 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx @@ -16,6 +16,7 @@ import { ArrowRight, Check, File, Sparkles, Video } from 'lucide-react' import { useOrg } from '@components/Contexts/OrgContext' import UserAvatar from '@components/Objects/UserAvatar' import CourseUpdates from '@components/Objects/CourseUpdates/CourseUpdates' +import { CourseProvider } from '@components/Contexts/CourseContext' const CourseClient = (props: any) => { const [user, setUser] = useState({}) @@ -75,7 +76,9 @@ const CourseClient = (props: any) => {

{course.name}

- + + +
diff --git a/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx b/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx index 5b2a68db..af17a047 100644 --- a/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx +++ b/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx @@ -1,5 +1,5 @@ -import { PencilLine, Rss } from 'lucide-react' -import React from 'react' +import { PencilLine, Rss, TentTree } from 'lucide-react' +import React, { useEffect } from 'react' import { motion } from 'framer-motion' import { useFormik } from 'formik' import * as Form from '@radix-ui/react-form' @@ -9,8 +9,19 @@ import FormLayout, { Input, Textarea, } from '@components/StyledElements/Form/Form' +import { useCourse } from '@components/Contexts/CourseContext' +import useSWR, { mutate } from 'swr' +import { getAPIUrl } from '@services/config/config' +import { swrFetcher } from '@services/utils/ts/requests' +import useAdminStatus from '@components/Hooks/useAdminStatus' +import { useOrg } from '@components/Contexts/OrgContext' +import { createCourseUpdate, deleteCourseUpdate } from '@services/courses/updates' +import toast from 'react-hot-toast' +import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal' function CourseUpdates() { + const course = useCourse() as any; + const { data: updates } = useSWR(`${getAPIUrl()}courses/${course?.courseStructure.course_uuid}/updates`, swrFetcher) const [isModelOpen, setIsModelOpen] = React.useState(false) function handleModelOpen() { @@ -18,17 +29,16 @@ function CourseUpdates() { } // if user clicks outside the model, close the model - React.useEffect(() => { + React.useLayoutEffect(() => { function handleClickOutside(event: any) { - if (event.target.closest('.bg-white') === null) { - setIsModelOpen(false) - } + console.log(event.target.id) + if (event.target.closest('.bg-white') || event.target.id === 'delete-update-button') return; + setIsModelOpen(false); } - document.addEventListener('mousedown', handleClickOutside) - return () => { - document.removeEventListener('mousedown', handleClickOutside) - } - }, []) + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + return (
@@ -36,7 +46,7 @@ function CourseUpdates() {
Updates - 5 + {updates && {updates.length}}
{isModelOpen && - + } ) } -const UpdatesModel = () => { +const UpdatesSection = () => { + const [selectedView, setSelectedView] = React.useState('list') + const isAdmin = useAdminStatus() as boolean; return (
@@ -64,19 +76,24 @@ const UpdatesModel = () => { Updates
-
+ {isAdmin &&
setSelectedView('new')} + className='py-2 px-4 space-x-2 items-center flex cursor-pointer text-xs font-medium hover:bg-gray-200 bg-gray-100 outline outline-1 outline-neutral-200/40'> New Update -
+
}
- + {selectedView === 'list' && } + {selectedView === 'new' && }
) } -const NewUpdateForm = () => { +const NewUpdateForm = ({ setSelectedView }: any) => { + const org = useOrg() as any; + const course = useCourse() as any; const validate = (values: any) => { const errors: any = {} @@ -96,9 +113,32 @@ const NewUpdateForm = () => { content: '' }, validate, - onSubmit: async (values) => { }, + onSubmit: async (values) => { + const body = { + title: values.title, + content: values.content, + course_uuid: course.courseStructure.course_uuid, + org_id: org.id + } + const res = await createCourseUpdate(body) + if (res.status === 200) { + toast.success('Update added successfully') + setSelectedView('list') + mutate(`${getAPIUrl()}courses/${course?.courseStructure.course_uuid}/updates`) + } + else { + toast.error('Failed to add update') + } + }, enableReinitialize: true, }) + + useEffect(() => { + + } + , [course, org]) + + return (
@@ -129,7 +169,7 @@ const NewUpdateForm = () => { />