CLASSROOMIO-T-30:Developed the UI for the video upload
CLASSROOMIO-T-30: UI cleanup CLASSROOMIO-T-30: Improve UX Merge branch 'CLASSROOMIO-T-30' of https://gitlab.com/thenextgreat/classroomio into CLASSROOMIO-T-30 CLASSROOMIO-T-30:Made the input clear after upload and also chnged the icons CLASSROOMIO-T-30: Remove console log CLASSROOMIO-T-30: Better trim out white spaces in url CLASSROOMIO-T-30:Fixed the bug between the save and edit CLASSROOMIO-T-30:Trying to fix the bug between edit and save Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-30:Stil working on the reviews CLASSROOMIO-T-30:Still working on some of the reviews and updates CLASSROOMIO-T-30: Remove unnessecary values in store CLASSROOMIO-T-30: Change button to div in modal CLASSROOMIO-T-100: Select only courses belonging to current org and current user is a member CLASSROOMIO-T-100: Update condition for fetchCourses CLASSROOMIO-T-100: Select only courses belonging to current org and current user is a member Co-authored-by: Rotimi Best <[email protected]> Merge-request: CLASSROOMIO-MR-35 Merged-by: Best Admin <[email protected]> Remove netlify app from redirect rule Add redirect to layout's load Redirect to about domain CLASSROOMIO-T-97: UX again? 😇 CLASSROOMIO-T-97: Better course UX 😍😍 CLASSROOMIO-T-97: Better button style and fix route not found CLASSROOMIO-T-97: Course UI improvement 😁😁😁 CLASSROOMIO-T-30: Add plyr CLASSROOMIO-T-30:Merged main into branch Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-30:Wotking on the lms dashboard CLASSROOMIO-T-30:Made some changes based on the reviews Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-30:Made some refactoring to keep the code DRY CLASSROOMIO-T-30:Developed the UI for the video upload Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio Merge branch 'main' of https://gitlab.com/thenextgreat/classroomio CLASSROOMIO-T-27: Review Section for Instructors on landing page CLASSROOMIO-T-27: Remove unused variable Merge branch 'CLASSROOMIO-T-27' of https://gitlab.com/thenextgreat/classroomio into CLASSROOMIO-T-27 CLASSROOMIO-T-27: Complete reviews functionality CLASSROOMIO-T-27: Review Render Update CLASSROOMIO-T-27: - Resolved Feedbacks CLASSROOMIO-T-27: Fixed Ratings Data CLASSROOMIO-T-27: - Added Ratings Svg - Fixed avatar_url feedback CLASSROOMIO-T-27: Import Adjustment CLASSROOMIO-T-27: Code Review CLASSROOM-T-27: Added Add Review Functionality CLASSROOMIO-T-27: - Unresolved Review - Enhance Code Redability CLASSROOMIO-T-27: Completed the delete functionality CLASSROOMIO-T-27: Hide/Show functionality CLASSROOMIO-T-27: Double Quotation Update CLASSROOMIO-T-27: Testing toggle functinality CLASSROOMIO-T-27: Added the icons CLASSROOMIO-T-27: Completion of the hide/show functionality CLASSROOMIO-T-27: Display the store data on the review page for styling CLASSROOMIO-T-27: Styling of the review component & connecting it's data CLASSROOMIO-T-27: - Add ReviewsForm - Completion of the Reviews Form Structure/Styling CLASSROOMIO-T-27: Added a reviews modal CLASSROOMIO-T-27: Logic update for review section CLASSROOMIO-T-50: Logic for overlapping reviews CLASSROOMIO-T-27: - Completion of the structure & styling with mock data. - Addition of custom ratings assets CLASSROOMIO-T-27: - Review Section Code Structure - Import Profile Picture Assets Co-authored-by: Rotimi Best <[email protected]> Co-authored-by: Tunji Adeyemi <[email protected]> Co-authored-by: tunny17 <[email protected]> Merge-request: CLASSROOMIO-MR-22 Merged-by: Best Admin <[email protected]> CLASSROOMIO-T-62: Dark Mode Bug Fix CLASSROOMIO-T-62: Delete Unused class CLASSROOMIO-T-62: Dark Mode Bug Fix Co-authored-by: Tunji Adeyemi <[email protected]> Merge-request: CLASSROOMIO-MR-23 Merged-by: Best Admin <[email protected]> Co-authored-by: Ifeanyi Nwosu <[email protected]> Co-authored-by: Rotimi Best <[email protected]> Co-authored-by: Tunji Adeyemi <[email protected]> Co-authored-by: tunny17 <[email protected]> Merge-request: CLASSROOMIO-MR-33 Merged-by: Best Admin <[email protected]>
This commit is contained in:
committed by
Space Cloud
co-authored by
Rotimi Best
tunny17
Ifeanyi Nwosu
parent
4910ec4f56
commit
a27bc32c63
@@ -0,0 +1,40 @@
|
||||
<script>
|
||||
import YoutubeVideo from './YoutubeVideo.svelte';
|
||||
import LocalVideo from './LocalVideo.svelte';
|
||||
import * as CONSTANTS from './constants';
|
||||
|
||||
let tabs = CONSTANTS.videoTabs;
|
||||
let currentTab = tabs[0].value;
|
||||
|
||||
const onChange = (tab) => () => (currentTab = tab);
|
||||
</script>
|
||||
|
||||
<section class="w-full h-full flex flex-row items-start gap-3">
|
||||
<div class="flex-1 flex-col">
|
||||
<p class="text-sm font-normal text-start text-[#4F4B4B] dark:text-[#b0a9a9] mb-3">Add By</p>
|
||||
{#each tabs as item (item.value)}
|
||||
<button
|
||||
on:click={onChange(item.value)}
|
||||
class={`w-full px-4 py-3 my-5 border ${
|
||||
currentTab === item.value
|
||||
? 'border border-[#0233BD] bg-[#F5F8FE] dark:text-black'
|
||||
: 'border border-gray-200'
|
||||
} rounded-md cursor-pointer flex flex-row items-center justify-start gap-2 whitespace-nowrap`}
|
||||
>
|
||||
<svelte:component
|
||||
this={item.icon}
|
||||
size={20}
|
||||
color={`${currentTab === item.value ? 'dark:invert-0' : 'dark:invert'}`}
|
||||
/>
|
||||
<p>{item.title}</p>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<main class="w-full h-full">
|
||||
{#if currentTab === 1}
|
||||
<YoutubeVideo />
|
||||
{:else}
|
||||
<LocalVideo />
|
||||
{/if}
|
||||
</main>
|
||||
</section>
|
||||
@@ -0,0 +1,88 @@
|
||||
<script>
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import { deleteLessonVideo } from '$lib/components/Course/components/Lesson/store/lessons';
|
||||
|
||||
let isWrongFormat = false;
|
||||
let isUploaded = false;
|
||||
let isLoaded = false;
|
||||
let isBig = false;
|
||||
|
||||
const uploadVideo = () => {
|
||||
// if the file is too big or not the correct format
|
||||
isWrongFormat = true;
|
||||
|
||||
// when the upload has been sucessfully completed
|
||||
// here the you set the video_url to something and that would be displayed on the page
|
||||
isUploaded = false;
|
||||
|
||||
// closes the modal
|
||||
// $uploadCourseVideo.isModalOpen = false;
|
||||
};
|
||||
|
||||
const onUpload = () => {
|
||||
// PROBABLY GET THE SNAPSHOT OR SOMETHING
|
||||
isLoaded = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if isLoaded == false}
|
||||
<button
|
||||
on:click={onUpload}
|
||||
class="h-full w-full flex flex-col items-center justify-center border border-blue-300 border-dashed rounded-xl"
|
||||
>
|
||||
<img src="/upload-video.svg" alt="upload" />
|
||||
<span class="pt-3">
|
||||
<h3 class="text-center text-xl font-normal dark:text-white">Upload video</h3>
|
||||
<p class=" text-center text-sm font-normal">
|
||||
Select file( Mp4, MOV, AVI) to upload or drag and drop video here.<br /> Size must be less than
|
||||
20 MB.
|
||||
</p>
|
||||
</span>
|
||||
</button>
|
||||
{:else if isBig && isUploaded == false}
|
||||
<div class="h-full w-full flex flex-col items-center justify-center rounded-xl">
|
||||
<img src="/video-upload-error.svg" alt="upload error" />
|
||||
<span class="pt-3 pb-2">
|
||||
<h3 class="text-center text-base font-normal dark:text-white">
|
||||
Oops 😬, couldn’t upload video
|
||||
</h3>
|
||||
<p class="text-center text-xs font-normal text-[#ADADAD]">
|
||||
Sorry we video wasn’t uploaded. The file size is too big,<br /> maximum size is 20 MB. Try again!
|
||||
</p>
|
||||
</span>
|
||||
<PrimaryButton label="Try again" />
|
||||
</div>
|
||||
{:else if isWrongFormat && isUploaded == false}
|
||||
<div class="h-full w-full flex flex-col items-center justify-center rounded-xl">
|
||||
<img src="/video-upload-error.svg" alt="upload error" />
|
||||
<span class="pt-3 pb-2">
|
||||
<h3 class="text-center text-base font-normal dark:text-white">
|
||||
Oops 😬, couldn’t upload video
|
||||
</h3>
|
||||
<p class="text-center text-xs font-normal text-[#ADADAD]">
|
||||
Sorry we video wasn’t uploaded, the file format isn’t supported.<br /> Upload MP4, MOV and AVI
|
||||
files.
|
||||
</p>
|
||||
</span>
|
||||
<PrimaryButton label="Try again" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col w-full h-full items-start justify-between">
|
||||
<div class="h-auto w-full border rounded-md px-8 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="flex items-center gap-2">
|
||||
<div class="rounded-sm overflow-hidden">
|
||||
<img src="/course-video.png" alt="coursevideo" class="object-cover" />
|
||||
</div>
|
||||
<p>Video.png</p>
|
||||
</span>
|
||||
<button on:click={deleteLessonVideo}>
|
||||
<img src="/delete-video.svg" alt="deletevideo" class="dark:invert" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<PrimaryButton label="upload" onClick={uploadVideo} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,79 @@
|
||||
<script>
|
||||
import { CopyButton, Tag } from 'carbon-components-svelte';
|
||||
import TrashCanIcon from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
||||
import TextField from '$lib/components/Form/TextField.svelte';
|
||||
import { lesson, isLessonDirty } from '$lib/components/Course/components/Lesson/store/lessons';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import Chip from '$lib/components/Chip/Text.svelte';
|
||||
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||
|
||||
let youtubeLinks = '';
|
||||
let error = '';
|
||||
|
||||
function addVideo() {
|
||||
const links = youtubeLinks.split(',').map((link) => link.trim());
|
||||
const validLinks = links.filter(isValidYouTubeLink);
|
||||
|
||||
if (validLinks.length === 0) {
|
||||
error = 'Invalid YouTube link(s)';
|
||||
} else {
|
||||
const existingLinks = $lesson.materials.video_url
|
||||
.split(',')
|
||||
.map((link) => link.trim())
|
||||
.filter(Boolean);
|
||||
const updatedLinks = [
|
||||
...existingLinks,
|
||||
...validLinks.filter((link) => !existingLinks.includes(link))
|
||||
];
|
||||
$lesson.materials.video_url = updatedLinks.join(',').trim();
|
||||
youtubeLinks = '';
|
||||
error = '';
|
||||
}
|
||||
}
|
||||
|
||||
function isValidYouTubeLink(link) {
|
||||
const youtubeRegex =
|
||||
/^(https?:\/\/)?(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/;
|
||||
|
||||
return youtubeRegex.test(link.trim());
|
||||
}
|
||||
|
||||
function removeVideo(index) {
|
||||
const links = $lesson.materials.video_url.split(',').map((link) => link.trim());
|
||||
links.splice(index, 1);
|
||||
$lesson.materials.video_url = links.join(',').trim();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full flex items-{error ? 'center' : 'end'} justify-between gap-5">
|
||||
<TextField
|
||||
label="Youtube link"
|
||||
bind:value={youtubeLinks}
|
||||
className="flex-1 text-left "
|
||||
inputClassName="text-sm"
|
||||
onChange={() => ($isLessonDirty = true)}
|
||||
placeholder="https://www.youtube.com/watch?v="
|
||||
errorMessage={error}
|
||||
/>
|
||||
<PrimaryButton label="Add Video" className="rounded-md" onClick={addVideo} />
|
||||
</div>
|
||||
<p class="mt-4 pl-2 text-sm">
|
||||
Added : <strong
|
||||
>{($lesson?.materials?.video_url && $lesson.materials.video_url.trim().split(',').length) || 0} Videos</strong
|
||||
>
|
||||
</p>
|
||||
<div class="">
|
||||
{#each $lesson?.materials?.video_url && $lesson.materials.video_url
|
||||
.trim()
|
||||
.split(',') as videoLink, index}
|
||||
<div class="flex items-center gap-1">
|
||||
<Tag type="blue">
|
||||
{videoLink}
|
||||
</Tag>
|
||||
<CopyButton text={videoLink} feedback="Copied to clipboard" />
|
||||
<IconButton value="delete-video" onClick={() => removeVideo(index)}>
|
||||
<TrashCanIcon size={16} class="carbon-icon dark:text-white" />
|
||||
</IconButton>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
import Link from 'carbon-icons-svelte/lib/Link.svelte';
|
||||
import Video from 'carbon-icons-svelte/lib/Video.svelte';
|
||||
export const videoTabs = [
|
||||
{
|
||||
value: 1,
|
||||
title: 'Youtube Link',
|
||||
icon: Link
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
title: 'Video',
|
||||
icon: Video
|
||||
}
|
||||
];
|
||||
@@ -1,11 +1,13 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { useCompletion } from 'ai/svelte';
|
||||
import MODES from '$lib/utils/constants/mode.js';
|
||||
import { getEmbedId } from '$lib/utils/functions/formatYoutubeVideo';
|
||||
import Modal from '$lib/components/Modal/index.svelte';
|
||||
import { Popover } from 'carbon-components-svelte';
|
||||
import AlignBoxTopLeftIcon from 'carbon-icons-svelte/lib/AlignBoxTopLeft.svelte';
|
||||
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
||||
import ListIcon from 'carbon-icons-svelte/lib/List.svelte';
|
||||
import IbmWatsonKnowledgeStudioIcon from 'carbon-icons-svelte/lib/IbmWatsonKnowledgeStudio.svelte';
|
||||
import { formatYoutubeVideo } from '$lib/utils/functions/formatYoutubeVideo';
|
||||
import MachineLearningModel from 'carbon-icons-svelte/lib/MachineLearningModel.svelte';
|
||||
import Tabs from '$lib/components/Tabs/index.svelte';
|
||||
import TabContent from '$lib/components/TabContent/index.svelte';
|
||||
@@ -14,24 +16,50 @@
|
||||
import EditContent from '$lib/components/EditContent/index.svelte';
|
||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||
import TextField from '$lib/components/Form/TextField.svelte';
|
||||
import { lesson, lessons, handleUpdateLessonMaterials, isLessonDirty } from '../store/lessons';
|
||||
import {
|
||||
lesson,
|
||||
lessons,
|
||||
handleUpdateLessonMaterials,
|
||||
isLessonDirty,
|
||||
uploadCourseVideoStore,
|
||||
deleteLessonVideo
|
||||
} from '$lib/components/Course/components/Lesson/store/lessons';
|
||||
import VideoUploader from '$lib/components/Course/components/Lesson/Materials/Video/Index.svelte';
|
||||
import { course } from '$lib/components/Course/store';
|
||||
import * as CONSTANTS from './constants';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
export let mode = MODES.view;
|
||||
export let prevMode = null;
|
||||
export let lessonId;
|
||||
export let prevMode = '';
|
||||
export let lessonId = '';
|
||||
|
||||
let tabs = CONSTANTS.tabs;
|
||||
let currentTab = tabs[0].value;
|
||||
let errors = {};
|
||||
let player = {};
|
||||
let textareaRef = {};
|
||||
let aiButtonRef = {};
|
||||
let openPopover = false;
|
||||
let videoUrls: Array<string> = [];
|
||||
let aiButtonClass =
|
||||
'flex items-center px-5 py-2 border border-gray-300 hover:bg-gray-200 rounded-md w-full mb-2';
|
||||
|
||||
const onChange = (tab) => () => (currentTab = tab);
|
||||
const onChange =
|
||||
(tab = 0) =>
|
||||
() =>
|
||||
(currentTab = tab);
|
||||
|
||||
function initPlyr(_player, videoUrl = '') {
|
||||
if (!browser) return;
|
||||
|
||||
const players = Array.from(document.querySelectorAll('.plyr-video-trigger')).map((p) => {
|
||||
// @ts-ignore
|
||||
return new Plyr(p);
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
window.players = players;
|
||||
}
|
||||
|
||||
function handleSave(prevMode) {
|
||||
if (prevMode === MODES.edit) {
|
||||
@@ -58,6 +86,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
const openAddVideoModal = () => {
|
||||
$uploadCourseVideoStore.isModalOpen = true;
|
||||
};
|
||||
|
||||
const { input, handleSubmit, completion, isLoading } = useCompletion({
|
||||
api: '/api/completion'
|
||||
});
|
||||
@@ -81,13 +113,31 @@
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
handleUpdateLessonMaterials($lesson, lessonId);
|
||||
$uploadCourseVideoStore.isModalOpen = false;
|
||||
};
|
||||
|
||||
$: handleSave(prevMode);
|
||||
|
||||
$: initPlyr(player, $lesson.materials.video_url);
|
||||
|
||||
$: addBadgeValueToTab($lesson.materials);
|
||||
|
||||
$: updateNoteByCompletion($completion);
|
||||
|
||||
$: videoUrls = ($lesson?.materials?.video_url || '').split(',').filter((url) => !!url.trim());
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
{onClose}
|
||||
bind:open={$uploadCourseVideoStore.isModalOpen}
|
||||
width="w-4/5 h-[566px]"
|
||||
modalHeading="Add a Video"
|
||||
>
|
||||
<VideoUploader {onClose} />
|
||||
</Modal>
|
||||
|
||||
<Tabs {tabs} {currentTab} {onChange}>
|
||||
<slot:fragment slot="content">
|
||||
<TabContent value={tabs[0].value} index={currentTab}>
|
||||
@@ -186,37 +236,48 @@
|
||||
</TabContent>
|
||||
|
||||
<TabContent value={tabs[2].value} index={currentTab}>
|
||||
{#if mode === MODES.edit}
|
||||
<TextField
|
||||
label="Youtube link"
|
||||
bind:value={$lesson.materials.video_url}
|
||||
onChange={() => ($isLessonDirty = true)}
|
||||
/>
|
||||
{:else if !!$lesson.materials.video_url && $lesson.materials.video_url.includes('youtube')}
|
||||
{#each $lesson.materials.video_url.split(',') as url}
|
||||
<div class="mb-5">
|
||||
<iframe
|
||||
width="100%"
|
||||
height="569"
|
||||
class="iframe"
|
||||
src={formatYoutubeVideo(url, errors)}
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#if mode === MODES.edit && videoUrls.length}
|
||||
<PrimaryButton label="Add/Edit Video" onClick={openAddVideoModal} className="mb-2" />
|
||||
{/if}
|
||||
<!-- Line 230 would run if its in edit mode or the video is truthy but then in line 235 we are mapping for an empty string -->
|
||||
{#if videoUrls.length}
|
||||
<div class="flex flex-col items-start w-full h-full">
|
||||
{#each videoUrls as url, index}
|
||||
<div class="w-full h-full flex flex-col gap-2 overflow-hidden">
|
||||
{#if url.includes('youtube')}
|
||||
<div class="mb-5">
|
||||
<div
|
||||
bind:this={player}
|
||||
class="plyr-video-trigger"
|
||||
data-plyr-provider="youtube"
|
||||
data-plyr-embed-id={getEmbedId(url)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<video bind:this={player} class="plyr-video-trigger" playsinline controls>
|
||||
<source
|
||||
src="https://pub-bd38ff7251aa402a93762181a947f7ea.r2.dev/generate-lesson-draft.mp4"
|
||||
type="video/mp4"
|
||||
/>
|
||||
<!-- <source src="/path/to/video.webm" type="video/webm" /> -->
|
||||
<!-- Captions are optional -->
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<Box>
|
||||
<div class="flex justify-between flex-col items-center w-96">
|
||||
<img src="/images/empty-video-icon.svg" alt="No Video" class="my-2.5 mx-auto" />
|
||||
<h2 class="text-xl my-1.5 font-normal">No videos yet</h2>
|
||||
<p class="text-sm text-center text-slate-500">
|
||||
Share your knowledge with the world by creating engaging videos. Add your video link
|
||||
now.
|
||||
</p>
|
||||
</div>
|
||||
<img src="/no-video.svg" alt="Video not found" />
|
||||
<h3 class="text-xl font-normal dark:text-white py-2">
|
||||
No youtube video added for this lesson yet
|
||||
</h3>
|
||||
<p class="text-sm text-center font-normal py-2">
|
||||
Share your knowledge with the world by creating engaging lessons.<br />
|
||||
Start by clicking on New Lesson button.
|
||||
</p>
|
||||
<PrimaryButton label="Add Video" className="rounded-md" onClick={openAddVideoModal} />
|
||||
</Box>
|
||||
{/if}
|
||||
</TabContent>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Writable, Updater } from 'svelte/store';
|
||||
import { createLesson, updateLesson, deleteLesson } from '$lib/utils/services/courses';
|
||||
|
||||
import type { Lesson, Course } from '$lib/utils/types';
|
||||
|
||||
export const uploadCourseVideoStore = writable({
|
||||
isModalOpen: false
|
||||
});
|
||||
|
||||
export const lessons: Writable<Lesson[]> = writable([]);
|
||||
export const lesson = writable({
|
||||
id: null,
|
||||
totalExercises: 0,
|
||||
is_complete: false,
|
||||
materials: {
|
||||
note: null,
|
||||
slide_url: null,
|
||||
video_url: null
|
||||
note: '',
|
||||
slide_url: '',
|
||||
video_url: ''
|
||||
},
|
||||
exercises: []
|
||||
});
|
||||
@@ -92,7 +95,23 @@ export async function handleUpdateLessonMaterials(lesson: any, lessonId: Lesson[
|
||||
...lesson.materials
|
||||
};
|
||||
|
||||
delete materials.profile;
|
||||
delete materials.totalExercises;
|
||||
|
||||
return await updateLesson(materials, lessonId);
|
||||
}
|
||||
|
||||
export const deleteLessonVideo = (index: any) => {
|
||||
lesson.update((currentLesson) => {
|
||||
const links = currentLesson.materials.video_url.split(',').map((link) => link.trim());
|
||||
links.splice(index, 1);
|
||||
const updatedVideoUrl = links.join(',').trim();
|
||||
return {
|
||||
...currentLesson,
|
||||
materials: {
|
||||
...currentLesson.materials,
|
||||
video_url: updatedVideoUrl
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
</RoleBasedSecurity>
|
||||
</slot:fragment>
|
||||
</PageNav> -->
|
||||
<div class="h-full mt-2 overflow-y-auto">
|
||||
<div class="h-full py-3 overflow-y-auto">
|
||||
{#each navItems as navItem}
|
||||
{#if !navItem.show || (typeof navItem.show === 'function' && navItem.show())}
|
||||
<NavExpandable
|
||||
@@ -270,7 +270,6 @@
|
||||
max-width: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user