fix: course settings showing save popup wrongly
This commit is contained in:
@@ -4,6 +4,9 @@
|
|||||||
import { dndzone } from 'svelte-dnd-action';
|
import { dndzone } from 'svelte-dnd-action';
|
||||||
import Draggable from 'carbon-icons-svelte/lib/Draggable.svelte';
|
import Draggable from 'carbon-icons-svelte/lib/Draggable.svelte';
|
||||||
import { t } from '$lib/utils/functions/translations';
|
import { t } from '$lib/utils/functions/translations';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
const flipDurationMs = 300;
|
const flipDurationMs = 300;
|
||||||
|
|
||||||
@@ -13,6 +16,8 @@
|
|||||||
|
|
||||||
function handleDndFinalize(e) {
|
function handleDndFinalize(e) {
|
||||||
$settings.tabs = e.detail.items;
|
$settings.tabs = e.detail.items;
|
||||||
|
|
||||||
|
dispatch('change');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
import IconButton from '$lib/components/IconButton/index.svelte';
|
import IconButton from '$lib/components/IconButton/index.svelte';
|
||||||
import generateSlug from '$lib/utils/functions/generateSlug';
|
import generateSlug from '$lib/utils/functions/generateSlug';
|
||||||
import { t } from '$lib/utils/functions/translations';
|
import { t } from '$lib/utils/functions/translations';
|
||||||
|
import DeleteModal from '$lib/components/Modal/DeleteModal.svelte';
|
||||||
import { COURSE_TYPE } from '$lib/utils/types';
|
import { COURSE_TYPE } from '$lib/utils/types';
|
||||||
|
|
||||||
let isSaving = false;
|
let isSaving = false;
|
||||||
@@ -43,9 +44,10 @@
|
|||||||
let errors: {
|
let errors: {
|
||||||
title: string | undefined;
|
title: string | undefined;
|
||||||
description: string | undefined;
|
description: string | undefined;
|
||||||
} = { title: undefined, description: undefined };
|
};
|
||||||
let avatar: string | undefined;
|
let avatar: string | undefined;
|
||||||
let hasUnsavedChanges = false;
|
let hasUnsavedChanges = false;
|
||||||
|
let openDeleteModal = false;
|
||||||
|
|
||||||
function widgetControl() {
|
function widgetControl() {
|
||||||
$handleOpenWidget.open = !$handleOpenWidget.open;
|
$handleOpenWidget.open = !$handleOpenWidget.open;
|
||||||
@@ -71,6 +73,7 @@
|
|||||||
slideUrl: lesson.slide_url || '',
|
slideUrl: lesson.slide_url || '',
|
||||||
video: lesson.videos || ''
|
video: lesson.videos || ''
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const response = await fetch(env.PUBLIC_SERVER_URL + '/downloadCourse', {
|
const response = await fetch(env.PUBLIC_SERVER_URL + '/downloadCourse', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -109,19 +112,21 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteBannerImage = () => {
|
const deleteBannerImage = () => {
|
||||||
$course.logo = '';
|
$settings.logo = '';
|
||||||
|
hasUnsavedChanges = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleDeleteCourse() {
|
async function handleDeleteCourse() {
|
||||||
isDeleting = true;
|
isDeleting = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteCourse($course.id);
|
await deleteCourse($course.id);
|
||||||
isDeleting = false;
|
|
||||||
goto($currentOrgPath + '/courses');
|
goto($currentOrgPath + '/courses');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
snackbar.error('snackbar.course_settings.error.went_wrong');
|
snackbar.error('snackbar.course_settings.error.went_wrong');
|
||||||
isDeleting = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDeleting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
@@ -172,12 +177,11 @@
|
|||||||
|
|
||||||
const generateNewCourseLink = () => {
|
const generateNewCourseLink = () => {
|
||||||
$course.slug = generateSlug($course.title);
|
$course.slug = generateSlug($course.title);
|
||||||
|
hasUnsavedChanges = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
let hasInitialized = false;
|
|
||||||
|
|
||||||
async function setDefault(course: Course) {
|
async function setDefault(course: Course) {
|
||||||
if (!course || !Object.keys(course).length || hasInitialized) return;
|
if (!course || !Object.keys(course).length) return;
|
||||||
|
|
||||||
settings.set({
|
settings.set({
|
||||||
course_title: course.title,
|
course_title: course.title,
|
||||||
@@ -190,14 +194,6 @@
|
|||||||
is_published: !!course.is_published,
|
is_published: !!course.is_published,
|
||||||
allow_new_students: course.metadata.allowNewStudent
|
allow_new_students: course.metadata.allowNewStudent
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.subscribe((s) => {
|
|
||||||
if (!hasInitialized) return;
|
|
||||||
|
|
||||||
hasUnsavedChanges = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
hasInitialized = true;
|
|
||||||
}
|
}
|
||||||
$: setDefault($course);
|
$: setDefault($course);
|
||||||
|
|
||||||
@@ -206,6 +202,8 @@
|
|||||||
|
|
||||||
<UnsavedChanges {hasUnsavedChanges} />
|
<UnsavedChanges {hasUnsavedChanges} />
|
||||||
|
|
||||||
|
<DeleteModal onDelete={handleDeleteCourse} bind:open={openDeleteModal} />
|
||||||
|
|
||||||
<Grid class="border-c rounded border-gray-200 dark:border-neutral-600">
|
<Grid class="border-c rounded border-gray-200 dark:border-neutral-600">
|
||||||
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
@@ -215,19 +213,24 @@
|
|||||||
</p>
|
</p>
|
||||||
<span class="flex items-center justify-start">
|
<span class="flex items-center justify-start">
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={VARIANTS.OUTLINED}
|
variant={VARIANTS.CONTAINED_DARK}
|
||||||
label={$t('course.navItem.settings.replace')}
|
label={$t('course.navItem.settings.replace')}
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
onClick={widgetControl}
|
onClick={widgetControl}
|
||||||
/>
|
/>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={VARIANTS.CONTAINED_DANGER}
|
variant={VARIANTS.OUTLINED}
|
||||||
label={$t('course.navItem.settings.del')}
|
label={$t('ai.reset')}
|
||||||
onClick={deleteBannerImage}
|
onClick={deleteBannerImage}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
{#if $handleOpenWidget.open}
|
{#if $handleOpenWidget.open}
|
||||||
<UploadWidget bind:imageURL={$settings.logo} />
|
<UploadWidget
|
||||||
|
bind:imageURL={$settings.logo}
|
||||||
|
on:change={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</Column>
|
</Column>
|
||||||
|
|
||||||
@@ -255,7 +258,10 @@
|
|||||||
className="w-full mb-5"
|
className="w-full mb-5"
|
||||||
isRequired
|
isRequired
|
||||||
bind:value={$settings.course_title}
|
bind:value={$settings.course_title}
|
||||||
errorMessage={errors.title}
|
errorMessage={errors?.title}
|
||||||
|
onInputChange={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
<TextArea
|
||||||
label={$t('course.navItem.settings.course_description')}
|
label={$t('course.navItem.settings.course_description')}
|
||||||
@@ -263,7 +269,10 @@
|
|||||||
className="w-full mb-5"
|
className="w-full mb-5"
|
||||||
isRequired
|
isRequired
|
||||||
bind:value={$settings.course_description}
|
bind:value={$settings.course_description}
|
||||||
errorMessage={errors.description}
|
errorMessage={errors?.description}
|
||||||
|
onChange={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div class="">
|
<div class="">
|
||||||
<p class="text-md flex items-center gap-2 mb-2">
|
<p class="text-md flex items-center gap-2 mb-2">
|
||||||
@@ -303,7 +312,11 @@
|
|||||||
<p>{$t('course.navItem.settings.drag')}</p>
|
<p>{$t('course.navItem.settings.drag')}</p>
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<DragAndDrop />
|
<DragAndDrop
|
||||||
|
on:change={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
||||||
@@ -315,7 +328,13 @@
|
|||||||
{#if $isFreePlan}
|
{#if $isFreePlan}
|
||||||
<UpgradeBanner>{$t('upgrade.download_lessons')}</UpgradeBanner>
|
<UpgradeBanner>{$t('upgrade.download_lessons')}</UpgradeBanner>
|
||||||
{:else}
|
{:else}
|
||||||
<Toggle size="sm" bind:toggled={$settings.lesson_download}>
|
<Toggle
|
||||||
|
size="sm"
|
||||||
|
bind:toggled={$settings.lesson_download}
|
||||||
|
on:toggle={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span slot="labelA" style="color: gray">{$t('course.navItem.settings.disabled')}</span>
|
<span slot="labelA" style="color: gray">{$t('course.navItem.settings.disabled')}</span>
|
||||||
<span slot="labelB" style="color: gray">{$t('course.navItem.settings.enabled')}</span>
|
<span slot="labelB" style="color: gray">{$t('course.navItem.settings.enabled')}</span>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
@@ -348,7 +367,13 @@
|
|||||||
<p>{$t('course.navItem.settings.course_type_desc')}</p>
|
<p>{$t('course.navItem.settings.course_type_desc')}</p>
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<RadioButtonGroup hideLegend bind:selected={$settings.type}>
|
<RadioButtonGroup
|
||||||
|
hideLegend
|
||||||
|
bind:selected={$settings.type}
|
||||||
|
on:change={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<RadioButton
|
<RadioButton
|
||||||
labelText={$t('course.navItem.settings.live_class')}
|
labelText={$t('course.navItem.settings.live_class')}
|
||||||
value={COURSE_TYPE.LIVE_CLASS}
|
value={COURSE_TYPE.LIVE_CLASS}
|
||||||
@@ -367,7 +392,13 @@
|
|||||||
<p>{$t('course.navItem.settings.access')}</p>
|
<p>{$t('course.navItem.settings.access')}</p>
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<Toggle size="sm" bind:toggled={$settings.allow_new_students}>
|
<Toggle
|
||||||
|
size="sm"
|
||||||
|
bind:toggled={$settings.allow_new_students}
|
||||||
|
on:toggle={() => {
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span slot="labelA" style="color: gray">{$t('course.navItem.settings.disabled')}</span>
|
<span slot="labelA" style="color: gray">{$t('course.navItem.settings.disabled')}</span>
|
||||||
<span slot="labelB" style="color: gray">{$t('course.navItem.settings.enabled')}</span>
|
<span slot="labelB" style="color: gray">{$t('course.navItem.settings.enabled')}</span>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
@@ -386,7 +417,7 @@
|
|||||||
bind:toggled={$settings.is_published}
|
bind:toggled={$settings.is_published}
|
||||||
on:toggle={(e) => {
|
on:toggle={(e) => {
|
||||||
$settings.allow_new_students = e.detail.toggled;
|
$settings.allow_new_students = e.detail.toggled;
|
||||||
|
hasUnsavedChanges = true;
|
||||||
if (!$course.slug) {
|
if (!$course.slug) {
|
||||||
generateNewCourseLink();
|
generateNewCourseLink();
|
||||||
}
|
}
|
||||||
@@ -407,7 +438,7 @@
|
|||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={VARIANTS.CONTAINED_DANGER}
|
variant={VARIANTS.CONTAINED_DANGER}
|
||||||
label={$t('course.navItem.settings.delete')}
|
label={$t('course.navItem.settings.delete')}
|
||||||
onClick={handleDeleteCourse}
|
onClick={() => (openDeleteModal = true)}
|
||||||
isLoading={isDeleting}
|
isLoading={isDeleting}
|
||||||
isDisabled={isDeleting}
|
isDisabled={isDeleting}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
export let errorMessage = '';
|
export let errorMessage = '';
|
||||||
export let isRequired = false;
|
export let isRequired = false;
|
||||||
export let onChange = () => {}; // This is to know if element is 'dirty'
|
export let onChange = () => {}; // This is to know if element is 'dirty'
|
||||||
export let ref = null;
|
export let ref: HTMLTextAreaElement | null = null;
|
||||||
export let isAIEnabled = false;
|
export let isAIEnabled = false;
|
||||||
export let initAIPrompt = '';
|
export let initAIPrompt = '';
|
||||||
export let aiAlignPopover: PopoverProps['align'] = 'left';
|
export let aiAlignPopover: PopoverProps['align'] = 'left';
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Modal from './index.svelte';
|
||||||
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
|
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
|
||||||
|
import { t } from '$lib/utils/functions/translations';
|
||||||
|
|
||||||
|
export let open = false;
|
||||||
|
export let onDelete = () => {};
|
||||||
|
|
||||||
|
function handleDelete() {
|
||||||
|
onDelete();
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
onClose={() => (open = false)}
|
||||||
|
bind:open
|
||||||
|
width="w-96"
|
||||||
|
containerClass="p-6"
|
||||||
|
headerClass="p-2"
|
||||||
|
modalHeading={$t('delete_modal.label')}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p class="dark:text-white text-xl text-center">
|
||||||
|
{$t('delete_modal.content')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-8 flex items-center justify-between">
|
||||||
|
<PrimaryButton
|
||||||
|
className="px-6 py-3"
|
||||||
|
variant={VARIANTS.OUTLINED}
|
||||||
|
label={$t('delete_modal.no')}
|
||||||
|
onClick={() => (open = false)}
|
||||||
|
/>
|
||||||
|
<PrimaryButton
|
||||||
|
className="px-6 py-3"
|
||||||
|
variant={VARIANTS.CONTAINED_DANGER}
|
||||||
|
label={$t('delete_modal.yes')}
|
||||||
|
onClick={handleDelete}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
role="presentation"
|
role="presentation"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between border border-l-0 border-r-0 border-t-0 border-gray-100 dark:border-neutral-600 p-4 px-5 {headerClass}"
|
class="{headerClass} flex items-center justify-between border border-l-0 border-r-0 border-t-0 border-gray-100 dark:border-neutral-600 p-4 px-5"
|
||||||
>
|
>
|
||||||
<p class="text-md m-0 font-medium dark:text-white {labelClass}">
|
<p class="text-md m-0 font-medium dark:text-white {labelClass}">
|
||||||
{modalHeading}
|
{modalHeading}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount, createEventDispatcher } from 'svelte';
|
||||||
import Tabs from '$lib/components/Tabs/index.svelte';
|
import Tabs from '$lib/components/Tabs/index.svelte';
|
||||||
import { getSupabase } from '$lib/utils/functions/supabase';
|
import { getSupabase } from '$lib/utils/functions/supabase';
|
||||||
import TabContent from '$lib/components/TabContent/index.svelte';
|
import TabContent from '$lib/components/TabContent/index.svelte';
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
export let imageURL = '';
|
export let imageURL = '';
|
||||||
|
|
||||||
const supabase = getSupabase();
|
const supabase = getSupabase();
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ label: 'Unsplash', value: 'unsplash' },
|
{ label: 'Unsplash', value: 'unsplash' },
|
||||||
{ label: 'Upload', value: 'upload' }
|
{ label: 'Upload', value: 'upload' }
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
const onChange = (tabValue: string) => () => (currentTab = tabValue);
|
const onChange = (tabValue: string) => () => (currentTab = tabValue);
|
||||||
|
|
||||||
async function handleImageClick(img: string) {
|
async function handleImageClick(img: string) {
|
||||||
|
dispatch('change');
|
||||||
imageURL = img;
|
imageURL = img;
|
||||||
$handleOpenWidget.open = false;
|
$handleOpenWidget.open = false;
|
||||||
}
|
}
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
if (data) {
|
if (data) {
|
||||||
const { data: response } = await supabase.storage.from('avatars').getPublicUrl(filename);
|
const { data: response } = await supabase.storage.from('avatars').getPublicUrl(filename);
|
||||||
imageURL = response.publicUrl;
|
imageURL = response.publicUrl;
|
||||||
|
dispatch('change');
|
||||||
}
|
}
|
||||||
isUploading = false;
|
isUploading = false;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { Theme } from 'carbon-components-svelte';
|
import { Theme } from 'carbon-components-svelte';
|
||||||
import type { CarbonTheme } from 'carbon-components-svelte/types/Theme/Theme.svelte';
|
import type { CarbonTheme } from 'carbon-components-svelte/types/Theme/Theme.svelte';
|
||||||
import LandingNavigation from '$lib/components/Navigation/index.svelte';
|
import LandingNavigation from '$lib/components/Navigation/index.svelte';
|
||||||
import ProgressBar from '$lib/components/ProgressBar/index.svelte';
|
import PageLoadProgressBar from '$lib/components/Progress/PageLoadProgressBar.svelte';
|
||||||
import OrgNavigation from '$lib/components/Navigation/app.svelte';
|
import OrgNavigation from '$lib/components/Navigation/app.svelte';
|
||||||
import LMSNavigation from '$lib/components/Navigation/lms.svelte';
|
import LMSNavigation from '$lib/components/Navigation/lms.svelte';
|
||||||
import OrgLandingPage from '$lib/components/Org/LandingPage/index.svelte';
|
import OrgLandingPage from '$lib/components/Org/LandingPage/index.svelte';
|
||||||
@@ -318,7 +318,7 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<ProgressBar textColorClass="text-gray-400" />
|
<PageLoadProgressBar textColorClass="text-neutral-700" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class={path.includes('home') ? '' : 'flex justify-between'}>
|
<div class={path.includes('home') ? '' : 'flex justify-between'}>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import PageNav from '$lib/components/PageNav/index.svelte';
|
import PageNav from '$lib/components/PageNav/index.svelte';
|
||||||
import PageBody from '$lib/components/PageBody/index.svelte';
|
import PageBody from '$lib/components/PageBody/index.svelte';
|
||||||
import Settings from '$lib/components/Course/components/Settings/index.svelte';
|
import Settings from '$lib/components/Course/components/Settings/index.svelte';
|
||||||
import { t } from '../../../../lib/utils/functions/translations';
|
import { t } from '$lib/utils/functions/translations';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user