course landing page improvements
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: onCompletion($completion);
|
$: onCompletion($completion);
|
||||||
|
$: prompt = defaultPrompt;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="{className} relative z-[100]">
|
<div class="{className} relative z-[100]">
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
<div class="root">
|
<script lang="ts">
|
||||||
|
export let disableCenteredContent = false;
|
||||||
|
export let className = '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="root {!className.includes('opacity') && 'opacity-100'} {!disableCenteredContent &&
|
||||||
|
'center'} {className}"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -8,15 +16,17 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
display: flex;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
z-index: 1201;
|
z-index: 1201;
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
|
||||||
import ArrowUpRightIcon from '$lib/components/Icons/ArrowTopRight.svelte';
|
import ArrowUpRightIcon from '$lib/components/Icons/ArrowTopRight.svelte';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrgDomain } from '$lib/utils/store/org';
|
||||||
import { isMobile } from '$lib/utils/store/useMobile';
|
import { isMobile } from '$lib/utils/store/useMobile';
|
||||||
|
|
||||||
export let className = '';
|
export let className = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a
|
<a href={$currentOrgDomain} target="_blank" class="{className} ml-2 hover:no-underline">
|
||||||
href={`https://${$currentOrg.siteName}.classroomio.com`}
|
|
||||||
target="_blank"
|
|
||||||
class="{className} ml-2 hover:no-underline"
|
|
||||||
>
|
|
||||||
<PrimaryButton>
|
<PrimaryButton>
|
||||||
{#if !$isMobile}
|
{#if !$isMobile}
|
||||||
View site
|
View site
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import { currentOrgPath } from '$lib/utils/store/org';
|
import { currentOrgPath } from '$lib/utils/store/org';
|
||||||
import { isObject } from '$lib/utils/functions/isObject';
|
import { isObject } from '$lib/utils/functions/isObject';
|
||||||
import { lessons } from '../Lesson/store/lessons';
|
import { lessons } from '../Lesson/store/lessons';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg, currentOrgDomain } from '$lib/utils/store/org';
|
||||||
import { snackbar } from '$lib/components/Snackbar/store';
|
import { snackbar } from '$lib/components/Snackbar/store';
|
||||||
import UploadWidget from '$lib/components/UploadWidget/index.svelte';
|
import UploadWidget from '$lib/components/UploadWidget/index.svelte';
|
||||||
import { handleOpenWidget } from '$lib/components/CourseLandingPage/store';
|
import { handleOpenWidget } from '$lib/components/CourseLandingPage/store';
|
||||||
@@ -24,14 +24,17 @@
|
|||||||
let isSaving = false;
|
let isSaving = false;
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
let isDeleting = false;
|
let isDeleting = false;
|
||||||
let errors = {};
|
let errors: {
|
||||||
let avatar;
|
title: string | undefined;
|
||||||
|
description: string | undefined;
|
||||||
|
} = { title: undefined, description: undefined };
|
||||||
|
let avatar: string | undefined;
|
||||||
|
|
||||||
function widgetControl() {
|
function widgetControl() {
|
||||||
$handleOpenWidget.open = true;
|
$handleOpenWidget.open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLessonOrder(id) {
|
function getLessonOrder(id: string) {
|
||||||
const index = $lessons.findIndex((lesson) => lesson.id === id);
|
const index = $lessons.findIndex((lesson) => lesson.id === id);
|
||||||
if (index < 9) {
|
if (index < 9) {
|
||||||
return '0' + (index + 1);
|
return '0' + (index + 1);
|
||||||
@@ -172,7 +175,6 @@
|
|||||||
|
|
||||||
$: $settings.course_description = $course.description;
|
$: $settings.course_description = $course.description;
|
||||||
$: $settings.course_title = $course.title;
|
$: $settings.course_title = $course.title;
|
||||||
$: $settings.allow_new_students = $settings.is_published;
|
|
||||||
|
|
||||||
$: setDefault($course);
|
$: setDefault($course);
|
||||||
</script>
|
</script>
|
||||||
@@ -238,11 +240,7 @@
|
|||||||
<div class="">
|
<div class="">
|
||||||
<p class="text-md">Course Link</p>
|
<p class="text-md">Course Link</p>
|
||||||
{#if $course.slug}
|
{#if $course.slug}
|
||||||
<CodeSnippet
|
<CodeSnippet wrapText type="multi" code={`${$currentOrgDomain}/course/${$course.slug}`} />
|
||||||
wrapText
|
|
||||||
type="multi"
|
|
||||||
code={`https://${$currentOrg.siteName}.classroomio.com/course/${$course.slug}`}
|
|
||||||
/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<CodeSnippet code="Setup landing page to get course link" />
|
<CodeSnippet code="Setup landing page to get course link" />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -256,23 +254,12 @@
|
|||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<Toggle size="sm" bind:toggled={$settings.grading}>
|
<Toggle size="sm" bind:toggled={$settings.grading}>
|
||||||
<span slot="labelA" style="color: gray">Disable</span>
|
<span slot="labelA" style="color: gray">Disabled</span>
|
||||||
<span slot="labelB" style="color: gray">Enable</span>
|
<span slot="labelB" style="color: gray">Enabled</span>
|
||||||
</Toggle>
|
|
||||||
</Column>
|
|
||||||
</Row>
|
|
||||||
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
|
||||||
<Column sm={8} md={8} lg={8}>
|
|
||||||
<SectionTitle>Allow New Students</SectionTitle>
|
|
||||||
<p>Allow new students to access this course</p>
|
|
||||||
</Column>
|
|
||||||
<Column sm={8} md={8} lg={8}>
|
|
||||||
<Toggle size="sm" bind:toggled={$settings.allow_new_students}>
|
|
||||||
<span slot="labelA" style="color: gray">Disable</span>
|
|
||||||
<span slot="labelB" style="color: gray">Enable</span>
|
|
||||||
</Toggle>
|
</Toggle>
|
||||||
</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">
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<SectionTitle>Order Lessons Tab</SectionTitle>
|
<SectionTitle>Order Lessons Tab</SectionTitle>
|
||||||
@@ -289,8 +276,8 @@
|
|||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<Toggle size="sm" bind:toggled={$settings.lesson_download}>
|
<Toggle size="sm" bind:toggled={$settings.lesson_download}>
|
||||||
<span slot="labelA" style="color: gray">Disable</span>
|
<span slot="labelA" style="color: gray">Disabled</span>
|
||||||
<span slot="labelB" style="color: gray">Enable</span>
|
<span slot="labelB" style="color: gray">Enabled</span>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
</Column>
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -301,8 +288,8 @@
|
|||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
variant={VARIANTS.LINK}
|
variant={VARIANTS.OUTLINED}
|
||||||
label="Download Course"
|
label="Download"
|
||||||
onClick={downloadCourse}
|
onClick={downloadCourse}
|
||||||
isDisabled={isLoading}
|
isDisabled={isLoading}
|
||||||
{isLoading}
|
{isLoading}
|
||||||
@@ -310,6 +297,19 @@
|
|||||||
</Column>
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
<Row class="flex lg:flex-row flex-col py-7 border-bottom-c">
|
||||||
|
<Column sm={8} md={8} lg={8}>
|
||||||
|
<SectionTitle>Allow New Students</SectionTitle>
|
||||||
|
<p>Allow new students to access this course</p>
|
||||||
|
</Column>
|
||||||
|
<Column sm={8} md={8} lg={8}>
|
||||||
|
<Toggle size="sm" bind:toggled={$settings.allow_new_students}>
|
||||||
|
<span slot="labelA" style="color: gray">Disabled</span>
|
||||||
|
<span slot="labelB" style="color: gray">Enabled</span>
|
||||||
|
</Toggle>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
|
||||||
<!-- Publish Course -->
|
<!-- Publish Course -->
|
||||||
<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}>
|
||||||
@@ -317,7 +317,13 @@
|
|||||||
<p>This determines if your course displays on your landing page</p>
|
<p>This determines if your course displays on your landing page</p>
|
||||||
</Column>
|
</Column>
|
||||||
<Column sm={8} md={8} lg={8}>
|
<Column sm={8} md={8} lg={8}>
|
||||||
<Toggle size="sm" bind:toggled={$settings.is_published}>
|
<Toggle
|
||||||
|
size="sm"
|
||||||
|
bind:toggled={$settings.is_published}
|
||||||
|
on:toggle={(e) => {
|
||||||
|
$settings.allow_new_students = e.detail.toggled;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span slot="labelA" style="color: gray">Unpublished</span>
|
<span slot="labelA" style="color: gray">Unpublished</span>
|
||||||
<span slot="labelB" style="color: gray">Published</span>
|
<span slot="labelB" style="color: gray">Published</span>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
$: setFormatter(courseData.currency);
|
$: setFormatter(courseData.currency);
|
||||||
|
|
||||||
$: discount = get(courseData, 'metadata.discount', 0);
|
$: discount = get(courseData, 'metadata.discount', 0);
|
||||||
$: calculatedCost = calcDisc(discount, courseData.cost || 0, courseData.metadata.showDiscount);
|
$: calculatedCost = calcDisc(discount, courseData.cost || 0, !!courseData.metadata.showDiscount);
|
||||||
$: isFree = isCourseFree(calculatedCost);
|
$: isFree = isCourseFree(calculatedCost);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -77,18 +77,24 @@
|
|||||||
<div class="flex items-center justify-center gap-3 px-3 py-3">
|
<div class="flex items-center justify-center gap-3 px-3 py-3">
|
||||||
<!-- Pricing -->
|
<!-- Pricing -->
|
||||||
<div class=" text-center">
|
<div class=" text-center">
|
||||||
<p class="dark:text-white font-medium text-sm flex items-center gap-1">
|
{#if courseData?.metadata?.allowNewStudent}
|
||||||
{formatter?.format(calculatedCost) || calculatedCost}
|
<p class="dark:text-white font-medium text-sm flex items-center gap-1">
|
||||||
{#if isFree}
|
{formatter?.format(calculatedCost) || calculatedCost}
|
||||||
<span class="text-xs">(Free)</span>
|
{#if isFree}
|
||||||
|
<span class="text-xs">(Free)</span>
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
{#if courseData?.metadata?.showDiscount}
|
||||||
|
<p class="dark:text-white font-light text-sm text-gray-500">
|
||||||
|
{discount}% Discount.
|
||||||
|
<span class="line-through"
|
||||||
|
>{formatter?.format(courseData?.cost || 0) || courseData.cost}</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
{:else}
|
||||||
{#if courseData?.metadata?.showDiscount}
|
<p class="dark:text-white text-lg">
|
||||||
<p class="dark:text-white font-light text-sm text-gray-500">
|
This course is currently not accepting new students.
|
||||||
{discount}% Discount.
|
|
||||||
<span class="line-through"
|
|
||||||
>{formatter?.format(courseData?.cost || 0) || courseData.cost}</span
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -99,7 +105,7 @@
|
|||||||
label={isFree ? 'Join Course' : 'Buy Now'}
|
label={isFree ? 'Join Course' : 'Buy Now'}
|
||||||
className="w-full sm:w-full h-[40px]"
|
className="w-full sm:w-full h-[40px]"
|
||||||
onClick={handleJoinCourse}
|
onClick={handleJoinCourse}
|
||||||
isDisabled={!editMode && !courseData.metadata.allowNewStudent}
|
isDisabled={!courseData.metadata.allowNewStudent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,18 +120,24 @@
|
|||||||
<div class="p-2 lg:p-10">
|
<div class="p-2 lg:p-10">
|
||||||
<!-- Pricing -->
|
<!-- Pricing -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<p class="dark:text-white font-medium text-lg">
|
{#if courseData?.metadata?.allowNewStudent}
|
||||||
{formatter?.format(calculatedCost) || calculatedCost}
|
<p class="dark:text-white font-medium text-lg">
|
||||||
{#if isFree}
|
{formatter?.format(calculatedCost) || calculatedCost}
|
||||||
<span class="text-sm">(Free)</span>
|
{#if isFree}
|
||||||
|
<span class="text-sm">(Free)</span>
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
{#if courseData?.metadata?.showDiscount}
|
||||||
|
<p class="dark:text-white font-light text-sm text-gray-500">
|
||||||
|
{discount}% Discount.
|
||||||
|
<span class="line-through"
|
||||||
|
>{formatter?.format(courseData?.cost || 0) || courseData.cost}</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
{:else}
|
||||||
{#if courseData?.metadata?.showDiscount}
|
<p class="dark:text-white text-lg">
|
||||||
<p class="dark:text-white font-light text-sm text-gray-500">
|
This course is currently not accepting new students.
|
||||||
{discount}% Discount.
|
|
||||||
<span class="line-through"
|
|
||||||
>{formatter?.format(courseData?.cost || 0) || courseData.cost}</span
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -136,9 +148,11 @@
|
|||||||
label={isFree ? 'Join Course' : 'Buy Now'}
|
label={isFree ? 'Join Course' : 'Buy Now'}
|
||||||
className="w-full sm:w-full py-3 mb-3"
|
className="w-full sm:w-full py-3 mb-3"
|
||||||
onClick={handleJoinCourse}
|
onClick={handleJoinCourse}
|
||||||
isDisabled={!editMode && !courseData.metadata.allowNewStudent}
|
isDisabled={!courseData.metadata.allowNewStudent}
|
||||||
/>
|
/>
|
||||||
<p class="dark:text-white font-light text-sm text-gray-500">Early bird offer. Buy ASAP</p>
|
{#if courseData?.metadata?.showDiscount && courseData.metadata.allowNewStudent}
|
||||||
|
<p class="dark:text-white font-light text-sm text-gray-500">Early bird offer. Buy ASAP</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -128,9 +128,10 @@
|
|||||||
label="Start Course"
|
label="Start Course"
|
||||||
className="px-6 py-5 mt-6 sm:w-fit hidden md:block"
|
className="px-6 py-5 mt-6 sm:w-fit hidden md:block"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (editMode) return;
|
||||||
startCoursePayment = true;
|
startCoursePayment = true;
|
||||||
}}
|
}}
|
||||||
isDisabled={!editMode && !courseData.metadata.allowNewStudent}
|
isDisabled={!courseData.metadata.allowNewStudent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -202,7 +203,7 @@
|
|||||||
<a
|
<a
|
||||||
href="{$page.url.pathname}{navItem.key}"
|
href="{$page.url.pathname}{navItem.key}"
|
||||||
class="{navItem.key === activeNav &&
|
class="{navItem.key === activeNav &&
|
||||||
'active text-primary-700'} rounded-lg px-2 mr-6 text-slate-700 font-normal hover:bg-neutral-600 dark:hover:text-slate-900 dark:text-white z-0"
|
'active text-primary-700'} rounded-lg px-2 mr-6 text-slate-700 font-normal hover:bg-gray-200 dark:hover:text-slate-900 dark:text-white z-0"
|
||||||
>
|
>
|
||||||
{navItem.label}
|
{navItem.label}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
{#if isAIEnabled}
|
{#if isAIEnabled}
|
||||||
<CustomPromptBtn
|
<CustomPromptBtn
|
||||||
defaultPrompt={initAIPrompt}
|
bind:defaultPrompt={initAIPrompt}
|
||||||
alignPopover={aiAlignPopover}
|
alignPopover={aiAlignPopover}
|
||||||
handleInsert={(v) => {
|
handleInsert={(v) => {
|
||||||
value = v;
|
value = v;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import InformationIcon from 'carbon-icons-svelte/lib/Information.svelte';
|
import InformationIcon from 'carbon-icons-svelte/lib/Information.svelte';
|
||||||
import WarningIcon from 'carbon-icons-svelte/lib/Warning.svelte';
|
import WarningIcon from 'carbon-icons-svelte/lib/Warning.svelte';
|
||||||
@@ -8,11 +8,11 @@
|
|||||||
import { SNACKBAR_SEVERITY, SNACKBAR_SEVERITY_COLOR } from './constants';
|
import { SNACKBAR_SEVERITY, SNACKBAR_SEVERITY_COLOR } from './constants';
|
||||||
import { snackbarStore, snackbarStoreInitialState } from './store';
|
import { snackbarStore, snackbarStoreInitialState } from './store';
|
||||||
|
|
||||||
let severityColor;
|
let severityColor: string;
|
||||||
let timeoutId;
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
if (typeof $snackbarStore.handleClose === 'number') {
|
if (typeof $snackbarStore.handleClose === 'function') {
|
||||||
$snackbarStore.handleClose();
|
$snackbarStore.handleClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,11 +21,11 @@
|
|||||||
...snackbarStoreInitialState
|
...snackbarStoreInitialState
|
||||||
}));
|
}));
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
timeoutId = null;
|
timeoutId = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When open is true trigger autoHideDuration
|
// When open is true trigger autoHideDuration
|
||||||
function handleOpen(open) {
|
function handleOpen(open: boolean) {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export async function uploadAvatar(courseId: string, avatar: string) {
|
|||||||
export async function updateCourse(
|
export async function updateCourse(
|
||||||
courseId: Course['id'],
|
courseId: Course['id'],
|
||||||
avatar: string | undefined,
|
avatar: string | undefined,
|
||||||
course: Course
|
course: Partial<Course>
|
||||||
) {
|
) {
|
||||||
if (avatar && courseId) {
|
if (avatar && courseId) {
|
||||||
const filename = `course/${courseId + Date.now()}.webp`;
|
const filename = `course/${courseId + Date.now()}.webp`;
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { goto } from '$app/navigation';
|
|||||||
import { supabase } from '$lib/utils/functions/supabase';
|
import { supabase } from '$lib/utils/functions/supabase';
|
||||||
import { orgs, currentOrg, orgAudience, orgTeam } from '$lib/utils/store/org';
|
import { orgs, currentOrg, orgAudience, orgTeam } from '$lib/utils/store/org';
|
||||||
import { ROLE, ROLE_LABEL } from '$lib/utils/constants/roles';
|
import { ROLE, ROLE_LABEL } from '$lib/utils/constants/roles';
|
||||||
|
import type { CurrentOrg, OrgTeamMember, OrgAudience } from '$lib/utils/types/org';
|
||||||
|
|
||||||
export async function getOrgTeam(orgId) {
|
export async function getOrgTeam(orgId: string) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('organizationmember')
|
.from('organizationmember')
|
||||||
.select(
|
.select(
|
||||||
@@ -24,9 +25,22 @@ export async function getOrgTeam(orgId) {
|
|||||||
)
|
)
|
||||||
.eq('organization_id', orgId)
|
.eq('organization_id', orgId)
|
||||||
.neq('role_id', ROLE.STUDENT)
|
.neq('role_id', ROLE.STUDENT)
|
||||||
.order('id', { ascending: false });
|
.order('id', { ascending: false })
|
||||||
|
.returns<
|
||||||
|
{
|
||||||
|
id: number;
|
||||||
|
email: string;
|
||||||
|
verified: boolean;
|
||||||
|
role_id: number;
|
||||||
|
profile: {
|
||||||
|
id: string;
|
||||||
|
fullname: string;
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
}[]
|
||||||
|
>();
|
||||||
|
|
||||||
const team = [];
|
const team: OrgTeamMember[] = [];
|
||||||
if (data?.length) {
|
if (data?.length) {
|
||||||
data.forEach((teamMember) => {
|
data.forEach((teamMember) => {
|
||||||
team.push({
|
team.push({
|
||||||
@@ -49,7 +63,7 @@ export async function getOrgTeam(orgId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOrganizations(userId) {
|
export async function getOrganizations(userId: string) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('organizationmember')
|
.from('organizationmember')
|
||||||
.select(
|
.select(
|
||||||
@@ -70,9 +84,18 @@ export async function getOrganizations(userId) {
|
|||||||
`
|
`
|
||||||
)
|
)
|
||||||
.eq('profile_id', userId)
|
.eq('profile_id', userId)
|
||||||
.order('id', { ascending: false });
|
.order('id', { ascending: false })
|
||||||
|
.returns<
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
profile_id: string;
|
||||||
|
role_id: string;
|
||||||
|
created_at: string;
|
||||||
|
organization: CurrentOrg;
|
||||||
|
}[]
|
||||||
|
>();
|
||||||
|
|
||||||
const orgsArray = [];
|
const orgsArray: CurrentOrg[] = [];
|
||||||
|
|
||||||
if (Array.isArray(data) && data.length) {
|
if (Array.isArray(data) && data.length) {
|
||||||
data.forEach((orgMember) => {
|
data.forEach((orgMember) => {
|
||||||
@@ -114,7 +137,7 @@ export async function getOrganizations(userId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOrgAudience(orgId) {
|
export async function getOrgAudience(orgId: string) {
|
||||||
// get all students who are participants in any course belonging to an org
|
// get all students who are participants in any course belonging to an org
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('profile')
|
.from('profile')
|
||||||
@@ -153,7 +176,7 @@ export async function getOrgAudience(orgId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCourseBySiteName(siteName) {
|
export async function getCourseBySiteName(siteName: string) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('course')
|
.from('course')
|
||||||
.select(
|
.select(
|
||||||
@@ -176,7 +199,7 @@ export async function getCourseBySiteName(siteName) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCurrentOrg(siteName, justGet = false) {
|
export async function getCurrentOrg(siteName: string, justGet = false) {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('organization')
|
.from('organization')
|
||||||
.select(
|
.select(
|
||||||
@@ -189,16 +212,21 @@ export async function getCurrentOrg(siteName, justGet = false) {
|
|||||||
theme
|
theme
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
.eq('siteName', siteName);
|
.eq('siteName', siteName)
|
||||||
|
.returns<CurrentOrg[]>();
|
||||||
|
|
||||||
if (!justGet && (error || isEmpty(data))) {
|
const isDataEmpty = !data?.[0];
|
||||||
|
|
||||||
|
if (!justGet && (error || isDataEmpty)) {
|
||||||
console.error('Error getOrganization', error);
|
console.error('Error getOrganization', error);
|
||||||
return goto('/404');
|
return goto('/404');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!justGet) {
|
if (!justGet) {
|
||||||
|
if (isDataEmpty) return;
|
||||||
|
|
||||||
currentOrg.set(data[0]);
|
currentOrg.set(data[0]);
|
||||||
} else if (!isEmpty(data)) {
|
} else if (!isDataEmpty) {
|
||||||
return data[0];
|
return data[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { writable, derived } from 'svelte/store';
|
import { writable, derived } from 'svelte/store';
|
||||||
|
import { dev, browser } from '$app/environment';
|
||||||
import { STEPS } from '../constants/quiz';
|
import { STEPS } from '../constants/quiz';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
import type { CurrentOrg, OrgTeamMember } from '../types/org';
|
import type { CurrentOrg, OrgTeamMember, OrgAudience } from '../types/org';
|
||||||
import { ROLE } from '$lib/utils/constants/roles';
|
import { ROLE } from '$lib/utils/constants/roles';
|
||||||
import type { UserLessonDataType } from '$lib/utils/types';
|
import type { UserLessonDataType } from '$lib/utils/types';
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ export const currentOrg: Writable<CurrentOrg> = writable({
|
|||||||
landingpage: {},
|
landingpage: {},
|
||||||
theme: ''
|
theme: ''
|
||||||
});
|
});
|
||||||
export const orgAudience = writable([]);
|
export const orgAudience = writable<OrgAudience[]>([]);
|
||||||
export const orgTeam = writable<OrgTeamMember[]>([]);
|
export const orgTeam = writable<OrgTeamMember[]>([]);
|
||||||
export const isOrgAdmin = derived(
|
export const isOrgAdmin = derived(
|
||||||
currentOrg,
|
currentOrg,
|
||||||
@@ -26,6 +27,14 @@ export const isOrgAdmin = derived(
|
|||||||
export const currentOrgPath = derived(currentOrg, ($currentOrg) =>
|
export const currentOrgPath = derived(currentOrg, ($currentOrg) =>
|
||||||
$currentOrg.siteName ? `/org/${$currentOrg.siteName}` : ''
|
$currentOrg.siteName ? `/org/${$currentOrg.siteName}` : ''
|
||||||
);
|
);
|
||||||
|
export const currentOrgDomain = derived(currentOrg, ($currentOrg) => {
|
||||||
|
const browserOrigin = dev && browser && window.location.origin;
|
||||||
|
return browserOrigin
|
||||||
|
? browserOrigin
|
||||||
|
: $currentOrg.siteName
|
||||||
|
? `https://${$currentOrg.siteName}.classroomio.com`
|
||||||
|
: '';
|
||||||
|
});
|
||||||
|
|
||||||
// Quiz
|
// Quiz
|
||||||
export const createQuizModal = writable({
|
export const createQuizModal = writable({
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const user = writable<{
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const profile = writable<{
|
export const profile = writable<{
|
||||||
id: string | number | undefined;
|
id: string | undefined;
|
||||||
fullname: string;
|
fullname: string;
|
||||||
avatar_url: string;
|
avatar_url: string;
|
||||||
profile_url?: string;
|
profile_url?: string;
|
||||||
|
|||||||
@@ -59,12 +59,12 @@ export interface ExerciseSubmissions {
|
|||||||
//===========================================
|
//===========================================
|
||||||
|
|
||||||
interface CourseMetadata {
|
interface CourseMetadata {
|
||||||
requirements: string;
|
requirements?: string;
|
||||||
description: string;
|
description?: string;
|
||||||
goals: string;
|
goals?: string;
|
||||||
videoUrl?: '';
|
videoUrl?: '';
|
||||||
showDiscount: false;
|
showDiscount?: false;
|
||||||
discount: 0;
|
discount?: 0;
|
||||||
paymentLink?: string;
|
paymentLink?: string;
|
||||||
reward?: {
|
reward?: {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
@@ -188,6 +188,7 @@ export interface Course {
|
|||||||
is_present: boolean;
|
is_present: boolean;
|
||||||
id: number;
|
id: number;
|
||||||
}[];
|
}[];
|
||||||
|
lessons?: Lesson[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Groupmember {
|
export interface Groupmember {
|
||||||
|
|||||||
@@ -21,3 +21,11 @@ export interface OrgTeamMember {
|
|||||||
role: string;
|
role: string;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OrgAudience {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
avatar_url: string;
|
||||||
|
date_joined: string;
|
||||||
|
}
|
||||||
|
|||||||
+13
-11
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { fly } from 'svelte/transition';
|
||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { dev, browser } from '$app/environment';
|
import { dev, browser } from '$app/environment';
|
||||||
@@ -8,9 +9,8 @@
|
|||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import { CaptureConsole } from '@sentry/integrations';
|
import { CaptureConsole } from '@sentry/integrations';
|
||||||
import posthog from 'posthog-js';
|
import posthog from 'posthog-js';
|
||||||
import { Theme } from 'carbon-components-svelte';
|
import { Theme, ToastNotification, Loading } 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 { Moon } from 'svelte-loading-spinners';
|
|
||||||
import LandingNavigation from '$lib/components/Navigation/index.svelte';
|
import LandingNavigation from '$lib/components/Navigation/index.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';
|
||||||
@@ -19,8 +19,6 @@
|
|||||||
import Backdrop from '$lib/components/Backdrop/index.svelte';
|
import Backdrop from '$lib/components/Backdrop/index.svelte';
|
||||||
import OrgSideBar from '$lib/components/Org/SideBar.svelte';
|
import OrgSideBar from '$lib/components/Org/SideBar.svelte';
|
||||||
import LMSSideBar from '$lib/components/LMS/SideBar.svelte';
|
import LMSSideBar from '$lib/components/LMS/SideBar.svelte';
|
||||||
// import SideBar from "../components/SideBar/index.svelte";
|
|
||||||
// import Footer from '$lib/components/Footer/index.svelte';
|
|
||||||
import Apps from '$lib/components/Apps/index.svelte';
|
import Apps from '$lib/components/Apps/index.svelte';
|
||||||
import PlayQuiz from '$lib/components/Org/Quiz/Play/index.svelte';
|
import PlayQuiz from '$lib/components/Org/Quiz/Play/index.svelte';
|
||||||
import { course } from '$lib/components/Course/store';
|
import { course } from '$lib/components/Course/store';
|
||||||
@@ -177,7 +175,7 @@
|
|||||||
// Set user in sentry
|
// Set user in sentry
|
||||||
setSentryUser();
|
setSentryUser();
|
||||||
|
|
||||||
const orgRes = await getOrganizations($profile.id);
|
const orgRes = await getOrganizations(profileData.id);
|
||||||
|
|
||||||
// student redirect
|
// student redirect
|
||||||
if (data.isOrgSite) {
|
if (data.isOrgSite) {
|
||||||
@@ -305,8 +303,16 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<main class="dark:bg-black">
|
<main class="dark:bg-black">
|
||||||
{#if $navigating && $delayedPreloading}
|
{#if $navigating && $delayedPreloading}
|
||||||
<Backdrop>
|
<Backdrop disableCenteredContent={true} className="opacity-90">
|
||||||
<Moon size="60" color="#1d4ed8" unit="px" duration="1s" />
|
<div class="h-full w-full relative" transition:fly={{ x: -200, duration: 500 }}>
|
||||||
|
<ToastNotification kind="info-square" class="absolute bottom-5 left-5">
|
||||||
|
<span slot="title" class="flex items-center">
|
||||||
|
<span class="mr-2">Redirecting</span>
|
||||||
|
<Loading withOverlay={false} small />
|
||||||
|
</span>
|
||||||
|
<span slot="caption">Taking you to the next page, please wait.</span>
|
||||||
|
</ToastNotification>
|
||||||
|
</div>
|
||||||
</Backdrop>
|
</Backdrop>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !hideNavByRoute($page.url.pathname)}
|
{#if !hideNavByRoute($page.url.pathname)}
|
||||||
@@ -351,10 +357,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{#if !['about', ''].includes(path)}
|
|
||||||
<!-- <Footer /> -->
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import CourseLandingPage from '$lib/components/CourseLandingPage/index.svelte';
|
import CourseLandingPage from '$lib/components/CourseLandingPage/index.svelte';
|
||||||
import Editor from '$lib/components/CourseLandingPage/components/Editor/index.svelte';
|
import Editor from '$lib/components/CourseLandingPage/components/Editor/index.svelte';
|
||||||
import { course } from '$lib/components/Course/store';
|
import { course } from '$lib/components/Course/store';
|
||||||
import { lessons } from '$lib/components/Course/components/Lesson/store/lessons';
|
import { lessons } from '$lib/components/Course/components/Lesson/store/lessons';
|
||||||
|
import type { Course, Lesson } from '$lib/utils/types';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
const { courseId } = data;
|
const { courseId } = data;
|
||||||
|
|
||||||
let courseData = {};
|
let courseData: Course;
|
||||||
|
|
||||||
function setCourseData(course = {}, lessons) {
|
function setCourseData(course: Course, lessons: Lesson[]) {
|
||||||
courseData = { ...course, lessons };
|
courseData = { ...course, lessons };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
triggerSendEmail,
|
triggerSendEmail,
|
||||||
NOTIFICATION_NAME
|
NOTIFICATION_NAME
|
||||||
} from '$lib/utils/services/notification/notification';
|
} from '$lib/utils/services/notification/notification';
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg, currentOrgDomain } from '$lib/utils/store/org';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
type items = {
|
type items = {
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
|
|
||||||
const { fullname, email } = submissionData?.student;
|
const { fullname, email } = submissionData?.student;
|
||||||
const { title, status_id }: { title: string; status_id: number } = submissionData;
|
const { title, status_id }: { title: string; status_id: number } = submissionData;
|
||||||
const exerciseLink = `${$currentOrg.siteName}.classroomio.com/courses/${courseId}/lessons/${lessonDetails.id}/exercises/${exerciseDetails.id}`;
|
const exerciseLink = `${$currentOrgDomain}/courses/${courseId}/lessons/${lessonDetails.id}/exercises/${exerciseDetails.id}`;
|
||||||
|
|
||||||
const content = `
|
const content = `
|
||||||
<p>Hello ${fullname},</p>
|
<p>Hello ${fullname},</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user