From 142e111c15eafba1819cac48207cafcc2d451385 Mon Sep 17 00:00:00 2001 From: swve Date: Fri, 25 Aug 2023 18:08:30 +0200 Subject: [PATCH] feat: add delete activity option + redesign edit --- .../(withmenu)/collections/admin.tsx | 12 ++-- .../edit/subpages/CourseContentEdition.tsx | 10 +-- .../[orgslug]/(withmenu)/courses/courses.tsx | 19 ++++-- .../Pages/CourseEdit/Draggables/Activity.tsx | 65 +++++++++++++++---- .../Pages/CourseEdit/Draggables/Chapter.tsx | 30 +++++---- front/services/courses/activities.ts | 6 ++ src/services/courses/activities/activities.py | 12 +++- 7 files changed, 111 insertions(+), 43 deletions(-) diff --git a/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx b/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx index d55e87c7..09ad6d53 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx @@ -6,7 +6,7 @@ import ConfirmationModal from '@components/StyledElements/ConfirmationModal/Conf import { getUriWithOrg } from '@services/config/config'; import { deleteCollection } from '@services/courses/collections'; import { revalidateTags } from '@services/utils/ts/requests'; -import { Link, Trash } from 'lucide-react'; +import { Link, Trash, X } from 'lucide-react'; import { useRouter } from 'next/navigation'; import React from 'react' @@ -26,15 +26,17 @@ const CollectionAdminEditsArea = (props: any) => { return ( -
+
- Delete - } +
+ +
} functionToExecute={() => deleteCollectionUI(props.collection_id)} status='warning' >
diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseContentEdition.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseContentEdition.tsx index 81b3adb9..b7fe651d 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseContentEdition.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/subpages/CourseContentEdition.tsx @@ -11,7 +11,7 @@ import { createActivity, createFileActivity, createExternalVideoActivity } from import { getOrganizationContextInfo } from "@services/organizations/orgs"; import Modal from "@components/StyledElements/Modal/Modal"; import { denyAccessToUser } from "@services/utils/react/middlewares/views"; -import { Folders, SaveIcon } from "lucide-react"; +import { Folders, Hexagon, SaveIcon } from "lucide-react"; import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; import { revalidateTags, swrFetcher } from "@services/utils/ts/requests"; import { mutate } from "swr"; @@ -33,7 +33,7 @@ function CourseContentEdition(props: any) { const courseid = props.courseid; const orgslug = props.orgslug; - + useEffect(() => { setwinReady(true); @@ -63,7 +63,7 @@ function CourseContentEdition(props: any) { const submitChapter = async (chapter: any) => { await createChapter(chapter, courseid); mutate(`${getAPIUrl()}chapters/meta/course_${courseid}`); - // await getCourseChapters(); + // await getCourseChapters(); await revalidateTags(['courses'], orgslug); router.refresh(); setNewChapterModal(false); @@ -81,6 +81,8 @@ function CourseContentEdition(props: any) { router.refresh(); }; + + // Submit File Upload const submitFileActivity = async (file: any, type: any, activity: any, chapterId: string) => { await updateChaptersMetadata(courseid, data); @@ -301,7 +303,7 @@ function CourseContentEdition(props: any) { dialogDescription="Add a new chapter to the course" dialogTrigger={
- +
Add chapter +
} diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx index 8484aece..f5af8a02 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx @@ -16,6 +16,7 @@ import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentT import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; import { getCourseThumbnailMediaDirectory } from '@services/media/media'; import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'; +import { Pencil, X } from 'lucide-react'; interface CourseProps { orgslug: string; @@ -91,22 +92,26 @@ function Courses(props: CourseProps) { const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => { return ( -
+
- Delete - } +
+ +
} functionToExecute={() => props.deleteCourses(props.courseId)} status='warning' >
- +
+ +
diff --git a/front/components/Pages/CourseEdit/Draggables/Activity.tsx b/front/components/Pages/CourseEdit/Draggables/Activity.tsx index 341d0629..1f245fcf 100644 --- a/front/components/Pages/CourseEdit/Draggables/Activity.tsx +++ b/front/components/Pages/CourseEdit/Draggables/Activity.tsx @@ -1,36 +1,73 @@ import Link from "next/link"; import { Draggable } from "react-beautiful-dnd"; -import { EyeOpenIcon, Pencil2Icon } from '@radix-ui/react-icons' -import { getUriWithOrg } from "@services/config/config"; -import { FileText, Video, Sparkles, File } from "lucide-react"; +import { EyeOpenIcon, Pencil2Icon, TrashIcon } from '@radix-ui/react-icons' +import { getAPIUrl, getUriWithOrg } from "@services/config/config"; +import { FileText, Video, Sparkles, XSquare, X, Pencil, MoreVertical, Eye } from "lucide-react"; +import { mutate } from "swr"; +import { revalidateTags } from "@services/utils/ts/requests"; +import { useRouter } from "next/navigation"; +import ConfirmationModal from "@components/StyledElements/ConfirmationModal/ConfirmationModal"; +import { deleteActivity } from "@services/courses/activities"; function Activity(props: any) { + const router = useRouter(); + + async function removeActivity() { + await deleteActivity(props.activity.id); + mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}`); + await revalidateTags(['courses'], props.orgslug); + router.refresh(); + } + return ( {(provided) => (
+ className="flex flex-row py-2 my-2 rounded-md bg-gray-50 text-gray-500 hover:bg-gray-100 hover:scale-102 hover:shadow space-x-1 w-auto items-center ring-1 ring-inset ring-gray-400/10 shadow-sm transition-all delay-100 duration-75 ease-linear" key={props.activity.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
{props.activity.type === "video" && <>
} {props.activity.type === "documentpdf" && <>
Document
} {props.activity.type === "dynamic" && <>
Dynamic
}
+

{props.activity.name}

-
- - + +
- -
+ + + + {props.activity.type === "dynamic" && <> + +
Edit
+ + } + +
+
+ + + +
} + functionToExecute={() => removeActivity()} + status='warning' + >
)} diff --git a/front/components/Pages/CourseEdit/Draggables/Chapter.tsx b/front/components/Pages/CourseEdit/Draggables/Chapter.tsx index e4e20c95..6bd4787f 100644 --- a/front/components/Pages/CourseEdit/Draggables/Chapter.tsx +++ b/front/components/Pages/CourseEdit/Draggables/Chapter.tsx @@ -2,7 +2,7 @@ import React from "react"; import styled from "styled-components"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; import Activity from "./Activity"; -import { PlusSquare, Sparkle, Sparkles, Trash, Trash2 } from "lucide-react"; +import { Folders, Hexagon, MoreVertical, PlusSquare, Sparkle, Sparkles, Trash, Trash2, X } from "lucide-react"; import ConfirmationModal from "@components/StyledElements/ConfirmationModal/ConfirmationModal"; function Chapter(props: any) { @@ -14,25 +14,34 @@ function Chapter(props: any) { {...provided.draggableProps} ref={provided.innerRef} // isDragging={snapshot.isDragging} - className="max-w-screen-2xl mx-auto" + className="max-w-screen-2xl mx-auto bg-white px-5" key={props.info.list.chapter.id} > -

-

{props.info.list.chapter.name} -

+
+
+
+ +
+ +

{props.info.list.chapter.name}

+
+ - Delete - } +
+ +

Delete Chapter

+
} functionToExecute={() => props.deleteChapter(props.info.list.chapter.id)} status='warning' >
-

+
{(provided) => ( @@ -62,8 +71,7 @@ function Chapter(props: any) { const ChapterWrapper = styled.div` margin-bottom: 20px; - padding: 4px; - background-color: #ffffff9d; + padding: 12px; font-size: 15px; display: block; border-radius: 9px; diff --git a/front/services/courses/activities.ts b/front/services/courses/activities.ts index d3c4e160..b5d88f27 100644 --- a/front/services/courses/activities.ts +++ b/front/services/courses/activities.ts @@ -52,6 +52,12 @@ export async function getActivity(activity_id: any, next: any) { return res; } +export async function deleteActivity(activity_id: any) { + const result = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("DELETE", null, null)); + const res = await result.json(); + return res; +} + export async function getActivityWithAuthHeader(activity_id: any, next: any, access_token: string) { const result = await fetch(`${getAPIUrl()}activities/activity_${activity_id}`, RequestBodyWithAuthHeader("GET", null, next, access_token)); const res = await result.json(); diff --git a/src/services/courses/activities/activities.py b/src/services/courses/activities/activities.py index 8a8a0aa3..44ee1779 100644 --- a/src/services/courses/activities/activities.py +++ b/src/services/courses/activities/activities.py @@ -162,10 +162,18 @@ async def delete_activity(request: Request, activity_id: str, current_user: Publ status_code=status.HTTP_409_CONFLICT, detail="activity does not exist" ) + # Remove Activity isDeleted = await activities.delete_one({"activity_id": activity_id}) - if isDeleted: - return {"detail": "activity deleted"} + # Remove Activity from chapter + courses = request.app.db["courses"] + isDeletedFromChapter = await courses.update_one( + {"chapters_content.activities": activity_id}, + {"$pull": {"chapters_content.$.activities": activity_id}}, + ) + + if isDeleted and isDeletedFromChapter: + return {"detail": "Activity deleted"} else: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE,