diff --git a/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx b/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx deleted file mode 100644 index 09ad6d53..00000000 --- a/front/app/orgs/[orgslug]/(withmenu)/collections/admin.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; -import { AuthContext } from '@components/Security/AuthProvider'; -import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'; -import { getUriWithOrg } from '@services/config/config'; -import { deleteCollection } from '@services/courses/collections'; -import { revalidateTags } from '@services/utils/ts/requests'; -import { Link, Trash, X } from 'lucide-react'; -import { useRouter } from 'next/navigation'; -import React from 'react' - -const CollectionAdminEditsArea = (props: any) => { - const router = useRouter(); - - const deleteCollectionUI = async (collectionId: number) => { - await deleteCollection(collectionId); - await revalidateTags(["collections"], props.orgslug); - // reload the page - router.refresh(); - router.push(getUriWithOrg(props.orgslug, "/collections")); - - // refresh page (FIX for Next.js BUG) - //window.location.reload(); - } - - return ( - - - - - } - functionToExecute={() => deleteCollectionUI(props.collection_id)} - status='warning' - > - - - ) -} - -export default CollectionAdminEditsArea; \ No newline at end of file diff --git a/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx index 82399971..6e97087a 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx @@ -1,15 +1,15 @@ import AuthenticatedClientElement from "@components/Security/AuthenticatedClientElement"; import TypeOfContentTitle from "@components/StyledElements/Titles/TypeOfContentTitle"; import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; -import { getBackendUrl, getUriWithOrg } from "@services/config/config"; -import { deleteCollection, getOrgCollectionsWithAuthHeader } from "@services/courses/collections"; +import { getUriWithOrg } from "@services/config/config"; +import { getOrgCollectionsWithAuthHeader } from "@services/courses/collections"; import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { Metadata } from "next"; import { cookies } from "next/headers"; import Link from "next/link"; -import CollectionAdminEditsArea from "./admin"; -import { getCourseThumbnailMediaDirectory } from "@services/media/media"; -import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; +import { getAccessTokenFromRefreshTokenCookie } from "@services/auth/auth"; +import CollectionThumbnail from "@components/Objects/Other/CollectionThumbnail"; +import NewCollectionButton from "@components/StyledElements/Buttons/NewCollectionButton"; type MetadataProps = { params: { orgslug: string, courseid: string }; @@ -19,10 +19,9 @@ type MetadataProps = { export async function generateMetadata( { params }: MetadataProps, ): Promise { - const cookieStore = cookies(); // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); - + // SEO return { title: `Collections — ${org.name}`, @@ -45,11 +44,6 @@ export async function generateMetadata( }; } -const removeCollectionPrefix = (collectionid: string) => { - return collectionid.replace("collection_", "") -} - - const CollectionsPage = async (params: any) => { const cookieStore = cookies(); const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) @@ -64,28 +58,37 @@ const CollectionsPage = async (params: any) => { - Add Collection + + {collections.map((collection: any) => ( - - - - - {collection.name} - - {collection.courses.slice(0, 3).map((course: any) => ( - - - - ))} - - - + + ))} + {collections.length == 0 && + + + + + + + + + + No collections yet + Create a collection to group courses together + + + + + + + + + } ); diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx index f5af8a02..f8265a07 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx @@ -1,22 +1,13 @@ 'use client'; import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse'; import Modal from '@components/StyledElements/Modal/Modal'; -import { getBackendUrl, getUriWithOrg } from '@services/config/config'; -import CoursesLogo from "public/svg/courses.svg"; -import CollectionsLogo from "public/svg/collections.svg"; -import { deleteCourseFromBackend } from '@services/courses/courses'; -import Link from 'next/link'; import React from 'react' -import Image from 'next/image'; -import { AuthContext } from '@components/Security/AuthProvider'; -import { revalidateTags } from '@services/utils/ts/requests'; -import { useRouter } from 'next/navigation'; +import { useSearchParams } from 'next/navigation'; import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'; import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'; 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'; +import CourseThumbnail from '@components/Objects/Other/CourseThumbnail'; +import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton'; interface CourseProps { orgslug: string; @@ -24,23 +15,12 @@ interface CourseProps { org_id: string; } -// function to remove "course_" from the course_id -function removeCoursePrefix(course_id: string) { - return course_id.replace("course_", ""); -} - function Courses(props: CourseProps) { const orgslug = props.orgslug; const courses = props.courses; - const [newCourseModal, setNewCourseModal] = React.useState(false); - const router = useRouter(); - - async function deleteCourses(course_id: any) { - await deleteCourseFromBackend(course_id); - await revalidateTags(['courses'], orgslug); - - router.refresh(); - } + const searchParams = useSearchParams(); + const isCreatingCourse = searchParams.get('new') ? true : false; + const [newCourseModal, setNewCourseModal] = React.useState(isCreatingCourse); async function closeNewCourseModal() { setNewCourseModal(false); @@ -64,8 +44,10 @@ function Courses(props: CourseProps) { dialogTitle="Create Course" dialogDescription="Create a new course" dialogTrigger={ - Add Course + - } + + + + } /> @@ -75,48 +57,51 @@ function Courses(props: CourseProps) { {courses.map((course: any) => ( - - - - - - - {course.name} + ))} + {courses.length == 0 && + + + + + + + + + + No courses yet + Create a course to add content + + + } + dialogTitle="Create Course" + dialogDescription="Create a new course" + dialogTrigger={ + + + } + /> + + + + } + + ) } -const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => { - return ( - - - - } - functionToExecute={() => props.deleteCourses(props.courseId)} - status='warning' - > - - - - - - - - ) -} + export default Courses \ No newline at end of file diff --git a/front/app/orgs/[orgslug]/(withmenu)/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/page.tsx index 83675fb2..518c32e2 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/page.tsx @@ -1,18 +1,20 @@ export const dynamic = 'force-dynamic'; -import { Metadata, ResolvingMetadata } from 'next'; -import { getBackendUrl, getUriWithOrg } from "@services/config/config"; -import { getCourse, getOrgCourses, getOrgCoursesWithAuthHeader } from "@services/courses/courses"; - +import { Metadata } from 'next'; +import { getUriWithOrg } from "@services/config/config"; +import { getOrgCoursesWithAuthHeader } from "@services/courses/courses"; import Link from "next/link"; -import Image from "next/image"; -import { getOrgCollections, getOrgCollectionsWithAuthHeader } from "@services/courses/collections"; +import { getOrgCollectionsWithAuthHeader } from "@services/courses/collections"; import { getOrganizationContextInfo } from '@services/organizations/orgs'; - import { cookies } from 'next/headers'; import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'; import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'; -import { getCourseThumbnailMediaDirectory } from '@services/media/media'; -import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from '@services/auth/auth'; +import { getAccessTokenFromRefreshTokenCookie } from '@services/auth/auth'; +import CourseThumbnail from '@components/Objects/Other/CourseThumbnail'; +import CollectionThumbnail from '@components/Objects/Other/CollectionThumbnail'; +import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; +import { Plus, PlusCircle } from 'lucide-react'; +import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton'; +import NewCollectionButton from '@components/StyledElements/Buttons/NewCollectionButton'; type MetadataProps = { params: { orgslug: string }; @@ -54,55 +56,85 @@ const OrgHomePage = async (params: any) => { const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null); const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); + const org_id = org.org_id; const collections = await getOrgCollectionsWithAuthHeader(org.org_id, access_token ? access_token : null, { revalidate: 0, tags: ['courses'] }); - - // function to remove "course_" from the course_id - function removeCoursePrefix(course_id: string) { - return course_id.replace("course_", ""); - } - - function removeCollectionPrefix(collection_id: string) { - return collection_id.replace("collection_", ""); - } - return ( {/* Collections */} - + + + + + + + + + + {collections.map((collection: any) => ( - - - {collection.name} - - {collection.courses.slice(0, 3).map((course: any) => ( - - - - ))} - - - + ))} + {collections.length == 0 && + + + + + + + + + + + No collections yet + Create a collection to group courses together + + + + + } {/* Courses */} - + + + + + + + + + + {courses.map((course: any) => ( - - - - - {course.name} + ))} + {courses.length == 0 && + + + + + + + + + + + No courses yet + Create a course to add content + + + + + } diff --git a/front/components/Objects/Modals/Course/Create/CreateCourse.tsx b/front/components/Objects/Modals/Course/Create/CreateCourse.tsx index 9456dfc2..a6ec2a09 100644 --- a/front/components/Objects/Modals/Course/Create/CreateCourse.tsx +++ b/front/components/Objects/Modals/Course/Create/CreateCourse.tsx @@ -1,12 +1,10 @@ import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, Input, Textarea } from '@components/StyledElements/Form/Form' import * as Form from '@radix-ui/react-form' -import { getAPIUrl, getUriWithOrg } from '@services/config/config'; import { FormMessage } from "@radix-ui/react-form"; import { createNewCourse } from '@services/courses/courses'; -import { getOrganizationContextInfo, getOrganizationContextInfoWithoutCredentials } from '@services/organizations/orgs'; +import { getOrganizationContextInfoWithoutCredentials } from '@services/organizations/orgs'; import React, { useState } from 'react' import { BarLoader } from 'react-spinners' -import { mutate } from 'swr'; import { revalidateTags } from '@services/utils/ts/requests'; import { useRouter } from 'next/navigation'; @@ -96,7 +94,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) { - Course Learnings + Course keywords Please provide learning elements, separated by comma (,) diff --git a/front/components/Objects/Other/CollectionThumbnail.tsx b/front/components/Objects/Other/CollectionThumbnail.tsx new file mode 100644 index 00000000..fc38df48 --- /dev/null +++ b/front/components/Objects/Other/CollectionThumbnail.tsx @@ -0,0 +1,77 @@ +"use client"; +import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement' +import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal' +import { getUriWithOrg } from '@services/config/config' +import { deleteCollection } from '@services/courses/collections' +import { getCourseThumbnailMediaDirectory } from '@services/media/media' +import { revalidateTags } from '@services/utils/ts/requests' +import { X } from 'lucide-react' +import Link from 'next/link' +import { useRouter } from 'next/navigation' +import React from 'react' + +type PropsType = { + collection: any, + orgslug: string, + org_id: string +} + +const removeCollectionPrefix = (collectionid: string) => { + return collectionid.replace("collection_", "") +} + +function CollectionThumbnail(props: PropsType) { + return ( + + + + {props.collection.courses.slice(0, 2).map((course: any) => ( + <> + + + + + > + ))} + + + {props.collection.name} + + + + + ) +} + +const CollectionAdminEditsArea = (props: any) => { + const router = useRouter(); + + const deleteCollectionUI = async (collectionId: number) => { + await deleteCollection(collectionId); + await revalidateTags(["collections"], props.orgslug); + // reload the page + router.refresh(); + } + + return ( + + + + + } + functionToExecute={() => deleteCollectionUI(props.collection_id)} + status='warning' + > + + + ) +} + +export default CollectionThumbnail \ No newline at end of file diff --git a/front/components/Objects/Other/CourseThumbnail.tsx b/front/components/Objects/Other/CourseThumbnail.tsx new file mode 100644 index 00000000..3ac0fdd6 --- /dev/null +++ b/front/components/Objects/Other/CourseThumbnail.tsx @@ -0,0 +1,75 @@ +"use client"; +import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'; +import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'; +import { getUriWithOrg } from '@services/config/config'; +import { deleteCourseFromBackend } from '@services/courses/courses'; +import { getCourseThumbnailMediaDirectory } from '@services/media/media'; +import { revalidateTags } from '@services/utils/ts/requests'; +import { FileEdit, X } from 'lucide-react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import React from 'react' + +type PropsType = { + course: any, + orgslug: string +} + +// function to remove "course_" from the course_id +function removeCoursePrefix(course_id: string) { + return course_id.replace("course_", ""); +} + +function CourseThumbnail(props: PropsType) { + const router = useRouter(); + + async function deleteCourses(course_id: any) { + await deleteCourseFromBackend(course_id); + await revalidateTags(['courses'], props.orgslug); + + router.refresh(); + } + + return ( + + + + + + + + {props.course.name} + + ) +} + +const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => { + return ( + + + + + + + + + + } + functionToExecute={() => props.deleteCourses(props.courseId)} + status='warning' + > + + + ) +} + +export default CourseThumbnail \ No newline at end of file diff --git a/front/components/StyledElements/Buttons/NewCollectionButton.tsx b/front/components/StyledElements/Buttons/NewCollectionButton.tsx new file mode 100644 index 00000000..2008e5d4 --- /dev/null +++ b/front/components/StyledElements/Buttons/NewCollectionButton.tsx @@ -0,0 +1,13 @@ +"use client"; + +function NewCollectionButton() { + return ( + + New Collection + + + + + ) +} + +export default NewCollectionButton \ No newline at end of file diff --git a/front/components/StyledElements/Buttons/NewCourseButton.tsx b/front/components/StyledElements/Buttons/NewCourseButton.tsx new file mode 100644 index 00000000..6511f26b --- /dev/null +++ b/front/components/StyledElements/Buttons/NewCourseButton.tsx @@ -0,0 +1,12 @@ +"use client"; + +function NewCourseButton() { + return ( + + New Course + + + + ) +} + +export default NewCourseButton \ No newline at end of file
Create a collection to group courses together
Create a course to add content