diff --git a/apps/api/src/db/activities.py b/apps/api/src/db/activities.py
index ada0d56d..95b6ecef 100644
--- a/apps/api/src/db/activities.py
+++ b/apps/api/src/db/activities.py
@@ -34,12 +34,6 @@ class ActivityBase(SQLModel):
content: dict = Field(default={}, sa_column=Column(JSON))
published_version: int
version: int
- course_id: int = Field(
- default=None,
- sa_column=Column(
- BigInteger, ForeignKey("course.id", ondelete="CASCADE")
- ),
- )
class Activity(ActivityBase, table=True):
@@ -57,9 +51,6 @@ class Activity(ActivityBase, table=True):
class ActivityCreate(ActivityBase):
- course_id: int = Field(
- sa_column=Column("course_id", ForeignKey("course.id", ondelete="CASCADE"))
- )
chapter_id: int
pass
diff --git a/apps/api/src/db/courses.py b/apps/api/src/db/courses.py
index 686538a2..a6a4199d 100644
--- a/apps/api/src/db/courses.py
+++ b/apps/api/src/db/courses.py
@@ -1,6 +1,6 @@
from typing import List, Optional
from sqlmodel import Field, SQLModel
-from src.db.users import User, UserRead
+from src.db.users import UserRead
from src.db.trails import TrailRead
from src.db.chapters import ChapterRead
diff --git a/apps/api/src/routers/courses/chapters.py b/apps/api/src/routers/courses/chapters.py
index be87247d..f6cf42db 100644
--- a/apps/api/src/routers/courses/chapters.py
+++ b/apps/api/src/routers/courses/chapters.py
@@ -6,7 +6,6 @@ from src.db.chapters import (
ChapterRead,
ChapterUpdate,
ChapterUpdateOrder,
- DepreceatedChaptersRead,
)
from src.services.courses.chapters import (
DEPRECEATED_get_course_chapters,
diff --git a/apps/api/src/services/courses/activities/activities.py b/apps/api/src/services/courses/activities/activities.py
index 0912937d..ba5ff82e 100644
--- a/apps/api/src/services/courses/activities/activities.py
+++ b/apps/api/src/services/courses/activities/activities.py
@@ -5,7 +5,6 @@ from src.security.rbac.rbac import (
authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
-from src.db.organizations import Organization
from src.db.activities import ActivityCreate, Activity, ActivityRead, ActivityUpdate
from src.db.chapter_activities import ChapterActivity
from src.db.users import AnonymousUser, PublicUser
@@ -44,6 +43,7 @@ async def create_activity(
activity.creation_date = str(datetime.now())
activity.update_date = str(datetime.now())
activity.org_id = chapter.org_id
+ activity.course_id = chapter.course_id
# Insert Activity in DB
db_session.add(activity)
@@ -65,7 +65,7 @@ async def create_activity(
activity_chapter = ChapterActivity(
chapter_id=activity_object.chapter_id,
activity_id=activity.id if activity.id else 0,
- course_id=activity_object.course_id,
+ course_id=chapter.course_id,
org_id=chapter.org_id,
creation_date=str(datetime.now()),
update_date=str(datetime.now()),
diff --git a/apps/api/src/services/courses/activities/video.py b/apps/api/src/services/courses/activities/video.py
index 8e25b9de..d73a311b 100644
--- a/apps/api/src/services/courses/activities/video.py
+++ b/apps/api/src/services/courses/activities/video.py
@@ -194,7 +194,7 @@ async def create_external_video_activity(
# update chapter
chapter_activity_object = ChapterActivity(
- chapter_id=coursechapter.id, # type: ignore
+ chapter_id=coursechapter.chapter_id, # type: ignore
activity_id=activity.id, # type: ignore
course_id=coursechapter.course_id,
org_id=coursechapter.org_id,
diff --git a/apps/api/src/services/courses/chapters.py b/apps/api/src/services/courses/chapters.py
index f610d6fe..1e166d74 100644
--- a/apps/api/src/services/courses/chapters.py
+++ b/apps/api/src/services/courses/chapters.py
@@ -16,7 +16,6 @@ from src.db.chapters import (
ChapterRead,
ChapterUpdate,
ChapterUpdateOrder,
- DepreceatedChaptersRead,
)
from src.services.courses.courses import Course
from src.services.users.users import PublicUser
@@ -270,7 +269,6 @@ async def DEPRECEATED_get_course_chapters(
chapters_in_db = await get_course_chapters(request, course.id, db_session, current_user) # type: ignore
# activities
- chapter_activityIdsGlobal = []
# chapters
chapters = {}
@@ -437,7 +435,7 @@ async def reorder_chapters_and_activities(
# Activities
###########
- # Delete ChapterActivities that are not linked to chapter_id and activity_id and org_id and course_id
+ # Delete ChapterActivities that are no longer part of the new order
statement = (
select(ChapterActivity)
.where(
@@ -448,20 +446,36 @@ async def reorder_chapters_and_activities(
)
chapter_activities = db_session.exec(statement).all()
- activity_ids_to_keep = [
- activity_order.activity_id
- for chapter_order in chapters_order.chapter_order_by_ids
- for activity_order in chapter_order.activities_order_by_ids
- ]
-
+ activity_ids_to_delete = []
for chapter_activity in chapter_activities:
- if chapter_activity.activity_id not in activity_ids_to_keep:
- db_session.delete(chapter_activity)
- db_session.commit()
+ if (
+ chapter_activity.chapter_id not in chapter_ids_to_keep
+ or chapter_activity.activity_id not in activity_ids_to_delete
+ ):
+ activity_ids_to_delete.append(chapter_activity.activity_id)
- # If links do not exists, create them
+ for activity_id in activity_ids_to_delete:
+ statement = (
+ select(ChapterActivity)
+ .where(
+ ChapterActivity.activity_id == activity_id,
+ ChapterActivity.course_id == course.id,
+ )
+ .order_by(ChapterActivity.order)
+ )
+ chapter_activity = db_session.exec(statement).first()
+
+ db_session.delete(chapter_activity)
+ db_session.commit()
+
+
+ # If links do not exist, create them
+ chapter_activity_map = {}
for chapter_order in chapters_order.chapter_order_by_ids:
for activity_order in chapter_order.activities_order_by_ids:
+ if activity_order.activity_id in chapter_activity_map and chapter_activity_map[activity_order.activity_id] != chapter_order.chapter_id:
+ continue
+
statement = (
select(ChapterActivity)
.where(
@@ -488,6 +502,8 @@ async def reorder_chapters_and_activities(
db_session.add(chapter_activity)
db_session.commit()
+ chapter_activity_map[activity_order.activity_id] = chapter_order.chapter_id
+
# Update order of activities
for chapter_order in chapters_order.chapter_order_by_ids:
for activity_order in chapter_order.activities_order_by_ids:
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx
new file mode 100644
index 00000000..d3a811f3
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx
@@ -0,0 +1,59 @@
+'use client';
+import EditCourseStructure from '../../../../../../../../components/Dashboard/EditCourseStructure/EditCourseStructure'
+import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import PageLoading from '@components/Objects/Loaders/PageLoading';
+import ClientComponentSkeleton from '@components/Utils/ClientComp';
+import { getAPIUrl, getUriWithOrg } from '@services/config/config';
+import { swrFetcher } from '@services/utils/ts/requests';
+import React, { createContext, use, useEffect, useState } from 'react'
+import useSWR from 'swr';
+import { CourseProvider, useCourse } from '../../../../../../../../components/Dashboard/CourseContext';
+import SaveState from '@components/Dashboard/UI/SaveState';
+import Link from 'next/link';
+import { CourseOverviewTop } from '@components/Dashboard/UI/CourseOverviewTop';
+
+export type CourseOverviewParams = {
+ orgslug: string,
+ courseuuid: string,
+ subpage: string
+}
+
+export const CourseStructureContext = createContext({}) as any;
+
+
+function CourseOverviewPage({ params }: { params: CourseOverviewParams }) {
+
+ function getEntireCourseUUID(courseuuid: string) {
+ // add course_ to uuid
+ return `course_${courseuuid}`
+ }
+
+ return (
+
+
+
+
+
+
+
General
+
+
+
Structure
+
+
+
+
+
+
+ {params.subpage == 'structure' ? : ''}
+
+
+
+ )
+}
+
+
+
+
+
+export default CourseOverviewPage
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/page.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/page.tsx
new file mode 100644
index 00000000..8f422f1e
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/courses/page.tsx
@@ -0,0 +1,22 @@
+'use client';
+import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import Link from 'next/link'
+import React from 'react'
+
+function CoursesHome() {
+ return (
+
+ )
+}
+
+export default CoursesHome
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/layout.tsx b/apps/web/app/orgs/[orgslug]/dash/layout.tsx
new file mode 100644
index 00000000..b651d773
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/layout.tsx
@@ -0,0 +1,20 @@
+import LeftMenu from '@components/Dashboard/UI/LeftMenu'
+import AuthProvider from '@components/Security/AuthProvider'
+import React from 'react'
+
+function DashboardLayout({ children, params }: { children: React.ReactNode, params: any }) {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default DashboardLayout
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/dash/page.tsx b/apps/web/app/orgs/[orgslug]/dash/page.tsx
new file mode 100644
index 00000000..ce5f585b
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/page.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+function DashboardHome() {
+ return (
+ DashboardHome
+ )
+}
+
+export default DashboardHome
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/layout.tsx b/apps/web/app/orgs/[orgslug]/layout.tsx
index d9d693ce..548477a0 100644
--- a/apps/web/app/orgs/[orgslug]/layout.tsx
+++ b/apps/web/app/orgs/[orgslug]/layout.tsx
@@ -1,4 +1,3 @@
-
import "@styles/globals.css";
export default function RootLayout({ children, params }: { children: React.ReactNode , params:any}) {
diff --git a/apps/web/app/orgs/[orgslug]/template.tsx b/apps/web/app/orgs/[orgslug]/template.tsx
deleted file mode 100644
index 6fca4b95..00000000
--- a/apps/web/app/orgs/[orgslug]/template.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-"use client";
-import { motion } from "framer-motion";
-export default function Template({ children }: { children: React.ReactNode }) {
-
- const variants = {
- hidden: { opacity: 0, x: 0, y: 0 },
- enter: { opacity: 1, x: 0, y: 0 },
- exit: { opacity: 0, x: 0, y: 0 },
- };
-
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/apps/web/components/Dashboard/CourseContext.tsx b/apps/web/components/Dashboard/CourseContext.tsx
new file mode 100644
index 00000000..a37d2a5e
--- /dev/null
+++ b/apps/web/components/Dashboard/CourseContext.tsx
@@ -0,0 +1,62 @@
+'use client';
+import { getAPIUrl } from '@services/config/config';
+import { swrFetcher } from '@services/utils/ts/requests';
+import React, { createContext, useContext, useEffect, useReducer } from 'react'
+import useSWR, { mutate } from 'swr';
+
+export const CourseContext = createContext(null) as any;
+export const CourseDispatchContext = createContext(null) as any;
+
+export function CourseProvider({ children, courseuuid }: { children: React.ReactNode, courseuuid: string }) {
+ const { data: courseStructureData } = useSWR(`${getAPIUrl()}courses/${courseuuid}/meta`, swrFetcher);
+ const [courseStructure, dispatchCourseStructure] = useReducer(courseReducer,
+ {
+ courseStructure: courseStructureData ? courseStructureData : {},
+ courseOrder: {},
+ isSaved: true
+ }
+ );
+
+
+ // When courseStructureData is loaded, update the state
+ useEffect(() => {
+ if (courseStructureData) {
+ dispatchCourseStructure({ type: 'setCourseStructure', payload: courseStructureData });
+ }
+ }, [courseStructureData]);
+
+
+ if (!courseStructureData) return Loading...
+
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+export function useCourse() {
+ return useContext(CourseContext);
+}
+
+export function useCourseDispatch() {
+ return useContext(CourseDispatchContext);
+}
+
+function courseReducer(state: any, action: any) {
+ switch (action.type) {
+ case 'setCourseStructure':
+ return { ...state, courseStructure: action.payload };
+ case 'setCourseOrder':
+ return { ...state, courseOrder: action.payload };
+ case 'setIsSaved':
+ return { ...state, isSaved: true };
+ case 'setIsNotSaved':
+ return { ...state, isSaved: false };
+ default:
+ throw new Error(`Unhandled action type: ${action.type}`);
+ }
+}
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/EditCourseStructure/Buttons/NewActivityButton.tsx b/apps/web/components/Dashboard/EditCourseStructure/Buttons/NewActivityButton.tsx
new file mode 100644
index 00000000..74974cc5
--- /dev/null
+++ b/apps/web/components/Dashboard/EditCourseStructure/Buttons/NewActivityButton.tsx
@@ -0,0 +1,93 @@
+import { useCourse } from '@components/Dashboard/CourseContext';
+import NewActivityModal from '@components/Objects/Modals/Activities/Create/NewActivity';
+import Modal from '@components/StyledElements/Modal/Modal';
+import { getAPIUrl } from '@services/config/config';
+import { createActivity, createExternalVideoActivity, createFileActivity } from '@services/courses/activities';
+import { getOrganizationContextInfoWithoutCredentials } from '@services/organizations/orgs';
+import { revalidateTags } from '@services/utils/ts/requests';
+import { Sparkles } from 'lucide-react'
+import { useRouter } from 'next/navigation';
+import React, { use, useEffect } from 'react'
+import { mutate } from 'swr';
+
+type NewActivityButtonProps = {
+ chapterId: string,
+ orgslug: string
+}
+
+function NewActivityButton(props: NewActivityButtonProps) {
+ const [newActivityModal, setNewActivityModal] = React.useState(false);
+ const router = useRouter();
+ const course = useCourse() as any;
+
+ const openNewActivityModal = async (chapterId: any) => {
+ setNewActivityModal(true);
+ };
+
+ const closeNewActivityModal = async () => {
+ setNewActivityModal(false);
+ };
+
+ // Submit new activity
+ const submitActivity = async (activity: any) => {
+ let org = await getOrganizationContextInfoWithoutCredentials(props.orgslug, { revalidate: 1800 });
+ await createActivity(activity, props.chapterId, org.org_id);
+ mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
+ setNewActivityModal(false);
+ await revalidateTags(['courses'], props.orgslug);
+ router.refresh();
+ };
+
+
+
+ // Submit File Upload
+ const submitFileActivity = async (file: any, type: any, activity: any, chapterId: string) => {
+ await createFileActivity(file, type, activity, chapterId);
+ mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
+ setNewActivityModal(false);
+ await revalidateTags(['courses'], props.orgslug);
+ router.refresh();
+ };
+
+ // Submit YouTube Video Upload
+ const submitExternalVideo = async (external_video_data: any, activity: any, chapterId: string) => {
+ await createExternalVideoActivity(external_video_data, activity, props.chapterId);
+ mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
+ setNewActivityModal(false);
+ await revalidateTags(['courses'], props.orgslug);
+ router.refresh();
+ };
+
+ useEffect(() => { }
+ , [course])
+
+ return (
+
+
}
+ dialogTitle="Create Activity"
+ dialogDescription="Choose between types of activities to add to the course"
+
+ />
+ {
+ openNewActivityModal(props.chapterId)
+ }} className="flex space-x-2 items-center py-2 my-3 rounded-md justify-center text-white bg-black hover:cursor-pointer">
+
+
Add Activity +
+
+
+ )
+}
+
+export default NewActivityButton
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ActivityElement.tsx b/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ActivityElement.tsx
new file mode 100644
index 00000000..a31fe3ac
--- /dev/null
+++ b/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ActivityElement.tsx
@@ -0,0 +1,96 @@
+import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import { getAPIUrl, getUriWithOrg } from '@services/config/config'
+import { deleteActivity } from '@services/courses/activities'
+import { revalidateTags } from '@services/utils/ts/requests'
+import { Eye, File, MoreVertical, Pencil, Save, Sparkles, Video, X } from 'lucide-react'
+import Link from 'next/link'
+import { useRouter } from 'next/navigation'
+import React from 'react'
+import { Draggable } from 'react-beautiful-dnd'
+import { mutate } from 'swr'
+
+type ActivitiyElementProps = {
+ orgslug: string,
+ activity: any,
+ activityIndex: any,
+ course_uuid: string
+}
+
+function ActivityElement(props: ActivitiyElementProps) {
+ const router = useRouter();
+
+ async function deleteActivityUI() {
+ await deleteActivity(props.activity.id);
+ mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`);
+ await revalidateTags(['courses'], props.orgslug);
+ router.refresh();
+ }
+
+ return (
+
+ {(provided, snapshot) => (
+
+
+ {/* Activity Type Icon */}
+
+ {props.activity.activity_type === "video" &&
+ <>
+
+ >}
+
+
+
+ {/* Centered Activity Name */}
+
+ {(
{props.activity.name}
)}
+
+
+ {/* Edit and View Button */}
+
+ {props.activity.activity_type === "TYPE_DYNAMIC" && <>
+
+
Edit
+
+ >}
+
+
+
+
+ {/* Delete Button */}
+
+
+
+
+
}
+ functionToExecute={() => deleteActivityUI()}
+ status='warning'
+ >
+
+ )}
+
+ )
+}
+
+export default ActivityElement
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ChapterElement.tsx b/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ChapterElement.tsx
new file mode 100644
index 00000000..94ad67ad
--- /dev/null
+++ b/apps/web/components/Dashboard/EditCourseStructure/DraggableElements/ChapterElement.tsx
@@ -0,0 +1,104 @@
+import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal';
+import { Activity, Hexagon, MoreHorizontal, MoreVertical, Pencil, Save, Sparkles, X } from 'lucide-react';
+import React from 'react'
+import ActivitiyElement from './ActivityElement';
+import { Draggable, Droppable } from 'react-beautiful-dnd';
+import ActivityElement from './ActivityElement';
+import NewActivity from '../Buttons/NewActivityButton';
+import NewActivityButton from '../Buttons/NewActivityButton';
+import { deleteChapter } from '@services/courses/chapters';
+import { revalidateTags } from '@services/utils/ts/requests';
+import { useRouter } from 'next/navigation';
+import { getAPIUrl } from '@services/config/config';
+import { mutate } from 'swr';
+
+type ChapterElementProps = {
+ chapter: any,
+ chapterIndex: number,
+ orgslug: string
+ course_uuid: string
+}
+
+function ChapterElement(props: ChapterElementProps) {
+ const activities = props.chapter.activities || [];
+ const router = useRouter();
+
+ const deleteChapterUI = async () => {
+ await deleteChapter(props.chapter.id);
+ mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`);
+ await revalidateTags(['courses'], props.orgslug);
+ router.refresh();
+ };
+
+ return (
+
+ {(provided, snapshot) => (
+
+
+
+
+
+
+
+
{props.chapter.name}
+
+
+
+
+
+
+ Delete Chapter
+ }
+ functionToExecute={() => deleteChapterUI()}
+ status='warning'
+ >
+
+
+ {(provided) => (
+
+
+ {activities.map((activity: any, index: any) => {
+ return (
+
+ )
+ })}
+ {provided.placeholder}
+
+
+
+ )}
+
+
+
+
+ )}
+
+ )
+}
+
+export default ChapterElement
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/EditCourseStructure/EditCourseStructure.tsx b/apps/web/components/Dashboard/EditCourseStructure/EditCourseStructure.tsx
new file mode 100644
index 00000000..658acb3b
--- /dev/null
+++ b/apps/web/components/Dashboard/EditCourseStructure/EditCourseStructure.tsx
@@ -0,0 +1,111 @@
+'use client';
+import { getAPIUrl } from '@services/config/config';
+import { revalidateTags, swrFetcher } from '@services/utils/ts/requests';
+import React, { useContext, useEffect, useState } from 'react'
+import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
+import useSWR, { mutate } from 'swr';
+import ChapterElement from './DraggableElements/ChapterElement';
+import PageLoading from '@components/Objects/Loaders/PageLoading';
+import { updateCourseOrderStructure } from '@services/courses/chapters';
+import { useRouter } from 'next/navigation';
+import { CourseStructureContext } from 'app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page';
+import { useCourse, useCourseDispatch } from '@components/Dashboard/CourseContext';
+
+type EditCourseStructureProps = {
+ orgslug: string,
+ course_uuid?: string,
+}
+
+export type OrderPayload = {
+ chapter_order_by_ids: [
+ {
+ chapter_id: string,
+ activities_order_by_ids: [
+ {
+ activity_id: string
+ }
+ ]
+ }
+ ],
+} | undefined
+
+const EditCourseStructure = (props: EditCourseStructureProps) => {
+ const router = useRouter();
+ // Check window availability
+ const [winReady, setwinReady] = useState(false);
+
+ const dispatchCourse = useCourseDispatch() as any;
+
+ const [order, setOrder] = useState();
+ const course = useCourse() as any;
+ const course_structure = course ? course.courseStructure : {};
+ const course_uuid = course ? course.courseStructure.course_uuid : '';
+
+
+
+ const updateStructure = (result: any) => {
+ const { destination, source, draggableId, type } = result;
+ if (!destination) return;
+ if (destination.droppableId === source.droppableId && destination.index === source.index) return;
+ if (type === 'chapter') {
+ const newChapterOrder = Array.from(course_structure.chapters);
+ newChapterOrder.splice(source.index, 1);
+ newChapterOrder.splice(destination.index, 0, course_structure.chapters[source.index]);
+ dispatchCourse({ type: 'setCourseStructure', payload: { ...course_structure, chapters: newChapterOrder } })
+ dispatchCourse({ type: 'setIsNotSaved' })
+ }
+ if (type === 'activity') {
+ const newChapterOrder = Array.from(course_structure.chapters);
+ const sourceChapter = newChapterOrder.find((chapter: any) => chapter.chapter_uuid === source.droppableId) as any;
+ const destinationChapter = newChapterOrder.find((chapter: any) => chapter.chapter_uuid === destination.droppableId) ? newChapterOrder.find((chapter: any) => chapter.chapter_uuid === destination.droppableId) : sourceChapter;
+ const activity = sourceChapter.activities.find((activity: any) => activity.activity_uuid === draggableId);
+ sourceChapter.activities.splice(source.index, 1);
+ destinationChapter.activities.splice(destination.index, 0, activity);
+ dispatchCourse({ type: 'setCourseStructure', payload: { ...course_structure, chapters: newChapterOrder } })
+ dispatchCourse({ type: 'setIsNotSaved' })
+ }
+ }
+
+ useEffect(() => {
+ setwinReady(true);
+
+ }, [props.course_uuid, course_structure, course]);
+
+
+ if (!course) return
+
+ return (
+
+ {winReady ?
+
+
+ {(provided) => (
+
+ {course_structure.chapters && course_structure.chapters.map((chapter: any, index: any) => {
+ return (
+
+
+ )
+ })}
+ {provided.placeholder}
+
+ )}
+
+
+
+ : <>>}
+
+
+
+ )
+}
+
+export default EditCourseStructure
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/UI/BreadCrumbs.tsx b/apps/web/components/Dashboard/UI/BreadCrumbs.tsx
new file mode 100644
index 00000000..2df5b8f9
--- /dev/null
+++ b/apps/web/components/Dashboard/UI/BreadCrumbs.tsx
@@ -0,0 +1,30 @@
+import { useCourse } from '@components/Dashboard/CourseContext'
+import { Book, ChevronRight, User } from 'lucide-react'
+import Link from 'next/link'
+import React, { use, useEffect } from 'react'
+
+type BreadCrumbsProps = {
+ type: 'courses' | 'users'
+ last_breadcrumb?: string
+}
+
+function BreadCrumbs(props: BreadCrumbsProps) {
+ const course = useCourse() as any;
+
+ return (
+
+
+
+
+ {props.type == 'courses' ?
Courses
: ''}
+ {props.type == 'users' ?
Users
: ''}
+
+ {props.last_breadcrumb ?
: ''}
+
{props.last_breadcrumb}
+
+
+
+ )
+}
+
+export default BreadCrumbs
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/UI/CourseOverviewTop.tsx b/apps/web/components/Dashboard/UI/CourseOverviewTop.tsx
new file mode 100644
index 00000000..58cb5579
--- /dev/null
+++ b/apps/web/components/Dashboard/UI/CourseOverviewTop.tsx
@@ -0,0 +1,31 @@
+import { useCourse } from "@components/Dashboard/CourseContext";
+import { useEffect } from "react";
+import BreadCrumbs from "./BreadCrumbs";
+import SaveState from "./SaveState";
+import { CourseOverviewParams } from "app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page";
+
+export function CourseOverviewTop({ params }: { params: CourseOverviewParams }) {
+ const course = useCourse() as any;
+
+ useEffect(() => { }
+ , [course])
+
+ return (
+ <>
+
+
+
+
+
+
Course
+
{course.courseStructure.name}
+
+
+
+
+
+
+ >
+ )
+
+}
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/UI/LeftMenu.tsx b/apps/web/components/Dashboard/UI/LeftMenu.tsx
new file mode 100644
index 00000000..a2b501a7
--- /dev/null
+++ b/apps/web/components/Dashboard/UI/LeftMenu.tsx
@@ -0,0 +1,22 @@
+
+import { Book } from 'lucide-react'
+import Link from 'next/link'
+import React from 'react'
+
+function LeftMenu() {
+ return (
+
+ )
+}
+
+export default LeftMenu
+
diff --git a/apps/web/components/Dashboard/UI/SaveState.tsx b/apps/web/components/Dashboard/UI/SaveState.tsx
new file mode 100644
index 00000000..603b08e2
--- /dev/null
+++ b/apps/web/components/Dashboard/UI/SaveState.tsx
@@ -0,0 +1,100 @@
+'use client';
+import { getAPIUrl } from '@services/config/config';
+import { updateCourseOrderStructure } from '@services/courses/chapters';
+import { revalidateTags } from '@services/utils/ts/requests';
+import { useCourse, useCourseDispatch } from '@components/Dashboard/CourseContext'
+import { Check, SaveAllIcon, Timer } from 'lucide-react'
+import { useRouter } from 'next/navigation';
+import React, { useEffect } from 'react'
+import { mutate } from 'swr';
+
+function SaveState(props: { orgslug: string }) {
+ const course = useCourse() as any;
+ const router = useRouter();
+ const saved = course ? course.isSaved : true;
+ const dispatchCourse = useCourseDispatch() as any;
+ const course_structure = course.courseStructure;
+
+ const saveCourseState = async () => {
+ // Course structure & order
+ if (saved) return;
+ await changeOrderBackend();
+ mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
+ dispatchCourse({ type: 'setIsSaved' })
+ }
+
+
+ //
+ // Course Order
+ const changeOrderBackend = async () => {
+ mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`);
+ await updateCourseOrderStructure(course.courseStructure.course_uuid, course.courseOrder);
+ await revalidateTags(['courses'], props.orgslug)
+ router.refresh();
+ dispatchCourse({ type: 'setIsSaved' })
+ }
+
+
+
+ const handleCourseOrder = (course_structure: any) => {
+ const chapters = course_structure.chapters;
+ const chapter_order_by_ids = chapters.map((chapter: any) => {
+ return {
+ chapter_id: chapter.id,
+ activities_order_by_ids: chapter.activities.map((activity: any) => {
+ return {
+ activity_id: activity.id
+ }
+ })
+ }
+ })
+ dispatchCourse({ type: 'setCourseOrder', payload: { chapter_order_by_ids: chapter_order_by_ids } })
+ dispatchCourse({ type: 'setIsNotSaved' })
+ }
+
+ const initOrderPayload = () => {
+ if (course_structure && course_structure.chapters) {
+ handleCourseOrder(course_structure);
+ dispatchCourse({ type: 'setIsSaved' })
+
+ }
+ }
+
+ const changeOrderPayload = () => {
+ if (course_structure && course_structure.chapters) {
+ handleCourseOrder(course_structure);
+ dispatchCourse({ type: 'setIsNotSaved' })
+
+ }
+ }
+
+ useEffect(() => {
+ if (course_structure?.chapters) {
+ initOrderPayload();
+ }
+ if (course_structure?.chapters && !saved) {
+ changeOrderPayload();
+ }
+ }, [course_structure]); // This effect depends on the `course_structure` variable
+
+ return (
+
+ {saved ? <>> :
+
+
+ Unsaved changes
+
+
+
}
+
+
+ {saved ?
:
}
+ {saved ?
Saved
:
Save
}
+
+
+
+ )
+}
+
+export default SaveState
\ No newline at end of file
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index e1035183..efc12988 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -1,21 +1,6 @@
-// This file sets a custom webpack configuration to use your Next.js app
-// with Sentry.
-// https://nextjs.org/docs/api-reference/next.config.js/introduction
-// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
-const { withSentryConfig } = require('@sentry/nextjs');
-
/** @type {import('next').NextConfig} */
const nextConfig = {
- reactStrictMode: false,
- compiler: {
- styledComponents: true,
- },
-};
+ reactStrictMode: false,
+}
-module.exports = nextConfig;
-
-module.exports = withSentryConfig(
- module.exports,
- { silent: true },
- { hideSourcemaps: true },
-);
+module.exports = nextConfig
diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json
index 9168a0d6..89c843f0 100644
--- a/apps/web/package-lock.json
+++ b/apps/web/package-lock.json
@@ -28,7 +28,7 @@
"framer-motion": "^10.16.1",
"lowlight": "^3.0.0",
"lucide-react": "^0.268.0",
- "next": "^14.0.3",
+ "next": "^14.0.4",
"re-resizable": "^6.9.9",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
@@ -2266,9 +2266,9 @@
}
},
"node_modules/@next/env": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.3.tgz",
- "integrity": "sha512-7xRqh9nMvP5xrW4/+L0jgRRX+HoNRGnfJpD+5Wq6/13j3dsdzxO3BCXn7D3hMqsDb+vjZnJq+vI7+EtgrYZTeA=="
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz",
+ "integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ=="
},
"node_modules/@next/eslint-plugin-next": {
"version": "13.5.4",
@@ -2300,9 +2300,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.3.tgz",
- "integrity": "sha512-64JbSvi3nbbcEtyitNn2LEDS/hcleAFpHdykpcnrstITFlzFgB/bW0ER5/SJJwUPj+ZPY+z3e+1jAfcczRLVGw==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz",
+ "integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==",
"cpu": [
"arm64"
],
@@ -2315,9 +2315,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.3.tgz",
- "integrity": "sha512-RkTf+KbAD0SgYdVn1XzqE/+sIxYGB7NLMZRn9I4Z24afrhUpVJx6L8hsRnIwxz3ERE2NFURNliPjJ2QNfnWicQ==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz",
+ "integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==",
"cpu": [
"x64"
],
@@ -2330,9 +2330,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.3.tgz",
- "integrity": "sha512-3tBWGgz7M9RKLO6sPWC6c4pAw4geujSwQ7q7Si4d6bo0l6cLs4tmO+lnSwFp1Tm3lxwfMk0SgkJT7EdwYSJvcg==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz",
+ "integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==",
"cpu": [
"arm64"
],
@@ -2345,9 +2345,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.3.tgz",
- "integrity": "sha512-v0v8Kb8j8T23jvVUWZeA2D8+izWspeyeDGNaT2/mTHWp7+37fiNfL8bmBWiOmeumXkacM/AB0XOUQvEbncSnHA==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz",
+ "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==",
"cpu": [
"arm64"
],
@@ -2360,9 +2360,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.3.tgz",
- "integrity": "sha512-VM1aE1tJKLBwMGtyBR21yy+STfl0MapMQnNrXkxeyLs0GFv/kZqXS5Jw/TQ3TSUnbv0QPDf/X8sDXuMtSgG6eg==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz",
+ "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==",
"cpu": [
"x64"
],
@@ -2375,9 +2375,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.3.tgz",
- "integrity": "sha512-64EnmKy18MYFL5CzLaSuUn561hbO1Gk16jM/KHznYP3iCIfF9e3yULtHaMy0D8zbHfxset9LTOv6cuYKJgcOxg==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz",
+ "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==",
"cpu": [
"x64"
],
@@ -2390,9 +2390,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.3.tgz",
- "integrity": "sha512-WRDp8QrmsL1bbGtsh5GqQ/KWulmrnMBgbnb+59qNTW1kVi1nG/2ndZLkcbs2GX7NpFLlToLRMWSQXmPzQm4tog==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz",
+ "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==",
"cpu": [
"arm64"
],
@@ -2405,9 +2405,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.3.tgz",
- "integrity": "sha512-EKffQeqCrj+t6qFFhIFTRoqb2QwX1mU7iTOvMyLbYw3QtqTw9sMwjykyiMlZlrfm2a4fA84+/aeW+PMg1MjuTg==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz",
+ "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==",
"cpu": [
"ia32"
],
@@ -2420,9 +2420,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.3.tgz",
- "integrity": "sha512-ERhKPSJ1vQrPiwrs15Pjz/rvDHZmkmvbf/BjPN/UCOI++ODftT0GtasDPi0j+y6PPJi5HsXw+dpRaXUaw4vjuQ==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz",
+ "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==",
"cpu": [
"x64"
],
@@ -7117,14 +7117,15 @@
"dev": true
},
"node_modules/next": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/next/-/next-14.0.3.tgz",
- "integrity": "sha512-AbYdRNfImBr3XGtvnwOxq8ekVCwbFTv/UJoLwmaX89nk9i051AEY4/HAWzU0YpaTDw8IofUpmuIlvzWF13jxIw==",
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz",
+ "integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==",
"dependencies": {
- "@next/env": "14.0.3",
+ "@next/env": "14.0.4",
"@swc/helpers": "0.5.2",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001406",
+ "graceful-fs": "^4.2.11",
"postcss": "8.4.31",
"styled-jsx": "5.1.1",
"watchpack": "2.4.0"
@@ -7136,15 +7137,15 @@
"node": ">=18.17.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "14.0.3",
- "@next/swc-darwin-x64": "14.0.3",
- "@next/swc-linux-arm64-gnu": "14.0.3",
- "@next/swc-linux-arm64-musl": "14.0.3",
- "@next/swc-linux-x64-gnu": "14.0.3",
- "@next/swc-linux-x64-musl": "14.0.3",
- "@next/swc-win32-arm64-msvc": "14.0.3",
- "@next/swc-win32-ia32-msvc": "14.0.3",
- "@next/swc-win32-x64-msvc": "14.0.3"
+ "@next/swc-darwin-arm64": "14.0.4",
+ "@next/swc-darwin-x64": "14.0.4",
+ "@next/swc-linux-arm64-gnu": "14.0.4",
+ "@next/swc-linux-arm64-musl": "14.0.4",
+ "@next/swc-linux-x64-gnu": "14.0.4",
+ "@next/swc-linux-x64-musl": "14.0.4",
+ "@next/swc-win32-arm64-msvc": "14.0.4",
+ "@next/swc-win32-ia32-msvc": "14.0.4",
+ "@next/swc-win32-x64-msvc": "14.0.4"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
diff --git a/apps/web/package.json b/apps/web/package.json
index 4844a0fe..5e2668af 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -29,7 +29,7 @@
"framer-motion": "^10.16.1",
"lowlight": "^3.0.0",
"lucide-react": "^0.268.0",
- "next": "^14.0.3",
+ "next": "14.0.4",
"re-resizable": "^6.9.9",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
diff --git a/apps/web/services/courses/activities.ts b/apps/web/services/courses/activities.ts
index 573ad7f3..bb07488c 100644
--- a/apps/web/services/courses/activities.ts
+++ b/apps/web/services/courses/activities.ts
@@ -40,7 +40,7 @@ export async function createExternalVideoActivity(data: any, activity: any, chap
data.chapter_id = chapter_id;
data.activity_id = activity.id;
- const result = await fetch(`${getAPIUrl()}activities/external_video?coursechapter_id=${chapter_id}`, RequestBody("POST", data, null));
+ const result = await fetch(`${getAPIUrl()}activities/external_video`, RequestBody("POST", data, null));
const res = await result.json();
return res;
}
diff --git a/apps/web/services/courses/chapters.ts b/apps/web/services/courses/chapters.ts
index 71e70678..26c54c81 100644
--- a/apps/web/services/courses/chapters.ts
+++ b/apps/web/services/courses/chapters.ts
@@ -1,3 +1,4 @@
+import { OrderPayload } from "@components/Dashboard/EditCourseStructure/EditCourseStructure";
import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyWithAuthHeader, errorHandling } from "@services/utils/ts/requests";
@@ -25,6 +26,12 @@ export async function updateChapter(coursechapter_id: any, data: any) {
return res;
}
+export async function updateCourseOrderStructure(course_uuid: any, data: OrderPayload) {
+ const result: any = await fetch(`${getAPIUrl()}chapters/course/${course_uuid}/order`, RequestBody("PUT", data, null));
+ const res = await errorHandling(result);
+ return res;
+}
+
export async function createChapter(data: any) {
const result: any = await fetch(`${getAPIUrl()}chapters/`, RequestBody("POST", data, null));
const res = await errorHandling(result);
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index 0bc0b60d..6546f796 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -5,11 +5,10 @@
"allowJs": true,
"skipLibCheck": true,
"strict": true,
- "forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
- "moduleResolution": "node",
+ "moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
@@ -29,7 +28,7 @@
"@editor/*": ["components/Objects/Editor/*"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx","**/**/*.tsx", ".next/types/**/*.ts"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
-}
\ No newline at end of file
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 41a46e2f..d1668b45 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -40,7 +40,7 @@ importers:
version: 1.0.7(@types/react-dom@18.0.6)(@types/react@18.2.8)(react-dom@18.2.0)(react@18.2.0)
'@sentry/nextjs':
specifier: ^7.47.0
- version: 7.74.0(next@13.5.4)(react@18.2.0)
+ version: 7.74.0(next@14.0.4)(react@18.2.0)
'@stitches/react':
specifier: ^1.2.8
version: 1.2.8(react@18.2.0)
@@ -81,8 +81,8 @@ importers:
specifier: ^0.268.0
version: 0.268.0(react@18.2.0)
next:
- specifier: ^13.5.4
- version: 13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)
+ specifier: 14.0.4
+ version: 14.0.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)
re-resizable:
specifier: ^6.9.9
version: 6.9.11(react-dom@18.2.0)(react@18.2.0)
@@ -1647,8 +1647,8 @@ packages:
'@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.4.15
- /@next/env@13.5.4:
- resolution: {integrity: sha512-LGegJkMvRNw90WWphGJ3RMHMVplYcOfRWf2Be3td3sUa+1AaxmsYyANsA+znrGCBjXJNi4XAQlSoEfUxs/4kIQ==}
+ /@next/env@14.0.4:
+ resolution: {integrity: sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==}
dev: false
/@next/eslint-plugin-next@13.5.4:
@@ -1657,8 +1657,8 @@ packages:
glob: 7.1.7
dev: true
- /@next/swc-darwin-arm64@13.5.4:
- resolution: {integrity: sha512-Df8SHuXgF1p+aonBMcDPEsaahNo2TCwuie7VXED4FVyECvdXfRT9unapm54NssV9tF3OQFKBFOdlje4T43VO0w==}
+ /@next/swc-darwin-arm64@14.0.4:
+ resolution: {integrity: sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -1666,8 +1666,8 @@ packages:
dev: false
optional: true
- /@next/swc-darwin-x64@13.5.4:
- resolution: {integrity: sha512-siPuUwO45PnNRMeZnSa8n/Lye5ZX93IJom9wQRB5DEOdFrw0JjOMu1GINB8jAEdwa7Vdyn1oJ2xGNaQpdQQ9Pw==}
+ /@next/swc-darwin-x64@14.0.4:
+ resolution: {integrity: sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -1675,8 +1675,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-gnu@13.5.4:
- resolution: {integrity: sha512-l/k/fvRP/zmB2jkFMfefmFkyZbDkYW0mRM/LB+tH5u9pB98WsHXC0WvDHlGCYp3CH/jlkJPL7gN8nkTQVrQ/2w==}
+ /@next/swc-linux-arm64-gnu@14.0.4:
+ resolution: {integrity: sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -1684,8 +1684,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-musl@13.5.4:
- resolution: {integrity: sha512-YYGb7SlLkI+XqfQa8VPErljb7k9nUnhhRrVaOdfJNCaQnHBcvbT7cx/UjDQLdleJcfyg1Hkn5YSSIeVfjgmkTg==}
+ /@next/swc-linux-arm64-musl@14.0.4:
+ resolution: {integrity: sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -1693,8 +1693,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-x64-gnu@13.5.4:
- resolution: {integrity: sha512-uE61vyUSClnCH18YHjA8tE1prr/PBFlBFhxBZis4XBRJoR+txAky5d7gGNUIbQ8sZZ7LVkSVgm/5Fc7mwXmRAg==}
+ /@next/swc-linux-x64-gnu@14.0.4:
+ resolution: {integrity: sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -1702,8 +1702,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-x64-musl@13.5.4:
- resolution: {integrity: sha512-qVEKFYML/GvJSy9CfYqAdUexA6M5AklYcQCW+8JECmkQHGoPxCf04iMh7CPR7wkHyWWK+XLt4Ja7hhsPJtSnhg==}
+ /@next/swc-linux-x64-musl@14.0.4:
+ resolution: {integrity: sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -1711,8 +1711,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-arm64-msvc@13.5.4:
- resolution: {integrity: sha512-mDSQfqxAlfpeZOLPxLymZkX0hYF3juN57W6vFHTvwKlnHfmh12Pt7hPIRLYIShk8uYRsKPtMTth/EzpwRI+u8w==}
+ /@next/swc-win32-arm64-msvc@14.0.4:
+ resolution: {integrity: sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -1720,8 +1720,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-ia32-msvc@13.5.4:
- resolution: {integrity: sha512-aoqAT2XIekIWoriwzOmGFAvTtVY5O7JjV21giozBTP5c6uZhpvTWRbmHXbmsjZqY4HnEZQRXWkSAppsIBweKqw==}
+ /@next/swc-win32-ia32-msvc@14.0.4:
+ resolution: {integrity: sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@@ -1729,8 +1729,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-x64-msvc@13.5.4:
- resolution: {integrity: sha512-cyRvlAxwlddlqeB9xtPSfNSCRy8BOa4wtMo0IuI9P7Y0XT2qpDrpFKRyZ7kUngZis59mPVla5k8X1oOJ8RxDYg==}
+ /@next/swc-win32-x64-msvc@14.0.4:
+ resolution: {integrity: sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -2442,7 +2442,7 @@ packages:
tslib: 2.6.2
dev: false
- /@sentry/nextjs@7.74.0(next@13.5.4)(react@18.2.0):
+ /@sentry/nextjs@7.74.0(next@14.0.4)(react@18.2.0):
resolution: {integrity: sha512-ZxhlBKRV8To3NgNblJ+8RL1urDIzsdV9+8k9GZqNG5BFxLN2tJvbgHaxI7iV4E4qYpJAae379dWpvCzGWUXWkw==}
engines: {node: '>=8'}
peerDependencies:
@@ -2463,7 +2463,7 @@ packages:
'@sentry/vercel-edge': 7.74.0
'@sentry/webpack-plugin': 1.20.0
chalk: 3.0.0
- next: 13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)
+ next: 14.0.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
rollup: 2.78.0
stacktrace-parser: 0.1.10
@@ -4901,9 +4901,9 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
- /next@13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-+93un5S779gho8y9ASQhb/bTkQF17FNQOtXLKAj3lsNgltEcF0C5PMLLncDmH+8X1EnJH1kbqAERa29nRXqhjA==}
- engines: {node: '>=16.14.0'}
+ /next@14.0.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==}
+ engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
@@ -4916,25 +4916,26 @@ packages:
sass:
optional: true
dependencies:
- '@next/env': 13.5.4
+ '@next/env': 14.0.4
'@swc/helpers': 0.5.2
busboy: 1.6.0
caniuse-lite: 1.0.30001547
+ graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(@babel/core@7.23.2)(react@18.2.0)
watchpack: 2.4.0
optionalDependencies:
- '@next/swc-darwin-arm64': 13.5.4
- '@next/swc-darwin-x64': 13.5.4
- '@next/swc-linux-arm64-gnu': 13.5.4
- '@next/swc-linux-arm64-musl': 13.5.4
- '@next/swc-linux-x64-gnu': 13.5.4
- '@next/swc-linux-x64-musl': 13.5.4
- '@next/swc-win32-arm64-msvc': 13.5.4
- '@next/swc-win32-ia32-msvc': 13.5.4
- '@next/swc-win32-x64-msvc': 13.5.4
+ '@next/swc-darwin-arm64': 14.0.4
+ '@next/swc-darwin-x64': 14.0.4
+ '@next/swc-linux-arm64-gnu': 14.0.4
+ '@next/swc-linux-arm64-musl': 14.0.4
+ '@next/swc-linux-x64-gnu': 14.0.4
+ '@next/swc-linux-x64-musl': 14.0.4
+ '@next/swc-win32-arm64-msvc': 14.0.4
+ '@next/swc-win32-ia32-msvc': 14.0.4
+ '@next/swc-win32-x64-msvc': 14.0.4
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros