From bb0f9166f9cb93cfeddb8ec2b2eb67e418ee401c Mon Sep 17 00:00:00 2001 From: swve Date: Fri, 19 May 2023 09:08:02 +0000 Subject: [PATCH 1/6] feat: reformat utility req functions to use next --- front/services/blocks/Image/images.ts | 4 ++-- front/services/blocks/Pdf/pdf.ts | 4 ++-- front/services/blocks/Quiz/quiz.ts | 2 +- front/services/blocks/Video/video.ts | 4 ++-- front/services/courses/activities.ts | 12 ++++++------ front/services/courses/activity.ts | 6 +++--- front/services/courses/chapters.ts | 10 +++++----- front/services/courses/collections.ts | 6 +++--- front/services/courses/courses.ts | 12 ++++++------ front/services/organizations/orgs.ts | 8 ++++---- front/services/settings/org.ts | 2 +- front/services/settings/password.ts | 2 +- front/services/settings/profile.ts | 2 +- front/services/utils/ts/requests.ts | 10 ++++++---- 14 files changed, 43 insertions(+), 41 deletions(-) diff --git a/front/services/blocks/Image/images.ts b/front/services/blocks/Image/images.ts index 0429b6dd..8a1ab1c2 100644 --- a/front/services/blocks/Image/images.ts +++ b/front/services/blocks/Image/images.ts @@ -7,14 +7,14 @@ export async function uploadNewImageFile(file: any, activity_id: string) { formData.append("file_object", file); formData.append("activity_id", activity_id); - return fetch(`${getAPIUrl()}blocks/image`, RequestBodyForm("POST", formData)) + return fetch(`${getAPIUrl()}blocks/image`, RequestBodyForm("POST", formData, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); } export async function getImageFile(file_id: string) { // todo : add course id to url - return fetch(`${getAPIUrl()}blocks/image?file_id=${file_id}`, RequestBody("GET", null)) + return fetch(`${getAPIUrl()}blocks/image?file_id=${file_id}`, RequestBody("GET", null, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); } diff --git a/front/services/blocks/Pdf/pdf.ts b/front/services/blocks/Pdf/pdf.ts index fc9b4e5e..89b520f1 100644 --- a/front/services/blocks/Pdf/pdf.ts +++ b/front/services/blocks/Pdf/pdf.ts @@ -7,14 +7,14 @@ export async function uploadNewPDFFile(file: any, activity_id: string) { formData.append("file_object", file); formData.append("activity_id", activity_id); - return fetch(`${getAPIUrl()}blocks/pdf`, RequestBodyForm("POST", formData)) + return fetch(`${getAPIUrl()}blocks/pdf`, RequestBodyForm("POST", formData, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); } export async function getPDFFile(file_id: string) { // todo : add course id to url - return fetch(`${getAPIUrl()}blocks/pdf?file_id=${file_id}`, RequestBody("GET", null)) + return fetch(`${getAPIUrl()}blocks/pdf?file_id=${file_id}`, RequestBody("GET", null, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); } diff --git a/front/services/blocks/Quiz/quiz.ts b/front/services/blocks/Quiz/quiz.ts index 0adb85dd..60e921ed 100644 --- a/front/services/blocks/Quiz/quiz.ts +++ b/front/services/blocks/Quiz/quiz.ts @@ -3,7 +3,7 @@ import { RequestBody, RequestBodyForm } from "@services/utils/ts/requests"; export async function submitQuizBlock(activity_id: string, data: any) { - const result: any = await fetch(`${getAPIUrl()}blocks/quiz/${activity_id}"`, RequestBody("POST", data)) + const result: any = await fetch(`${getAPIUrl()}blocks/quiz/${activity_id}"`, RequestBody("POST", data, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); return result; diff --git a/front/services/blocks/Video/video.ts b/front/services/blocks/Video/video.ts index bf7142b4..714a2fc3 100644 --- a/front/services/blocks/Video/video.ts +++ b/front/services/blocks/Video/video.ts @@ -7,13 +7,13 @@ export async function uploadNewVideoFile(file: any, activity_id: string) { formData.append("file_object", file); formData.append("activity_id", activity_id); - return fetch(`${getAPIUrl()}blocks/video`, RequestBodyForm("POST", formData)) + return fetch(`${getAPIUrl()}blocks/video`, RequestBodyForm("POST", formData, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); } export async function getVideoFile(file_id: string) { - return fetch(`${getAPIUrl()}blocks/video?file_id=${file_id}`, RequestBody("GET", null)) + return fetch(`${getAPIUrl()}blocks/video?file_id=${file_id}`, RequestBody("GET", null, null)) .then((result) => result.json()) .catch((error) => console.log("error", error)); } diff --git a/front/services/courses/activities.ts b/front/services/courses/activities.ts index a7976e05..a87b398e 100644 --- a/front/services/courses/activities.ts +++ b/front/services/courses/activities.ts @@ -6,7 +6,7 @@ export async function createActivity(data: any, chapter_id: any, org_id: any) { // remove chapter_id from data delete data.chapterId; - const result = await fetch(`${getAPIUrl()}activities/?coursechapter_id=${chapter_id}&org_id=${org_id}`, RequestBody("POST", data)); + const result = await fetch(`${getAPIUrl()}activities/?coursechapter_id=${chapter_id}&org_id=${org_id}`, RequestBody("POST", data, null)); const res = await result.json(); return res; } @@ -31,7 +31,7 @@ export async function createFileActivity(file: File, type: string, data: any, ch // Handle other file types here } - const result: any = await fetch(endpoint, RequestBodyForm("POST", formData)); + const result: any = await fetch(endpoint, RequestBodyForm("POST", formData, null)); const res = await result.json(); return res; } @@ -41,19 +41,19 @@ export async function createExternalVideoActivity(data: any, activity: any, chap data.coursechapter_id = chapter_id; data.activity_id = activity.id; - const result = await fetch(`${getAPIUrl()}activities/external_video?coursechapter_id=${chapter_id}`, RequestBody("POST", data)); + const result = await fetch(`${getAPIUrl()}activities/external_video?coursechapter_id=${chapter_id}`, RequestBody("POST", data, null)); const res = await result.json(); return res; } -export async function getActivity(activity_id: any) { - const result = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("GET", null)); +export async function getActivity(activity_id: any, next: any) { + const result = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("GET", null,next)); const res = await result.json(); return res; } export async function updateActivity(data: any, activity_id: any) { - const result = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("PUT", data)); + const result = await fetch(`${getAPIUrl()}activities/${activity_id}`, RequestBody("PUT", data, null)); const res = await result.json(); return res; } diff --git a/front/services/courses/activity.ts b/front/services/courses/activity.ts index aec4ec45..9ac90450 100644 --- a/front/services/courses/activity.ts +++ b/front/services/courses/activity.ts @@ -7,19 +7,19 @@ import { getAPIUrl } from "@services/config/config"; */ export async function startCourse(course_id: string, org_slug: string) { - const result: any = await fetch(`${getAPIUrl()}trail/org_slug/${org_slug}/add_course/${course_id}`, RequestBody("POST", null)) + const result: any = await fetch(`${getAPIUrl()}trail/org_slug/${org_slug}/add_course/${course_id}`, RequestBody("POST", null, null)) const res = await errorHandling(result); return res; } export async function removeCourse(course_id: string, org_slug: string) { - const result: any = await fetch(`${getAPIUrl()}trail/org_slug/${org_slug}/remove_course/${course_id}`, RequestBody("POST", null)) + const result: any = await fetch(`${getAPIUrl()}trail/org_slug/${org_slug}/remove_course/${course_id}`, RequestBody("POST", null, null)) const res = await errorHandling(result); return res; } export async function markActivityAsComplete(org_slug: string, course_id: string, activity_id: string) { - const result: any = await fetch(`${getAPIUrl()}trail/org_slug/${org_slug}/add_activity/course_id/${course_id}/activity_id/${activity_id}`, RequestBody("POST", null)) + const result: any = await fetch(`${getAPIUrl()}trail/org_slug/${org_slug}/add_activity/course_id/${course_id}/activity_id/${activity_id}`, RequestBody("POST", null, null)) const res = await errorHandling(result); return res; } diff --git a/front/services/courses/chapters.ts b/front/services/courses/chapters.ts index ddf2c521..eecaca41 100644 --- a/front/services/courses/chapters.ts +++ b/front/services/courses/chapters.ts @@ -7,27 +7,27 @@ import { RequestBody, errorHandling } from "@services/utils/ts/requests"; */ //TODO : depreciate this function -export async function getCourseChaptersMetadata(course_id: any) { - const result = await fetch(`${getAPIUrl()}chapters/meta/course_${course_id}`, RequestBody("GET", null)); +export async function getCourseChaptersMetadata(course_id: any, next: any) { + const result = await fetch(`${getAPIUrl()}chapters/meta/course_${course_id}`, RequestBody("GET", null,next)); const res = await errorHandling(result); return res; } export async function updateChaptersMetadata(course_id: any, data: any) { - const result: any = await fetch(`${getAPIUrl()}chapters/meta/course_${course_id}`, RequestBody("PUT", data)); + const result: any = await fetch(`${getAPIUrl()}chapters/meta/course_${course_id}`, RequestBody("PUT", data, null)); const res = await errorHandling(result); return res; } export async function createChapter(data: any, course_id: any) { - const result: any = await fetch(`${getAPIUrl()}chapters/?course_id=course_${course_id}`, RequestBody("POST", data)); + const result: any = await fetch(`${getAPIUrl()}chapters/?course_id=course_${course_id}`, RequestBody("POST", data, null)); const res = await errorHandling(result); return res; } export async function deleteChapter(coursechapter_id: any) { - const result: any = await fetch(`${getAPIUrl()}chapters/${coursechapter_id}`, RequestBody("DELETE", null)); + const result: any = await fetch(`${getAPIUrl()}chapters/${coursechapter_id}`, RequestBody("DELETE", null, null)); const res = await errorHandling(result); return res; } diff --git a/front/services/courses/collections.ts b/front/services/courses/collections.ts index 9228fbc2..b3e4c027 100644 --- a/front/services/courses/collections.ts +++ b/front/services/courses/collections.ts @@ -7,14 +7,14 @@ import { RequestBody, errorHandling } from "@services/utils/ts/requests"; */ export async function deleteCollection(collection_id: any) { - const result: any = await fetch(`${getAPIUrl()}collections/${collection_id}`, RequestBody("DELETE", null)); + const result: any = await fetch(`${getAPIUrl()}collections/${collection_id}`, RequestBody("DELETE", null, null)); const res = await errorHandling(result); return res; } // Create a new collection export async function createCollection(collection: any) { - const result: any = await fetch(`${getAPIUrl()}collections/`, RequestBody("POST", collection)); + const result: any = await fetch(`${getAPIUrl()}collections/`, RequestBody("POST", collection, null)); const res = await errorHandling(result); return res; } @@ -22,7 +22,7 @@ export async function createCollection(collection: any) { // Get collections // TODO : add per org filter export async function getOrgCollections() { - const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`); + const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, { next: { revalidate: 10 } }); const res = await errorHandling(result); return res; } diff --git a/front/services/courses/courses.ts b/front/services/courses/courses.ts index f200f46b..ea1b4941 100644 --- a/front/services/courses/courses.ts +++ b/front/services/courses/courses.ts @@ -6,15 +6,15 @@ import { RequestBody, RequestBodyForm, errorHandling } from "@services/utils/ts/ GET requests are called from the frontend using SWR (https://swr.vercel.app/) */ -export async function getOrgCourses(org_id: number) { - const result: any = await fetch(`${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, RequestBody("GET", null)); +export async function getOrgCourses(org_id: number, next: any) { + const result: any = await fetch(`${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, RequestBody("GET", null, next)); const res = await errorHandling(result); return res; } -export async function getCourse(course_id: string) { - const result: any = await fetch(`${getAPIUrl()}courses/${course_id}`, RequestBody("GET", null)); +export async function getCourse(course_id: string, next: any) { + const result: any = await fetch(`${getAPIUrl()}courses/${course_id}`, RequestBody("GET", null, next)); const res = await errorHandling(result); return res; } @@ -28,13 +28,13 @@ export async function createNewCourse(org_id: string, course_body: any, thumbnai formData.append("mini_description", "course_body.mini_description"); formData.append("public", "true"); - const result = await fetch(`${getAPIUrl()}courses/?org_id=${org_id}`, RequestBodyForm("POST", formData)); + const result = await fetch(`${getAPIUrl()}courses/?org_id=${org_id}`, RequestBodyForm("POST", formData, null)); const res = await errorHandling(result); return res; } export async function deleteCourseFromBackend(course_id: any) { - const result: any = await fetch(`${getAPIUrl()}courses/${course_id}`, RequestBody("DELETE", null)); + const result: any = await fetch(`${getAPIUrl()}courses/${course_id}`, RequestBody("DELETE", null, null)); const res = await errorHandling(result); return res; } diff --git a/front/services/organizations/orgs.ts b/front/services/organizations/orgs.ts index 2718df70..56a11a94 100644 --- a/front/services/organizations/orgs.ts +++ b/front/services/organizations/orgs.ts @@ -7,19 +7,19 @@ import { RequestBody, errorHandling } from "@services/utils/ts/requests"; */ export async function createNewOrganization(body: any) { - const result = await fetch(`${getAPIUrl()}orgs/`, RequestBody("POST", body)); + const result = await fetch(`${getAPIUrl()}orgs/`, RequestBody("POST", body, null)); const res = await errorHandling(result); return res; } export async function deleteOrganizationFromBackend(org_id: any) { - const result = await fetch(`${getAPIUrl()}orgs/${org_id}`, RequestBody("DELETE", null)); + const result = await fetch(`${getAPIUrl()}orgs/${org_id}`, RequestBody("DELETE", null, null)); const res = await errorHandling(result); return res; } -export async function getOrganizationContextInfo(org_slug: any) { - const result = await fetch(`${getAPIUrl()}orgs/slug/${org_slug}`, RequestBody("GET", null)); +export async function getOrganizationContextInfo(org_slug: any, next: any) { + const result = await fetch(`${getAPIUrl()}orgs/slug/${org_slug}`, RequestBody("GET", null,next)); const res = await errorHandling(result); return res; } diff --git a/front/services/settings/org.ts b/front/services/settings/org.ts index 1f4a0fd8..786eb56a 100644 --- a/front/services/settings/org.ts +++ b/front/services/settings/org.ts @@ -7,7 +7,7 @@ import { RequestBody, errorHandling } from "@services/utils/ts/requests"; */ export async function updateOrganization(org_id: string, data: any) { - const result: any = await fetch(`${getAPIUrl()}orgs/` + org_id, RequestBody("PUT", data)); + const result: any = await fetch(`${getAPIUrl()}orgs/` + org_id, RequestBody("PUT", data, null)); const res = await errorHandling(result); return res; } diff --git a/front/services/settings/password.ts b/front/services/settings/password.ts index a93483f3..86d4a659 100644 --- a/front/services/settings/password.ts +++ b/front/services/settings/password.ts @@ -7,7 +7,7 @@ import { RequestBody, errorHandling } from "@services/utils/ts/requests"; */ export async function updatePassword(user_id : string, data: any) { - const result: any = await fetch(`${getAPIUrl()}users/password/user_id/` + user_id, RequestBody("PUT", data)) + const result: any = await fetch(`${getAPIUrl()}users/password/user_id/` + user_id, RequestBody("PUT", data, null)) const res = await errorHandling(result); return res; } diff --git a/front/services/settings/profile.ts b/front/services/settings/profile.ts index 06f0667e..adfb3045 100644 --- a/front/services/settings/profile.ts +++ b/front/services/settings/profile.ts @@ -7,7 +7,7 @@ import { RequestBody, errorHandling } from "@services/utils/ts/requests"; */ export async function updateProfile(data: any) { - const result: any = await fetch(`${getAPIUrl()}users/user_id/` + data.user_id, RequestBody("PUT", data)) + const result: any = await fetch(`${getAPIUrl()}users/user_id/` + data.user_id, RequestBody("PUT", data, null)) const res = await errorHandling(result); return res; } diff --git a/front/services/utils/ts/requests.ts b/front/services/utils/ts/requests.ts index 3df81e89..2d636be4 100644 --- a/front/services/utils/ts/requests.ts +++ b/front/services/utils/ts/requests.ts @@ -1,15 +1,15 @@ import { AppRouterInstance } from "next/dist/shared/lib/app-router-context"; import { denyAccessToUser } from "../react/middlewares/views"; -export const RequestBody = (method: string, data: any) => { +export const RequestBody = (method: string, data: any, next: any) => { let HeadersConfig = new Headers({ "Content-Type": "application/json" }); let options: any = { method: method, headers: HeadersConfig, redirect: "follow", credentials: "include", - // Next.js - cache: 'no-store' + // Next.js + next: next, }; if (data) { options.body = JSON.stringify(data); @@ -17,7 +17,7 @@ export const RequestBody = (method: string, data: any) => { return options; }; -export const RequestBodyForm = (method: string, data: any) => { +export const RequestBodyForm = (method: string, data: any, next: any) => { let HeadersConfig = new Headers({}); let options: any = { method: method, @@ -25,6 +25,8 @@ export const RequestBodyForm = (method: string, data: any) => { redirect: "follow", credentials: "include", body: data, + // Next.js + next: next, }; return options; }; From 86e4ec64b56b15c0488d6e26e462a9d7598ddad8 Mon Sep 17 00:00:00 2001 From: swve Date: Fri, 19 May 2023 09:09:13 +0000 Subject: [PATCH 2/6] feat: init metadata for homepage --- .../[orgslug]/(withmenu)/courses/page.tsx | 2 +- front/app/orgs/[orgslug]/(withmenu)/page.tsx | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx index 831dc15e..d0099bba 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx @@ -11,7 +11,7 @@ export const metadata: Metadata = { const CoursesPage = async (params: any) => { const orgslug = params.params.orgslug; - const courses = await getOrgCourses(orgslug); + const courses = await getOrgCourses(orgslug, { revalidate: 360 }); return (
diff --git a/front/app/orgs/[orgslug]/(withmenu)/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/page.tsx index 94b1c30d..23c6ac4d 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/page.tsx @@ -1,5 +1,5 @@ export const dynamic = 'force-dynamic'; - +import { Metadata, ResolvingMetadata } from 'next'; import { Menu } from "@components/UI/Elements/Menu"; import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getOrgCourses } from "@services/courses/courses"; @@ -10,13 +10,28 @@ import Image from "next/image"; import { log } from "console"; import AuthProvider from "@components/Security/AuthProvider"; import { getOrgCollections } from "@services/courses/collections"; +import { getOrganizationContextInfo } from '@services/organizations/orgs'; + +type MetadataProps = { + params: { orgslug: string }; + searchParams: { [key: string]: string | string[] | undefined }; +}; + +export async function generateMetadata( + { params }: MetadataProps, +): Promise { + + // Get Org context information + const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800 }); + return { + title: org.name + " — Home", + description: org.description, + }; +} const OrgHomePage = async (params: any) => { const orgslug = params.params.orgslug; - // timeout to simulate a slow connection - // await new Promise((resolve) => setTimeout(resolve, 12000)); - - const courses = await getOrgCourses(orgslug); + const courses = await getOrgCourses(orgslug, { revalidate: 360 }); const collections = await getOrgCollections(); // function to remove "course_" from the course_id @@ -26,9 +41,6 @@ const OrgHomePage = async (params: any) => { return (
- - -
{/* Collections */} From cc6cc52ba5fc88e4688747f270683fbf579b6b12 Mon Sep 17 00:00:00 2001 From: swve <bdswve@gmail.com> Date: Fri, 19 May 2023 09:33:54 +0000 Subject: [PATCH 3/6] feat: use Next.js on demand revalidation --- front/app/api/revalidate/route.ts | 19 +++++++++++++++++++ .../[orgslug]/(withmenu)/courses/courses.tsx | 6 ++++-- .../[orgslug]/(withmenu)/courses/page.tsx | 4 ++-- front/app/orgs/[orgslug]/(withmenu)/page.tsx | 4 ++-- .../Modals/Course/Create/CreateCourse.tsx | 7 ++++--- front/services/config/config.ts | 2 +- front/services/utils/ts/requests.ts | 7 +++++++ 7 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 front/app/api/revalidate/route.ts diff --git a/front/app/api/revalidate/route.ts b/front/app/api/revalidate/route.ts new file mode 100644 index 00000000..c3d0aa71 --- /dev/null +++ b/front/app/api/revalidate/route.ts @@ -0,0 +1,19 @@ +import { NextRequest, NextResponse } from "next/server"; +import { revalidateTag } from "next/cache"; + +export async function GET(request: NextRequest) { + const tag: any = request.nextUrl.searchParams.get("tag"); + revalidateTag(tag); + + return NextResponse.json( + { revalidated: true, now: Date.now() }, + { + status: 200, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type, Authorization", + }, + } + ); +} diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx index a37f2010..079cc54a 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx @@ -10,6 +10,7 @@ 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'; interface CourseProps { orgslug: string; @@ -28,13 +29,14 @@ function Courses(props: CourseProps) { async function deleteCourses(course_id: any) { await deleteCourseFromBackend(course_id); + revalidateTags(['courses']); } async function closeNewCourseModal() { setNewCourseModal(false); } - + return ( @@ -103,7 +105,7 @@ const AdminEditsArea = (props: any) => { // this is amazingly terrible code, but gotta release that MVP // TODO: fix this - + function isAuthorized() { const org_id = props.course.org_id; const org_roles = auth.userInfo.user_object.orgs; diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx index d0099bba..aa66b09a 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx @@ -11,11 +11,11 @@ export const metadata: Metadata = { const CoursesPage = async (params: any) => { const orgslug = params.params.orgslug; - const courses = await getOrgCourses(orgslug, { revalidate: 360 }); + const courses = await getOrgCourses(orgslug, { revalidate: 360, tags: ['courses'] }); return ( <div> - <Courses orgslug={orgslug} courses={courses}/> + <Courses orgslug={orgslug} courses={courses} /> </div> ); }; diff --git a/front/app/orgs/[orgslug]/(withmenu)/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/page.tsx index 23c6ac4d..ee1d2d7f 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/page.tsx @@ -22,7 +22,7 @@ export async function generateMetadata( ): Promise<Metadata> { // Get Org context information - const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800 }); + const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); return { title: org.name + " — Home", description: org.description, @@ -31,7 +31,7 @@ export async function generateMetadata( const OrgHomePage = async (params: any) => { const orgslug = params.params.orgslug; - const courses = await getOrgCourses(orgslug, { revalidate: 360 }); + const courses = await getOrgCourses(orgslug, { revalidate: 360 , tags: ['courses'] }); const collections = await getOrgCollections(); // function to remove "course_" from the course_id diff --git a/front/components/Modals/Course/Create/CreateCourse.tsx b/front/components/Modals/Course/Create/CreateCourse.tsx index f182bb38..96a7456e 100644 --- a/front/components/Modals/Course/Create/CreateCourse.tsx +++ b/front/components/Modals/Course/Create/CreateCourse.tsx @@ -1,4 +1,4 @@ -import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, Input, Textarea } from '@components/UI/Form/Form' +import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, Input, Textarea } from '@components/UI/Form/Form' import * as Form from '@radix-ui/react-form' import { getAPIUrl, getUriWithOrg } from '@services/config/config'; import { FormMessage } from "@radix-ui/react-form"; @@ -7,6 +7,7 @@ import { getOrganizationContextInfo } 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'; function CreateCourseModal({ closeModal, orgslug }: any) { const [isSubmitting, setIsSubmitting] = useState(false); @@ -19,7 +20,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) { const getOrgMetadata = async () => { - const org = await getOrganizationContextInfo(orgslug); + const org = await getOrganizationContextInfo(orgslug, { revalidate: 360, tags: ['organizations'] }); setOrgId(org.org_id); } @@ -40,7 +41,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) { e.preventDefault(); setIsSubmitting(true); let status = await createNewCourse(orgId, { name, description }, thumbnail); - mutate(`${getAPIUrl()}courses/org_slug/${orgslug}/page/1/limit/10`); + revalidateTags(['courses']); setIsSubmitting(false); if (status.org_id == orgId) { diff --git a/front/services/config/config.ts b/front/services/config/config.ts index fd674bf0..4fca83ce 100644 --- a/front/services/config/config.ts +++ b/front/services/config/config.ts @@ -1,4 +1,4 @@ -const LEARNHOUSE_HTTP_PROTOCOL = process.env.NEXT_PUBLIC_LEARNHOUSE_HTTPS === "true" ? "https://" : "http://"; +export const LEARNHOUSE_HTTP_PROTOCOL = process.env.NEXT_PUBLIC_LEARNHOUSE_HTTPS === "true" ? "https://" : "http://"; const LEARNHOUSE_API_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_API_URL}`; const LEARNHOUSE_BACKEND_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}`; export const LEARNHOUSE_DOMAIN = process.env.NEXT_PUBLIC_LEARNHOUSE_DOMAIN; diff --git a/front/services/utils/ts/requests.ts b/front/services/utils/ts/requests.ts index 2d636be4..07ba1142 100644 --- a/front/services/utils/ts/requests.ts +++ b/front/services/utils/ts/requests.ts @@ -1,5 +1,6 @@ import { AppRouterInstance } from "next/dist/shared/lib/app-router-context"; import { denyAccessToUser } from "../react/middlewares/views"; +import { LEARNHOUSE_DOMAIN, LEARNHOUSE_HTTP_PROTOCOL } from "@services/config/config"; export const RequestBody = (method: string, data: any, next: any) => { let HeadersConfig = new Headers({ "Content-Type": "application/json" }); @@ -69,3 +70,9 @@ export const errorHandling = (res: any) => { } return res.json(); }; + +export const revalidateTags = (tags: string[]) => { + tags.forEach((tag) => { + fetch(`${LEARNHOUSE_HTTP_PROTOCOL}${LEARNHOUSE_DOMAIN}/api/revalidate?tag=${tag}`); + }); +}; From 82eda2351579412871857770aa91e27eeab12eb1 Mon Sep 17 00:00:00 2001 From: swve <bdswve@gmail.com> Date: Fri, 19 May 2023 09:38:49 +0000 Subject: [PATCH 4/6] fix: missing next options --- front/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx | 2 +- .../orgs/[orgslug]/(withmenu)/course/[courseid]/edit/page.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/front/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx index 28185a44..e1de0cb6 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/collections/new/page.tsx @@ -20,7 +20,7 @@ function NewCollection(params: any) { React.useEffect(() => { async function getOrg() { - const org = await getOrganizationContextInfo(orgslug); + const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800 }); setOrg(org); } getOrg(); diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/page.tsx index bbb0b04f..0dda558b 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/edit/page.tsx @@ -35,7 +35,7 @@ function CourseEdit(params: any) { async function getCourseChapters() { try { - const courseChapters = await getCourseChaptersMetadata(courseid); + const courseChapters = await getCourseChaptersMetadata(courseid, { revalidate: 120 }); setData(courseChapters); } catch (error: any) { denyAccessToUser(error, router) @@ -80,7 +80,7 @@ function CourseEdit(params: any) { // Submit new activity const submitActivity = async (activity: any) => { console.log("submitActivity", activity); - let org = await getOrganizationContextInfo(orgslug); + let org = await getOrganizationContextInfo(orgslug, { revalidate: 1800 }); await updateChaptersMetadata(courseid, data); await createActivity(activity, activity.chapterId, org.org_id); await getCourseChapters(); From c07acf5ddf9fe1241b13aac8fbad47e4a96798ee Mon Sep 17 00:00:00 2001 From: swve <bdswve@gmail.com> Date: Fri, 19 May 2023 18:57:58 +0200 Subject: [PATCH 5/6] feat: add dynamic metadata for courses page --- .../[orgslug]/(withmenu)/courses/page.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx index aa66b09a..17dd3580 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx @@ -3,12 +3,25 @@ import React from "react"; import Courses from "./courses"; import { getOrgCourses } from "@services/courses/courses"; import { Metadata } from "next"; +import { getOrganizationContextInfo } from "@services/organizations/orgs"; -export const metadata: Metadata = { - title: 'LearnHouse - Courses', - description: 'courses', +type MetadataProps = { + params: { orgslug: string }; + searchParams: { [key: string]: string | string[] | undefined }; }; +export async function generateMetadata( + { params }: MetadataProps, +): Promise<Metadata> { + + // Get Org context information + const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); + return { + title: org.name + " — Courses", + description: org.description, + }; +} + const CoursesPage = async (params: any) => { const orgslug = params.params.orgslug; const courses = await getOrgCourses(orgslug, { revalidate: 360, tags: ['courses'] }); From 887f0b745a19c8851a3c02e6f99fb095ee132bfb Mon Sep 17 00:00:00 2001 From: swve <bdswve@gmail.com> Date: Sun, 21 May 2023 13:28:38 +0200 Subject: [PATCH 6/6] feat: change favicon --- front/public/favicon.ico | Bin 25931 -> 15406 bytes front/public/vercel.svg | 4 ---- 2 files changed, 4 deletions(-) delete mode 100644 front/public/vercel.svg diff --git a/front/public/favicon.ico b/front/public/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..a3082559db8c705734a01ae8c820d3e5c5457ab6 100644 GIT binary patch literal 15406 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 ZoZqXwn}qFe{eGqer+;q!6M^{%{1-eu<nRCh literal 25931 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 LrJugUA?W`A8`#=m diff --git a/front/public/vercel.svg b/front/public/vercel.svg deleted file mode 100644 index fbf0e25a..00000000 --- a/front/public/vercel.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="283" height="64" viewBox="0 0 283 64" fill="none" - xmlns="http://www.w3.org/2000/svg"> - <path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/> -</svg> \ No newline at end of file