CLASSROOMIO-T-32: Move landing page builder to course route

This commit is contained in:
Rotimi Best
2022-01-10 00:18:08 +02:00
parent 7d7d68594b
commit 457fbd2902
14 changed files with 374 additions and 111 deletions
+8 -2
View File
@@ -1,12 +1,18 @@
<script>
import Close24 from 'carbon-icons-svelte/lib/Close24';
import Close16 from 'carbon-icons-svelte/lib/Close16';
import IconButton from '../../IconButton/index.svelte';
export let onClick;
export let contained;
export let color = '';
export let size = 'large';
</script>
<IconButton {onClick} {color} {contained}>
<Close24 class="carbon-icon" title="Close" />
<IconButton {onClick} {color} {contained} {size}>
{#if size === 'large'}
<Close24 class="carbon-icon" title="Delete" />
{:else}
<Close16 class="carbon-icon" title="Delete" />
{/if}
</IconButton>
@@ -124,6 +124,14 @@
to: getNavItemRoute($course.id, 'people'),
hideSortIcon: true,
},
{
label: 'Landing Page',
to: getNavItemRoute($course.id, 'landingpage'),
hideSortIcon: true,
show() {
return !isStudent;
},
},
];
}
</script>
@@ -0,0 +1,19 @@
<script>
import TextField from '../../../Form/TextField.svelte';
import EditContent from '../../../EditContent/index.svelte';
import { landingPageStore } from '../../store';
</script>
<TextField
label="Company name"
bind:value={$landingPageStore.aboutUs.companyName}
className="mt-3 font-bold"
/>
<TextField
label="Company Logo URL"
bind:value={$landingPageStore.aboutUs.logoUrl}
className="mt-3 font-bold"
/>
<p class="mt-3 font-bold">Description</p>
<EditContent bind:value={$landingPageStore.aboutUs.description} />
@@ -0,0 +1,6 @@
<script>
import EditContent from '../../../EditContent/index.svelte';
import { landingPageStore } from '../../store';
</script>
<EditContent bind:value={$landingPageStore.description.content} />
@@ -0,0 +1,38 @@
<script>
import TextArea from '../../../Form/TextArea.svelte';
import TextField from '../../../Form/TextField.svelte';
import { landingPageStore } from '../../store';
</script>
<TextArea
label="Title"
bind:value={$landingPageStore.header.title}
rows="3"
className="mt-3 font-bold"
bgColor="bg-white"
/>
<TextArea
label="Description"
bind:value={$landingPageStore.header.description}
rows="6"
className="mt-3 font-bold"
bgColor="bg-white"
/>
<TextField
className="mt-3 font-bold"
label="Currency"
bind:value={$landingPageStore.header.currency}
/>
<TextField
className="mt-3 font-bold"
label="Cost"
bind:value={$landingPageStore.header.cost}
/>
<TextField
label="Button Label"
className="mt-3 font-bold"
bind:value={$landingPageStore.header.buttonLabel}
/>
@@ -0,0 +1,6 @@
<script>
import EditContent from '../../../EditContent/index.svelte';
import { landingPageStore } from '../../store';
</script>
<EditContent bind:value={$landingPageStore.requirement.content} />
@@ -0,0 +1,61 @@
<script>
import TextField from '../../../Form/TextField.svelte';
import PrimaryButton from '../../../PrimaryButton/index.svelte';
import CloseButton from '../../../Buttons/Close/index.svelte';
import { VARIANTS } from '../../../PrimaryButton/constants';
import { landingPageStore } from '../../store';
function handleAdd() {
landingPageStore.update((store) => {
const { syllabus } = store;
const lessons = [
...syllabus.lessons,
{
title: '',
},
];
return {
...store,
syllabus: {
lessons,
},
};
});
}
function handleDelete(index) {
landingPageStore.update((store) => {
const { syllabus } = store;
return {
...store,
syllabus: {
lessons: syllabus.lessons.filter(
(lesson, lessonIndex) => index !== lessonIndex
),
},
};
});
}
</script>
<h4 class="mb-3">Lessons</h4>
{#each $landingPageStore.syllabus.lessons as lesson, index}
<div class="mb-4 relative">
<TextField bind:value={lesson.title} />
<div class="absolute -top-3 -right-3">
<CloseButton
onClick={() => handleDelete(index)}
contained={true}
size="small"
/>
</div>
</div>
{/each}
<div class="mt-3">
<PrimaryButton label="Add" onClick={handleAdd} variant={VARIANTS.OUTLINED} />
</div>
@@ -1,38 +1,109 @@
<script>
import { goto } from '@sapper/app';
import ChevronRight24 from 'carbon-icons-svelte/lib/ChevronRight24';
import ArrowLeft24 from 'carbon-icons-svelte/lib/ArrowLeft24';
import Container from './Container.svelte';
import Expandable from '../../../Expandable/index.svelte';
import IconButton from '../../../IconButton/index.svelte';
import CloseButton from '../../../Buttons/Close/index.svelte';
import HeaderForm from './HeaderForm.svelte';
import RequirementForm from './RequirementForm.svelte';
import DescriptionForm from './DescriptionForm.svelte';
import SyllabusForm from './SyllabusForm.svelte';
import AboutUsForm from './AboutUsForm.svelte';
import PrimaryButton from '../../../PrimaryButton/index.svelte';
export let courseId;
let borderBottomGrey = 'border-r-0 border-b border-l-0 border-gray-300';
const sections = [
{
key: 1,
title: 'Header',
},
{
key: 2,
title: 'Requirement',
},
{
key: 3,
title: 'Description',
},
{
key: 4,
title: 'Syllabus',
},
{
key: 5,
title: 'About Us',
},
];
let selectedSection = null;
function handleClose() {
console.log('closing');
if (!selectedSection) {
goto(`/courses/${courseId}`);
}
selectedSection = null;
}
function handleSectionSelect(sectionKey) {
return () => {
selectedSection = sections.find((section) => section.key === sectionKey);
};
}
function handlePublish() {}
</script>
<Container>
<h3 class="bg-white {borderBottomGrey} p-2">Builder</h3>
{#each sections as section}
<button
class="flex items-center justify-between p-2 w-full bg-white border border-b-0 border-gray-300"
{#if !selectedSection}
<div
class="flex justify-between items-center px-2 {borderBottomGrey} w-full"
>
<p class="mr-2">{section.title} section</p>
<ChevronRight24 class="carbon-class" />
</button>
{/each}
<CloseButton onClick={handleClose} />
<PrimaryButton
label="Publish"
onClick={handlePublish}
disablePadding={true}
/>
</div>
<div
class="flex justify-between items-center px-2 bg-white {borderBottomGrey} w-full mb-2"
>
<h3 class="">Page Builder</h3>
</div>
{#each sections as section}
<button
class="w-full flex items-center justify-between px-2 py-3 bg-white border border-b-0 border-gray-300"
on:click={handleSectionSelect(section.key)}
>
<p class="mr-2">{section.title} section</p>
<ChevronRight24 class="carbon-class" />
</button>
{/each}
{:else}
<div class="flex items-center bg-white {borderBottomGrey} w-full">
<IconButton onClick={handleClose}>
<ArrowLeft24 class="carbon-icon" title="Go back" />
</IconButton>
<h3 class="">{selectedSection.title}</h3>
</div>
<div class="p-2">
{#if selectedSection.key === 1}
<HeaderForm />
{:else if selectedSection.key === 2}
<RequirementForm />
{:else if selectedSection.key === 3}
<DescriptionForm />
{:else if selectedSection.key === 4}
<SyllabusForm />
{:else if selectedSection.key === 5}
<AboutUsForm />
{/if}
</div>
{/if}
</Container>
@@ -0,0 +1,26 @@
export const SECTION_KEYS = {
HEADER: 'header',
REQUIREMENT: 'requirement',
DESCRIPTION: 'description',
SYLLABUS: 'syllabus',
ABOUT_US: 'about-us',
};
export const NAV_ITEMS = [
{
key: SECTION_KEYS.REQUIREMENT,
label: 'Requirement',
},
{
key: SECTION_KEYS.DESCRIPTION,
label: 'Description',
},
{
key: SECTION_KEYS.SYLLABUS,
label: 'Syllabus',
},
{
key: SECTION_KEYS.ABOUT_US,
label: 'About Us',
},
];
+30 -74
View File
@@ -7,64 +7,11 @@
import Expandable from '../Expandable/index.svelte';
import PrimaryButton from '../PrimaryButton/index.svelte';
import MarkdownRender from '../MarkdownRender/index.svelte';
import { NAV_ITEMS, SECTION_KEYS } from './constants';
import { landingPageStore } from './store';
let activeNavItem = '';
const data = {
title: 'Online Business Masterclass: Sell Your Own Digital Products',
shortDescription:
'Create, Launch & Market Your Own Online Business w/ Digital Products such as Online Courses, Coaching, eBooks, Webinars+',
cost: {
currency: '$',
price: '300',
},
requirement:
"You don't need any experience creating an online business or digital product. \nYou don't need an online presence, website, or following - but it will help kickstart your online business if you do.",
description:
"### Have you ever dreamed of starting your own online business?. \n\nOf course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. \n### Enroll today and get instant access to. \n\nOf course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though.\n ### You will learn many types of digital marketing:\n\n Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though.",
lessons: [
{
title: 'What is FinTech?',
},
{
title: 'Payments, Cryptocurrencies and Blockchain',
},
{
title: 'Digital Finance and Alternative Finance',
},
{
title: 'FinTech Regulation and RegTech',
},
{
title: 'Data & TechFin',
},
],
aboutUs: {
companyName: 'The Brad School',
logoUrl: 'https://img-c.udemycdn.com/user/200_H/19701574_95d1_13.jpg',
description:
'*Learn creative skills, from absolute beginner to advanced mastery.*\nVideo School exists to help you succeed in life. Each course has been hand-tailored to teach a specific skill from photography and video to art, design and business.\n**Whether you’re trying to learn a new skill from scratch, or want to refresh your memory on something you’ve learned in the past, you’ve come to the right place.**\nEducation makes the world a better place. Make your world better with new skills!\nOur courses can be watched 24/7 wherever you are. Most are fully downloadable so you can take them with you. You can also view them on mobile devices with the Udemy mobile app.\nAll courses have a 30-day money-back guarantee so that you can check it out, make sure it’s the right course for you, and get a refund if it’s not!',
},
};
const navItems = [
{
label: 'Requirement',
key: 'requirement',
},
{
label: 'Description',
key: 'description',
},
{
label: 'Syllabus',
key: 'syllabus',
},
{
label: 'About Us',
key: 'about-us',
},
];
const expandableRootClass =
'max-w-4xl pt-5 my-3 border-r-0 border-b border-l-0 border-gray-300';
const { page } = stores();
@@ -77,12 +24,12 @@
}
onMount(() => {
activeNavItem = window.location.hash || navItems[0].key;
activeNavItem = (window.location.hash || NAV_ITEMS[0].key).replace('#', '');
});
</script>
<svelte:head>
<title>{data.title}</title>
<title>{$landingPageStore.header.title}</title>
</svelte:head>
<div class="w-full bg-white flex flex-col items-center">
@@ -92,10 +39,10 @@
<div class="container flex justify-between items-center">
<div class="course-description mr-3">
<h1>
{data.title}
{$landingPageStore.header.title}
</h1>
<h5>
{data.shortDescription}
{$landingPageStore.header.description}
</h5>
<div>
<span class="flex items-center">
@@ -107,14 +54,14 @@
<div class="action-container bg-white p-3 flex items-start flex-col">
<p class="mb-2 text-black text-md flex items-center">
<Alarm16 class="carbon-icon active mr-2" />
Estimated 8 weeks
{$landingPageStore.header.duration}
</p>
<p class="mb-2 text-black text-md flex items-center">
<CurrencyDollar16 class="carbon-icon active mr-2" />
{data.cost.currency}{data.cost.price}
{$landingPageStore.header.currency}{$landingPageStore.header.cost}
</p>
<PrimaryButton
label="Enrol"
label={$landingPageStore.header.buttonLabel}
onClick={handleEnrol}
className="md:w-full"
/>
@@ -125,7 +72,7 @@
<nav
class="w-full z-10 max-w-4xl flex items-center justify-evenly py-4 px-4 bg-gray-100 mt-4 sticky top-0"
>
{#each navItems as navItem}
{#each NAV_ITEMS as navItem}
<p
class="text-md text-gray-900 {activeNavItem === navItem.key &&
'font-bold'}"
@@ -137,33 +84,39 @@
</nav>
<Expandable
id="requirement"
id={SECTION_KEYS.REQUIREMENT}
title="Requirement"
titleClass="text-2xl"
rootClass={expandableRootClass}
supportsLink={true}
>
<MarkdownRender content={data.requirement} disableMaxWidth={true} />
<MarkdownRender
content={$landingPageStore.requirement.content}
disableMaxWidth={true}
/>
</Expandable>
<Expandable
id="description"
id={SECTION_KEYS.DESCRIPTION}
title="Description"
titleClass="text-2xl"
rootClass={expandableRootClass}
supportsLink={true}
>
<MarkdownRender content={data.description} disableMaxWidth={true} />
<MarkdownRender
content={$landingPageStore.description.content}
disableMaxWidth={true}
/>
</Expandable>
<Expandable
id="syllabus"
id={SECTION_KEYS.SYLLABUS}
title="Syllabus"
titleClass="text-2xl"
rootClass={expandableRootClass}
supportsLink={true}
>
{#each data.lessons as lesson, index}
{#each $landingPageStore.syllabus.lessons as lesson, index}
<div class="flex items-center my-2">
<strong class="mr-2">Lesson {index + 1}. </strong>
{lesson.title}
@@ -172,22 +125,25 @@
</Expandable>
<Expandable
id="about-us"
title="About Us - {data.aboutUs.companyName}"
id={SECTION_KEYS.ABOUT_US}
title="About Us - {$landingPageStore.aboutUs.companyName}"
titleClass="text-2xl"
rootClass={expandableRootClass}
supportsLink={true}
>
<div class="w-full">
<img
src={data.aboutUs.logoUrl}
alt={data.aboutUs.companyName}
src={$landingPageStore.aboutUs.logoUrl}
alt={$landingPageStore.aboutUs.companyName}
height="260"
width="260"
class="rounded-full m-auto"
/>
</div>
<MarkdownRender content={data.description} disableMaxWidth={true} />
<MarkdownRender
content={$landingPageStore.aboutUs.description}
disableMaxWidth={true}
/>
</Expandable>
</div>
+46
View File
@@ -0,0 +1,46 @@
import { writable } from 'svelte/store';
export const landingPageStore = writable({
header: {
title: 'Online Business Masterclass: Sell Your Own Digital Products',
description:
'Create, Launch & Market Your Own Online Business w/ Digital Products such as Online Courses, Coaching, eBooks, Webinars+',
duration: 'Estimated 8 weeks',
cost: 300,
currency: '$',
buttonLabel: 'Enrol',
},
requirement: {
content:
"You don't need any experience creating an online business or digital product. \nYou don't need an online presence, website, or following - but it will help kickstart your online business if you do.",
},
description: {
content:
"### Have you ever dreamed of starting your own online business?. \n\nOf course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. \n### Enroll today and get instant access to. \n\nOf course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though.\n ### You will learn many types of digital marketing:\n\n Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though. Hello, thank you for reply. Of course I know that :-) I was just thinking if there is some way to do same as getStaticProps for other components too. I don't think such feature is completely necessary, though.",
},
syllabus: {
lessons: [
{
title: 'What is FinTech?',
},
{
title: 'Payments, Cryptocurrencies and Blockchain',
},
{
title: 'Digital Finance and Alternative Finance',
},
{
title: 'FinTech Regulation and RegTech',
},
{
title: 'Data & TechFin',
},
],
},
aboutUs: {
companyName: 'The Brad School',
logoUrl: 'https://img-c.udemycdn.com/user/200_H/19701574_95d1_13.jpg',
description:
'*Learn creative skills, from absolute beginner to advanced mastery.*\nVideo School exists to help you succeed in life. Each course has been hand-tailored to teach a specific skill from photography and video to art, design and business.\n**Whether you’re trying to learn a new skill from scratch, or want to refresh your memory on something you’ve learned in the past, you’ve come to the right place.**\nEducation makes the world a better place. Make your world better with new skills!\nOur courses can be watched 24/7 wherever you are. Most are fully downloadable so you can take them with you. You can also view them on mobile devices with the Udemy mobile app.\nAll courses have a 30-day money-back guarantee so that you can check it out, make sure it’s the right course for you, and get a refund if it’s not!',
},
});
+3 -1
View File
@@ -5,6 +5,7 @@
export let rows = '3';
export let maxRows = 3;
export let className = '';
export let bgColor = 'bg-gray-100';
export let helperMessage = '';
export let errorMessage = '';
export let isRequired = false;
@@ -36,7 +37,8 @@
{placeholder}
bind:value
required={isRequired}
class="form-textarea mt-1 block w-full bg-gray-100 border-t-0 border-l-0 border-r-0 border-b-1 {!!errorMessage
class="form-textarea mt-1 block w-full
{bgColor} border-t-0 border-l-0 border-r-0 border-b-1 {!!errorMessage
? 'border-red-500'
: 'border-gray-300'}"
on:change={onChange}
+3 -24
View File
@@ -6,29 +6,8 @@
<script>
import CourseLandingPage from '../../components/CourseLandingPage/index.svelte';
import Editor from '../../components/CourseLandingPage/components/Editor/index.svelte';
import { isMobile } from '../../utils/store/useMobile';
export let slug;
</script>
<div class="root">
<Editor />
<div class="rightBar" class:isMobile={$isMobile}>
<CourseLandingPage />
</div>
</div>
<style>
.root {
display: flex;
width: 100%;
}
.rightBar {
flex-grow: 1;
width: calc(100% - 360px);
}
.rightBar.isMobile {
margin-left: 10px;
}
</style>
<CourseLandingPage {slug} />
@@ -0,0 +1,39 @@
<script context="module">
export async function preload({ params }) {
return { courseId: params.id };
}
</script>
<script>
import { fly } from 'svelte/transition';
import CourseLandingPage from '../../../components/CourseLandingPage/index.svelte';
import Editor from '../../../components/CourseLandingPage/components/Editor/index.svelte';
import { isMobile } from '../../../utils/store/useMobile';
export let courseId;
</script>
<div
class="root absolute inset-0 flex justify-center items-center z-50 bg-white"
in:fly={{ y: 500, duration: 500 }}
out:fly={{ y: 500, duration: 500 }}
>
<Editor {courseId} />
<div class="rightBar" class:isMobile={$isMobile}>
<CourseLandingPage />
</div>
</div>
<style>
.rightBar {
flex-grow: 1;
width: calc(100% - 360px);
overflow: auto;
height: 100%;
overflow: auto;
}
.rightBar.isMobile {
margin-left: 10px;
}
</style>