From 8ab51095a7390bb912922021824f347af835deab Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Mon, 9 Sep 2024 01:14:55 +0100 Subject: [PATCH] fix: course and org landing page improvements (#468) * make gift section HTML rendered * hide classroomio branding on landing page * improve section expanding * feat: toggle show sections on course landing page * fix: show discount on landing page course card --- .../components/NewsFeed/NewsFeedCard.svelte | 10 +- .../components/Editor/CertificateForm.svelte | 80 +++++++ .../components/Editor/DescriptionForm.svelte | 50 +++- .../components/Editor/GoalsForm.svelte | 50 +++- .../components/Editor/InstructorForm.svelte | 17 +- .../components/Editor/RequirementForm.svelte | 48 +++- .../components/Editor/index.svelte | 23 +- .../components/PricingSection.svelte | 23 +- .../components/SectionsDisplay.svelte | 47 ++-- .../components/CourseLandingPage/constants.ts | 21 +- .../components/CourseLandingPage/index.svelte | 215 ++++++++---------- .../lib/components/CourseLandingPage/utils.ts | 45 ++++ .../Courses/components/Card/index.svelte | 33 ++- .../components/Org/LandingPage/index.svelte | 26 ++- .../lib/components/UploadImage/index.svelte | 10 +- .../src/lib/utils/functions/course.ts | 7 + apps/dashboard/src/lib/utils/store/org.ts | 10 +- .../src/lib/utils/translations/de.json | 4 +- .../src/lib/utils/translations/en.json | 10 +- .../src/lib/utils/translations/es.json | 4 +- .../src/lib/utils/translations/fr.json | 4 +- .../src/lib/utils/translations/pt.json | 4 +- .../src/lib/utils/translations/ru.json | 4 +- .../src/lib/utils/translations/vi.json | 4 +- apps/dashboard/src/lib/utils/types/index.ts | 4 + apps/dashboard/src/routes/+layout.svelte | 2 +- ...907090948_open_orgplan_for_landingpage.sql | 8 + 27 files changed, 531 insertions(+), 232 deletions(-) create mode 100644 apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/CertificateForm.svelte create mode 100644 apps/dashboard/src/lib/components/CourseLandingPage/utils.ts create mode 100644 supabase/migrations/20240907090948_open_orgplan_for_landingpage.sql diff --git a/apps/dashboard/src/lib/components/Course/components/NewsFeed/NewsFeedCard.svelte b/apps/dashboard/src/lib/components/Course/components/NewsFeed/NewsFeedCard.svelte index c848e8a3..51c60723 100644 --- a/apps/dashboard/src/lib/components/Course/components/NewsFeed/NewsFeedCard.svelte +++ b/apps/dashboard/src/lib/components/Course/components/NewsFeed/NewsFeedCard.svelte @@ -30,7 +30,9 @@ let comment = ''; let areCommentsExpanded = false; let isDeleteFeedModal = false; - let errors = {}; + let errors: { + newComment: string; + }; let reactions = { smile: '😀', @@ -58,7 +60,7 @@ const handleAddNewComment = (event) => { if (event.key === 'Enter' || event.type === 'click') { - errors = addNewsfeedCommentValidation(comment); + errors = addNewsfeedCommentValidation(comment) as typeof errors; if (Object.keys(errors).length) { return; } @@ -230,8 +232,8 @@ - {#if errors.newComment} -

{errors.newComment}

+ {#if errors?.newComment} +

{errors?.newComment}

{/if} diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/CertificateForm.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/CertificateForm.svelte new file mode 100644 index 00000000..f42b2b27 --- /dev/null +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/CertificateForm.svelte @@ -0,0 +1,80 @@ + + +
+

+ {$t('course.navItem.landing_page.editor.certificate_form.upload_template')} +

+ +
+ +
+

+ {$t('course.navItem.landing_page.editor.display_section')} +

+ + + {$t('settings.landing_page.hide_section')} + {$t('settings.landing_page.show_section')} + +
diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/DescriptionForm.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/DescriptionForm.svelte index d22895e6..1f754b5d 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/DescriptionForm.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/DescriptionForm.svelte @@ -1,9 +1,51 @@ - -
- +
+ +
+ +
+

+ {$t('course.navItem.landing_page.editor.display_section')} +

+ + + {$t('settings.landing_page.hide_section')} + {$t('settings.landing_page.show_section')} +
diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/GoalsForm.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/GoalsForm.svelte index d22895e6..1fe7e99b 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/GoalsForm.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/GoalsForm.svelte @@ -1,9 +1,51 @@ - -
- +
+ +
+ +
+

+ {$t('course.navItem.landing_page.editor.display_section')} +

+ + + {$t('settings.landing_page.hide_section')} + {$t('settings.landing_page.show_section')} +
diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/InstructorForm.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/InstructorForm.svelte index b59ebd1e..f4183dba 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/InstructorForm.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/InstructorForm.svelte @@ -11,6 +11,7 @@ import { t } from '$lib/utils/functions/translations'; export let course: Course; + let name: string | undefined; let role: string | undefined; let imgUrl: string | undefined; @@ -18,6 +19,7 @@ let courseNo: string | undefined; let avatar: string | undefined; let hasBeenSet = false; + let isUploading = false; function setter(value: any, setterKey: string) { if (!value) return; @@ -30,12 +32,13 @@ async function onAvatarChange(_avatar: string | undefined) { if (!_avatar || !course.id) return; - + isUploading = true; const logo = await uploadAvatar(course.id, _avatar); if (!logo) return; imgUrl = logo; + isUploading = false; } function setDefaults(course: Course) { @@ -60,11 +63,13 @@ $: setDefaults(course); -
- - +
+ +
+ +
import TextEditor from '$lib/components/TextEditor/index.svelte'; + import cloneDeep from 'lodash/cloneDeep'; + import set from 'lodash/set'; + import get from 'lodash/get'; + import isEmpty from 'lodash/isEmpty'; + import { Toggle } from 'carbon-components-svelte'; + import type { Course } from '$lib/utils/types'; + import { t } from '$lib/utils/functions/translations'; + import { NAV_ITEM_KEY } from '../../constants'; - export let value: string | undefined; + export let course: Course; + + let show: boolean; + let hasBeenSet = false; + + function setter(value: any, setterKey: string) { + if (typeof value === 'undefined') return; + + const _course = cloneDeep(course); + set(_course, setterKey, value); + + course = _course; + } + + function setDefaults(course: Course) { + if (isEmpty(course) || hasBeenSet) return; + + hasBeenSet = true; + + show = get(course, `metadata.sectionDisplay.${NAV_ITEM_KEY.REQUIREMENT}`) ?? true; + } + + $: setter(show, `metadata.sectionDisplay.${NAV_ITEM_KEY.REQUIREMENT}`); + $: setDefaults(course); -
- +
+ +
+ +
+

+ {$t('course.navItem.landing_page.editor.display_section')} +

+ + + {$t('settings.landing_page.hide_section')} + {$t('settings.landing_page.show_section')} +
diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/index.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/index.svelte index 9fc573da..fef2ccdf 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/index.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/Editor/index.svelte @@ -16,6 +16,7 @@ import PricingForm from './PricingForm.svelte'; import GoalsForm from './GoalsForm.svelte'; import ReviewsForm from './ReviewsForm.svelte'; + import CertificateForm from './CertificateForm.svelte'; import InstructorForm from './InstructorForm.svelte'; import PrimaryButton from '$lib/components/PrimaryButton/index.svelte'; import { VARIANTS } from '$lib/components/PrimaryButton/constants'; @@ -73,16 +74,21 @@ { key: 5, path: '', - title: $t('course.navItem.landing_page.editor.title.reviews') + title: $t('course.navItem.landing_page.editor.title.certificate') }, { key: 6, path: '', - title: $t('course.navItem.landing_page.editor.title.instructor') + title: $t('course.navItem.landing_page.editor.title.reviews') }, { key: 7, path: '', + title: $t('course.navItem.landing_page.editor.title.instructor') + }, + { + key: 8, + path: '', title: $t('course.navItem.landing_page.editor.title.pricing') } ]; @@ -123,6 +129,7 @@ attendance: undefined, group: undefined, lessons: undefined, + lesson_section: undefined, polls: undefined, slug: course.slug }); @@ -228,16 +235,18 @@ {#if selectedSection.key === 1} {:else if selectedSection.key === 2} - + {:else if selectedSection.key === 3} - + {:else if selectedSection.key === 4} - + {:else if selectedSection.key === 5} - + {:else if selectedSection.key === 6} - + {:else if selectedSection.key === 7} + + {:else if selectedSection.key === 8} {/if}
diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/PricingSection.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/PricingSection.svelte index c9d461b9..14a650ef 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/components/PricingSection.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/PricingSection.svelte @@ -6,11 +6,13 @@ import { getStudentInviteLink } from '$lib/utils/functions/course'; import { currentOrg, currentOrgDomain } from '$lib/utils/store/org'; import { goto } from '$app/navigation'; + import HtmlRender from '$lib/components/HTMLRender/HTMLRender.svelte'; import PaymentModal from './PaymentModal.svelte'; import type { Course } from '$lib/utils/types'; import { ROLE } from '$lib/utils/constants/roles'; import { capturePosthogEvent } from '$lib/utils/services/posthog'; import { t } from '$lib/utils/functions/translations'; + import { calcCourseDiscount } from '$lib/utils/functions/course'; export let className = ''; export let editMode = false; @@ -24,13 +26,6 @@ let formatter: Intl.NumberFormat | undefined; let isFree = false; - function calcDisc(percent: number, cost: number, showDiscount: boolean) { - if (!percent || !showDiscount) return cost; - const discountAmount = (percent / 100) * cost; - const discountedPrice = cost - discountAmount; - return Math.round(discountedPrice); - } - function handleJoinCourse() { if (editMode) return; @@ -64,7 +59,11 @@ $: setFormatter(courseData.currency); $: discount = get(courseData, 'metadata.discount', 0); - $: calculatedCost = calcDisc(discount, courseData.cost || 0, !!courseData.metadata.showDiscount); + $: calculatedCost = calcCourseDiscount( + discount, + courseData.cost || 0, + !!courseData.metadata.showDiscount + ); $: isFree = isCourseFree(calculatedCost); $: startCoursePayment && handleJoinCourse(); @@ -101,9 +100,9 @@ {#if courseData?.metadata?.showDiscount}

{discount}% {$t('course.navItem.landing_page.pricing_section.discount')}. - {formatter?.format(courseData?.cost || 0) || courseData.cost} + + {formatter?.format(courseData?.cost || 0) || courseData.cost} +

{/if} {:else} @@ -180,7 +179,7 @@ {#if courseData?.metadata?.reward?.show}
- {@html get(courseData, 'metadata.reward.description', '')} +
{/if} diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/components/SectionsDisplay.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/components/SectionsDisplay.svelte index ace1f5a0..bdd3c25e 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/components/SectionsDisplay.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/components/SectionsDisplay.svelte @@ -1,46 +1,41 @@
-
+
+ {#if expand} + + {:else} + + {/if} + {title} + + + {pluralize($t('course.navItem.landing_page.lessons'), lessonCount, true)}, + {pluralize($t('course.navItem.landing_page.exercises'), exerciseCount, true)} + + - - {#if showLessons[index]} + {#if expand} {#each lessons as lesson} -
+
{lesson.title}
diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/constants.ts b/apps/dashboard/src/lib/components/CourseLandingPage/constants.ts index 14c505ed..cc393789 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/constants.ts +++ b/apps/dashboard/src/lib/components/CourseLandingPage/constants.ts @@ -1,10 +1,9 @@ -// import { t } from '$lib/utils/functions/translations'; - -export const SECTION_KEYS = { +export const NAV_ITEM_KEY = { HEADER: 'header', REQUIREMENT: '#requirement', DESCRIPTION: '#description', GOALS: '#goals', + CERTIFICATE: '#certificate', LESSONS: '#lessons', REVIEWS: '#reviews', INSTRUCTOR: '#instructor' @@ -12,27 +11,31 @@ export const SECTION_KEYS = { export const NAV_ITEMS = [ { - key: SECTION_KEYS.REQUIREMENT, + key: NAV_ITEM_KEY.REQUIREMENT, label: 'course.navItem.landing_page.editor.title.requirement' }, { - key: SECTION_KEYS.DESCRIPTION, + key: NAV_ITEM_KEY.DESCRIPTION, label: 'course.navItem.landing_page.editor.title.description' }, { - key: SECTION_KEYS.GOALS, + key: NAV_ITEM_KEY.GOALS, label: 'course.navItem.landing_page.editor.title.goals' }, { - key: SECTION_KEYS.LESSONS, + key: NAV_ITEM_KEY.CERTIFICATE, + label: 'course.navItem.landing_page.editor.title.certificate' + }, + { + key: NAV_ITEM_KEY.LESSONS, label: 'course.navItems.nav_content' }, { - key: SECTION_KEYS.REVIEWS, + key: NAV_ITEM_KEY.REVIEWS, label: 'course.navItem.landing_page.editor.title.reviews' }, { - key: SECTION_KEYS.INSTRUCTOR, + key: NAV_ITEM_KEY.INSTRUCTOR, label: 'course.navItem.landing_page.editor.title.instructor' } ]; diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/index.svelte b/apps/dashboard/src/lib/components/CourseLandingPage/index.svelte index 7423dc41..4d466911 100644 --- a/apps/dashboard/src/lib/components/CourseLandingPage/index.svelte +++ b/apps/dashboard/src/lib/components/CourseLandingPage/index.svelte @@ -1,14 +1,12 @@ {#if !editMode} @@ -214,6 +197,7 @@ {/if}
+ {:else if $course.version === COURSE_VERSION.V2} -
+

{$t('course.navItem.landing_page.course_content')}

- {lesson_sections?.length} - {$t('course.navItem.landing_page.modules')}, {totalLessons} - {$t('course.navItem.landing_page.lessons')} + + {pluralize($t('course.navItem.landing_page.modules'), lessonSections.length, true)}, + {pluralize($t('course.navItem.landing_page.lessons'), totalLessons, true)} +
- {#each lesson_sections as sections, index} + {#each lessonSections as section} total + (lesson.totalExercises?.[0]?.count || 0), - 0 - )} - lessons={sections.lessons} + exerciseCount={getExerciseCount(section.lessons)} + lessonCount={section.lessons?.length} + lessons={section.lessons} + title={section.title} /> {/each}
{/if} - {#if reviews && reviews.length > 0} -
+ {#if navItemKeys.includes(NAV_ITEM_KEY.REVIEWS)} +

{$t('course.navItem.landing_page.reviews')}

@@ -554,6 +527,10 @@ background-color: rgba(0, 0, 0, 0.5); } + :global(.certificate-img) { + width: unset !important; + } + .active { position: relative; display: inline-block; diff --git a/apps/dashboard/src/lib/components/CourseLandingPage/utils.ts b/apps/dashboard/src/lib/components/CourseLandingPage/utils.ts new file mode 100644 index 00000000..c24fc555 --- /dev/null +++ b/apps/dashboard/src/lib/components/CourseLandingPage/utils.ts @@ -0,0 +1,45 @@ +import { NAV_ITEMS, NAV_ITEM_KEY } from './constants'; +import type { Course, Review } from '$lib/utils/types'; +import get from 'lodash/get'; + +export function getExerciseCount(lessons) { + return lessons.reduce((total, lesson) => total + (lesson.totalExercises?.[0]?.count || 0), 0); +} + +export function getLessonSections(data) { + const sections = + data?.lesson_section?.map((section) => { + return { + ...section, + lessons: data.lessons.filter((lesson) => lesson.section_id === section.id) + }; + }) || []; + + return sections || []; +} + +export function getTotalLessons(sections) { + return sections.reduce((total, section) => { + return total + section.lessons.length; + }, 0); +} + +export function filterNavItems(course: Course, reviews: Review[]) { + const rules = get(course, 'metadata.sectionDisplay', {}); + + return NAV_ITEMS.filter((item) => { + const key = item.key; + + if (typeof rules[key] === 'boolean') { + return !!rules[key]; + } + + if (key === NAV_ITEM_KEY.REVIEWS) { + // Show only if there are reviews added + return reviews.length > 0; + } + + // In case they are undefined + return true; + }); +} diff --git a/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte b/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte index 9fbba218..a8d40944 100644 --- a/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte +++ b/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte @@ -18,6 +18,7 @@ import RadioButtonChecked from 'carbon-icons-svelte/lib/RadioButtonChecked.svelte'; import GrowthIcon from 'carbon-icons-svelte/lib/Growth.svelte'; import UserProfileIcon from 'carbon-icons-svelte/lib/UserProfile.svelte'; + import { calcCourseDiscount } from '$lib/utils/functions/course'; export let bannerImage: string | undefined; export let id = ''; @@ -25,7 +26,6 @@ export let title = ''; export let description = ''; export let isPublished = false; - export let cost = 0; export let totalLessons = 0; export let totalStudents = 0; export let currency = 'USD'; @@ -34,6 +34,14 @@ export let isExplore = false; export let progressRate = 45; export let type: COURSE_TYPE; + export let pricingData: { + cost: number; + currency?: string; + showDiscount?: boolean; + discount?: number; + } = { + cost: 0 + }; let target: any; @@ -92,6 +100,12 @@ icon: GrowthIcon } }; + + $: cost = calcCourseDiscount( + pricingData.discount, + pricingData.cost ?? 0, + !!pricingData.showDiscount + ); {#if !isLMS && !isOnLandingPage} @@ -169,11 +183,18 @@

{#if isOnLandingPage} - {!cost - ? $t('course.navItem.landing_page.pricing_section.free') - : formatter.format(cost)} + + {#if !cost} + $t('course.navItem.landing_page.pricing_section.free') + {:else if pricingData.showDiscount} + {formatter.format(cost)} + + {formatter?.format(pricingData?.cost)} + + {:else} + {formatter.format(cost)} + {/if} + {:else if isLMS} {#if !isExplore}

diff --git a/apps/dashboard/src/lib/components/Org/LandingPage/index.svelte b/apps/dashboard/src/lib/components/Org/LandingPage/index.svelte index d031c623..1bc144f0 100644 --- a/apps/dashboard/src/lib/components/Org/LandingPage/index.svelte +++ b/apps/dashboard/src/lib/components/Org/LandingPage/index.svelte @@ -1,7 +1,6 @@ - {!org.name ? '' : `${org.name}'s `}{$t('course.navItem.landing_page.landing_page')} + {!org?.name ? '' : `${org.name}'s `}{$t('course.navItem.landing_page.landing_page')} -{#if !org.landingpage} +{#if !org?.landingpage} {:else}
@@ -312,7 +312,12 @@ type={courseData.type} description={courseData.description} isPublished={courseData.is_published} - cost={courseData.cost} + pricingData={{ + cost: courseData.cost, + discount: courseData.metadata.discount, + showDiscount: courseData.metadata.showDiscount, + currency: courseData.currency + }} currency={courseData.currency} totalLessons={get(courseData, 'lessons[0].count', 0)} isOnLandingPage={true} @@ -442,8 +447,7 @@ label={$t('course.navItem.landing_page.message')} bind:value={contact.message} errorMessage={contactError.message} - rows="9" - maxRows={15} + rows={9} placeholder={$t('course.navItem.landing_page.your_message')} />
diff --git a/apps/dashboard/src/lib/components/UploadImage/index.svelte b/apps/dashboard/src/lib/components/UploadImage/index.svelte index b62849b9..41fbcb55 100644 --- a/apps/dashboard/src/lib/components/UploadImage/index.svelte +++ b/apps/dashboard/src/lib/components/UploadImage/index.svelte @@ -1,5 +1,6 @@
@@ -63,7 +67,11 @@ }} disabled={isDisabled} > - + {#if isUploading} + + {:else} + + {/if} {$t('settings.profile.profile_picture.upload_image')}

diff --git a/apps/dashboard/src/lib/utils/functions/course.ts b/apps/dashboard/src/lib/utils/functions/course.ts index 2730eacd..2a5d7b33 100644 --- a/apps/dashboard/src/lib/utils/functions/course.ts +++ b/apps/dashboard/src/lib/utils/functions/course.ts @@ -29,3 +29,10 @@ export function replaceHTMLTag(text: string) { .map((char) => tagsToReplace[char] || char) .join(''); } + +export function calcCourseDiscount(percent = 0, cost: number, showDiscount: boolean) { + if (!percent || !showDiscount) return cost; + const discountAmount = (percent / 100) * cost; + const discountedPrice = cost - discountAmount; + return Math.round(discountedPrice); +} diff --git a/apps/dashboard/src/lib/utils/store/org.ts b/apps/dashboard/src/lib/utils/store/org.ts index 0fc379b5..a3aba642 100644 --- a/apps/dashboard/src/lib/utils/store/org.ts +++ b/apps/dashboard/src/lib/utils/store/org.ts @@ -51,11 +51,11 @@ export const currentOrgDomain = derived(currentOrg, ($currentOrg) => { : ''; }); -// Utility org store -export const isFreePlan = derived( - currentOrgPlan, - ($plan) => !$plan || $plan.plan_name === PLAN.BASIC -); +export const isFreePlan = derived(currentOrgPlan, ($plan) => { + if (!$plan) return false; + + return !$plan || $plan.plan_name === PLAN.BASIC; +}); export const currentOrgMaxAudience = derived(currentOrgPlan, ($plan) => !$plan ? 20 diff --git a/apps/dashboard/src/lib/utils/translations/de.json b/apps/dashboard/src/lib/utils/translations/de.json index fc433ca6..e8d6fc5d 100644 --- a/apps/dashboard/src/lib/utils/translations/de.json +++ b/apps/dashboard/src/lib/utils/translations/de.json @@ -395,7 +395,7 @@ } }, "course_content": "Kursinhalte", - "modules": "Module", + "modules": "module", "lessons": "Unterricht", "exercises": "Übungen" }, @@ -1430,4 +1430,4 @@ "no": "NEIN", "yes": "Ja" } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/lib/utils/translations/en.json b/apps/dashboard/src/lib/utils/translations/en.json index e5a1dc50..fbac4217 100644 --- a/apps/dashboard/src/lib/utils/translations/en.json +++ b/apps/dashboard/src/lib/utils/translations/en.json @@ -923,12 +923,14 @@ "goals": "Goals", "reviews": "Reviews", "instructor": "Instructor", - "pricing": "Pricing" + "pricing": "Pricing", + "certificate": "Certificate" }, "save": "Save", "page_builder": "Page Builder", "section": "section", + "display_section": "Display section", "header_form": { "title": "Title", @@ -962,6 +964,10 @@ } }, + "certificate_form": { + "upload_template": "Certificate template" + }, + "instructor_form": { "upload": "Instructor Logo", "name": "Instructor name", @@ -1002,7 +1008,7 @@ }, "course_content": "Course content", - "modules": "Modules", + "modules": "modules", "lessons": "lessons", "exercises": "exercises", "start_course": "Start Course", diff --git a/apps/dashboard/src/lib/utils/translations/es.json b/apps/dashboard/src/lib/utils/translations/es.json index a5299b95..d59fe673 100644 --- a/apps/dashboard/src/lib/utils/translations/es.json +++ b/apps/dashboard/src/lib/utils/translations/es.json @@ -395,7 +395,7 @@ } }, "course_content": "Contenido del curso", - "modules": "Módulos", + "modules": "módulos", "lessons": "lecciones", "exercises": "ceremonias" }, @@ -1430,4 +1430,4 @@ "no": "No", "yes": "Sí" } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/lib/utils/translations/fr.json b/apps/dashboard/src/lib/utils/translations/fr.json index ac26ba4c..74c07b2c 100644 --- a/apps/dashboard/src/lib/utils/translations/fr.json +++ b/apps/dashboard/src/lib/utils/translations/fr.json @@ -395,7 +395,7 @@ } }, "course_content": "Contenu du cours", - "modules": "Modules", + "modules": "modules", "lessons": "leçons", "exercises": "exercices" }, @@ -1430,4 +1430,4 @@ "no": "Non", "yes": "Oui" } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/lib/utils/translations/pt.json b/apps/dashboard/src/lib/utils/translations/pt.json index 1851bce9..9dc1da8a 100644 --- a/apps/dashboard/src/lib/utils/translations/pt.json +++ b/apps/dashboard/src/lib/utils/translations/pt.json @@ -395,7 +395,7 @@ } }, "course_content": "Conteúdo do curso", - "modules": "Módulos", + "modules": "módulos", "lessons": "lições", "exercises": "exercícios" }, @@ -1430,4 +1430,4 @@ "no": "Não", "yes": "Sim" } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/lib/utils/translations/ru.json b/apps/dashboard/src/lib/utils/translations/ru.json index 548b5d4a..c003c271 100644 --- a/apps/dashboard/src/lib/utils/translations/ru.json +++ b/apps/dashboard/src/lib/utils/translations/ru.json @@ -395,7 +395,7 @@ } }, "course_content": "Содержание курса", - "modules": "Модули", + "modules": "mодули", "lessons": "уроки", "exercises": "упражнения" }, @@ -1430,4 +1430,4 @@ "no": "Нет", "yes": "Да" } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/lib/utils/translations/vi.json b/apps/dashboard/src/lib/utils/translations/vi.json index c7d17be6..0432279b 100644 --- a/apps/dashboard/src/lib/utils/translations/vi.json +++ b/apps/dashboard/src/lib/utils/translations/vi.json @@ -395,7 +395,7 @@ } }, "course_content": "Nội dung khóa học", - "modules": "Mô-đun", + "modules": "mô-đun", "lessons": "bài học", "exercises": "bài tập" }, @@ -1430,4 +1430,4 @@ "no": "KHÔNG", "yes": "Đúng" } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/lib/utils/types/index.ts b/apps/dashboard/src/lib/utils/types/index.ts index 55c22250..768b0a08 100644 --- a/apps/dashboard/src/lib/utils/types/index.ts +++ b/apps/dashboard/src/lib/utils/types/index.ts @@ -97,11 +97,15 @@ interface CourseMetadata { description: string; imgUrl: string; }; + certificate?: { + templateUrl: string; + }; reviews?: Array; lessonTabsOrder?: Array; grading?: boolean; lessonDownload?: boolean; allowNewStudent: boolean; + sectionDisplay?: Record; } export interface LessonCommentInsertPayload { diff --git a/apps/dashboard/src/routes/+layout.svelte b/apps/dashboard/src/routes/+layout.svelte index 5f01c70b..9aa4e871 100644 --- a/apps/dashboard/src/routes/+layout.svelte +++ b/apps/dashboard/src/routes/+layout.svelte @@ -307,7 +307,7 @@ {:else if data.skipAuth} {:else if data.isOrgSite && !path} - + {:else}

{#if $navigating && $delayedPreloading} diff --git a/supabase/migrations/20240907090948_open_orgplan_for_landingpage.sql b/supabase/migrations/20240907090948_open_orgplan_for_landingpage.sql new file mode 100644 index 00000000..1c9b6ab4 --- /dev/null +++ b/supabase/migrations/20240907090948_open_orgplan_for_landingpage.sql @@ -0,0 +1,8 @@ +drop policy "User must be an org member to SELECT" on "public"."organization_plan"; + +create policy "Enable read access for all users" +on "public"."organization_plan" +as permissive +for select +to public +using (true);