feat: unpublished activities are now hidden by default

This commit is contained in:
swve 2025-04-17 15:57:57 +02:00
parent e6d7e881e3
commit 46e06201fb
14 changed files with 83 additions and 42 deletions

View file

@ -8,11 +8,11 @@ import { useLHSession } from '@components/Contexts/LHSessionContext'
export const CourseContext = createContext(null)
export const CourseDispatchContext = createContext(null)
export function CourseProvider({ children, courseuuid }: any) {
export function CourseProvider({ children, courseuuid, withUnpublishedActivities = false }: any) {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const { data: courseStructureData, error } = useSWR(`${getAPIUrl()}courses/${courseuuid}/meta`,
const { data: courseStructureData, error } = useSWR(`${getAPIUrl()}courses/${courseuuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`,
url => swrFetcher(url, access_token)
);
@ -22,7 +22,8 @@ export function CourseProvider({ children, courseuuid }: any) {
},
courseOrder: {},
isSaved: true,
isLoading: true
isLoading: true,
withUnpublishedActivities: withUnpublishedActivities
};
const [state, dispatch] = useReducer(courseReducer, initialState) as any;

View file

@ -6,37 +6,43 @@ import {
useCourse,
useCourseDispatch,
} from '@components/Contexts/CourseContext'
import { Check, SaveAllIcon, Timer } from 'lucide-react'
import { Check, SaveAllIcon, Timer, Loader2 } from 'lucide-react'
import { useRouter } from 'next/navigation'
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { mutate } from 'swr'
import { updateCourse } from '@services/courses/courses'
import { useLHSession } from '@components/Contexts/LHSessionContext'
function SaveState(props: { orgslug: string }) {
const [isLoading, setIsLoading] = useState(false)
const course = useCourse() as any
const session = useLHSession() as any;
const router = useRouter()
const saved = course ? course.isSaved : true
const dispatchCourse = useCourseDispatch() as any
const course_structure = course.courseStructure
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
const saveCourseState = async () => {
// Course order
if (saved) return
await changeOrderBackend()
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
// Course metadata
await changeMetadataBackend()
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
await revalidateTags(['courses'], props.orgslug)
dispatchCourse({ type: 'setIsSaved' })
if (saved || isLoading) return
setIsLoading(true)
try {
// Course order
await changeOrderBackend()
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
// Course metadata
await changeMetadataBackend()
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
dispatchCourse({ type: 'setIsSaved' })
} finally {
setIsLoading(false)
}
}
//
// Course Order
const changeOrderBackend = async () => {
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await updateCourseOrderStructure(
course.courseStructure.course_uuid,
course.courseOrder,
@ -49,7 +55,7 @@ function SaveState(props: { orgslug: string }) {
// Course metadata
const changeMetadataBackend = async () => {
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await updateCourse(
course.courseStructure.course_uuid,
course.courseStructure,
@ -117,12 +123,25 @@ function SaveState(props: { orgslug: string }) {
`px-4 py-2 rounded-lg drop-shadow-md cursor-pointer flex space-x-2 items-center font-bold antialiased transition-all ease-linear ` +
(saved
? 'bg-gray-600 text-white'
: 'bg-black text-white border hover:bg-gray-900 ')
: 'bg-black text-white border hover:bg-gray-900 ') +
(isLoading ? 'opacity-50 cursor-not-allowed' : '')
}
onClick={saveCourseState}
>
{saved ? <Check size={20} /> : <SaveAllIcon size={20} />}
{saved ? <div className="">Saved</div> : <div className="">Save</div>}
{isLoading ? (
<Loader2 size={20} className="animate-spin" />
) : saved ? (
<Check size={20} />
) : (
<SaveAllIcon size={20} />
)}
{isLoading ? (
<div className="">Saving...</div>
) : saved ? (
<div className="">Saved</div>
) : (
<div className="">Save</div>
)}
</div>
</div>
)

View file

@ -17,6 +17,7 @@ function ThumbnailUpdate() {
const [isLoading, setIsLoading] = React.useState(false) as any
const [error, setError] = React.useState('') as any
const [showUnsplashPicker, setShowUnsplashPicker] = useState(false)
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
const handleFileChange = async (event: any) => {
const file = event.target.files[0]
@ -40,7 +41,7 @@ function ThumbnailUpdate() {
file,
session.data?.tokens?.access_token
)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
// wait for 1 second to show loading animation
await new Promise((r) => setTimeout(r, 1500))
if (res.success === false) {

View file

@ -27,6 +27,7 @@ function NewActivityButton(props: NewActivityButtonProps) {
const course = useCourse() as any
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
const openNewActivityModal = async (chapterId: any) => {
setNewActivityModal(true)
@ -44,7 +45,7 @@ function NewActivityButton(props: NewActivityButtonProps) {
)
const toast_loading = toast.loading('Creating activity...')
await createActivity(activity, props.chapterId, org.org_id, access_token)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
toast.dismiss(toast_loading)
toast.success('Activity created successfully')
setNewActivityModal(false)
@ -61,7 +62,7 @@ function NewActivityButton(props: NewActivityButtonProps) {
) => {
toast.loading('Uploading file and creating activity...')
await createFileActivity(file, type, activity, chapterId, access_token)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
setNewActivityModal(false)
toast.dismiss()
toast.success('File uploaded successfully')
@ -82,7 +83,7 @@ function NewActivityButton(props: NewActivityButtonProps) {
activity,
props.chapterId, access_token
)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
setNewActivityModal(false)
toast.dismiss(toast_loading)
toast.success('Activity created successfully')

View file

@ -56,6 +56,8 @@ function ActivityElement(props: ActivitiyElementProps) {
const [isUpdatingName, setIsUpdatingName] = React.useState<boolean>(false)
const activityUUID = props.activity.activity_uuid
const isMobile = useMediaQuery('(max-width: 767px)')
const course = useCourse() as any;
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
async function deleteActivityUI() {
const toast_loading = toast.loading('Deleting activity...')
@ -65,7 +67,7 @@ function ActivityElement(props: ActivitiyElementProps) {
}
await deleteActivity(props.activity.activity_uuid, access_token)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
toast.dismiss(toast_loading)
toast.success('Activity deleted successfully')
@ -82,7 +84,7 @@ function ActivityElement(props: ActivitiyElementProps) {
props.activity.activity_uuid,
access_token
)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
toast.dismiss(toast_loading)
toast.success('The activity has been updated successfully')
await revalidateTags(['courses'], props.orgslug)
@ -103,7 +105,7 @@ function ActivityElement(props: ActivitiyElementProps) {
try {
await updateActivity(modifiedActivityCopy, activityUUID, access_token)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
toast.success('Activity name updated successfully')
router.refresh()

View file

@ -18,6 +18,7 @@ import { useRouter } from 'next/navigation'
import { getAPIUrl } from '@services/config/config'
import { mutate } from 'swr'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useCourse } from '@components/Contexts/CourseContext'
type ChapterElementProps = {
chapter: any
@ -41,12 +42,14 @@ function ChapterElement(props: ChapterElementProps) {
const [selectedChapter, setSelectedChapter] = React.useState<
string | undefined
>(undefined)
const course = useCourse() as any;
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
const router = useRouter()
const deleteChapterUI = async () => {
await deleteChapter(props.chapter.id, access_token)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
router.refresh()
}
@ -57,7 +60,7 @@ function ChapterElement(props: ChapterElementProps) {
name: modifiedChapter.chapterName,
}
await updateChapter(chapterId, modifiedChapterCopy, access_token)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${props.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
router.refresh()
}

View file

@ -50,7 +50,7 @@ const EditCourseStructure = (props: EditCourseStructureProps) => {
const course = useCourse() as any
const course_structure = course ? course.courseStructure : {}
const course_uuid = course ? course.courseStructure.course_uuid : ''
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
// New Chapter creation
const [newChapterModal, setNewChapterModal] = useState(false)
@ -61,7 +61,7 @@ const EditCourseStructure = (props: EditCourseStructureProps) => {
// Submit new chapter
const submitChapter = async (chapter: any) => {
await createChapter(chapter,access_token)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta`)
mutate(`${getAPIUrl()}courses/${course.courseStructure.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
router.refresh()
setNewChapterModal(false)

View file

@ -18,6 +18,7 @@ import { useRouter } from 'next/navigation'
import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { deleteActivity, updateActivity } from '@services/courses/activities'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useCourse } from '@components/Contexts/CourseContext'
interface ModifiedActivityInterface {
activityId: string
@ -33,10 +34,12 @@ function Activity(props: any) {
const [selectedActivity, setSelectedActivity] = React.useState<
string | undefined
>(undefined)
const course = useCourse() as any;
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
async function removeActivity() {
await deleteActivity(props.activity.id, session.data?.tokens?.access_token)
mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}`)
mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
router.refresh()
}
@ -52,7 +55,7 @@ function Activity(props: any) {
}
await updateActivity(modifiedActivityCopy, activityId, session.data?.tokens?.access_token)
await mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}`)
await mutate(`${getAPIUrl()}chapters/meta/course_${props.courseid}?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
router.refresh()
}

View file

@ -10,7 +10,7 @@ import { mutate } from 'swr'
import { getAPIUrl } from '@services/config/config'
import { revalidateTags } from '@services/utils/ts/requests'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useCourse } from '@components/Contexts/CourseContext'
interface ModifiedChapterInterface {
chapterId: string
chapterName: string
@ -25,6 +25,8 @@ function Chapter(props: any) {
const [selectedChapter, setSelectedChapter] = React.useState<
string | undefined
>(undefined)
const course = useCourse() as any;
const withUnpublishedActivities = course ? course.withUnpublishedActivities : false
async function updateChapterName(chapterId: string) {
if (modifiedChapter?.chapterId === chapterId) {
@ -32,7 +34,7 @@ function Chapter(props: any) {
name: modifiedChapter.chapterName,
}
await updateChapter(chapterId, modifiedChapterCopy, session.data?.tokens?.access_token)
await mutate(`${getAPIUrl()}chapters/course/${props.course_uuid}/meta`)
await mutate(`${getAPIUrl()}chapters/course/${props.course_uuid}/meta?with_unpublished_activities=${withUnpublishedActivities}`)
await revalidateTags(['courses'], props.orgslug)
router.refresh()
}