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) => { - +
{collections.map((collection: any) => ( -
- - -
-

{collection.name}

-
- {collection.courses.slice(0, 3).map((course: any) => ( - - {course.name} - - ))} -
-
- +
+
))} + {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={ - - } + + } />
@@ -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) => ( - - {course.name} - - ))} -
-
- +
))} + {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 ( + + + ) +} + +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 ( + + ) +} + +export default NewCourseButton \ No newline at end of file