CLASSROOMIO-T-45: Basic Landing Page for subdomains

Co-authored-by: Rotimi Best <[email protected]>

Merge-request: CLASSROOMIO-MR-10
Merged-by: Digdippa Team <[email protected]>
This commit is contained in:
Rotimi Best
2023-05-15 15:13:42 +00:00
committed by Space Cloud
co-authored by Rotimi Best
parent 30d019ac51
commit 4f213181ce
7 changed files with 272 additions and 79 deletions
@@ -4,9 +4,11 @@
import { ROLE } from '../../../../utils/constants/roles'; import { ROLE } from '../../../../utils/constants/roles';
import getCurrencyFormatter from '../../../../utils/functions/getCurrencyFormatter'; import getCurrencyFormatter from '../../../../utils/functions/getCurrencyFormatter';
import OverflowMenuHorizontal24 from 'carbon-icons-svelte/lib/OverflowMenuHorizontal24'; import OverflowMenuHorizontal24 from 'carbon-icons-svelte/lib/OverflowMenuHorizontal24';
import ArrowRight from 'carbon-icons-svelte/lib/ArrowRight24';
export let bannerImage: string | undefined; export let bannerImage: string | undefined;
export let id = ''; export let id = '';
export let slug = '';
export let title = ''; export let title = '';
export let description = ''; export let description = '';
export let role_id = ROLE.STUDENT; export let role_id = ROLE.STUDENT;
@@ -14,6 +16,7 @@
export let cost = 0; export let cost = 0;
export let totalLessons = 0; export let totalLessons = 0;
export let currency = 'NGN'; export let currency = 'NGN';
export let isOnLandingPage = false;
let formatter = getCurrencyFormatter(currency); let formatter = getCurrencyFormatter(currency);
@@ -42,10 +45,15 @@
} }
</script> </script>
<a rel="prefetch" href="/courses/{id}" class=" text-black"> <a
<div rel="prefetch"
class="root w-72 border bg-gray-100 dark:bg-gray-700 mr-4 mb-4 rounded-md transition ease-in-out relative" href={isOnLandingPage ? `/course/${slug}` : `/courses/${id}`}
class=" text-black"
> >
<div
class="root w-80 border bg-gray-100 dark:bg-gray-700 sm:mr-4 mb-4 rounded-md hover:shadow-2xl transition ease-in-out relative"
>
{#if !isOnLandingPage}
<Dropdown <Dropdown
options={getOptions()} options={getOptions()}
classNames="absolute top-4 right-4" classNames="absolute top-4 right-4"
@@ -55,12 +63,13 @@
<OverflowMenuHorizontal24 class="carbon-icon" /> <OverflowMenuHorizontal24 class="carbon-icon" />
</div> </div>
</Dropdown> </Dropdown>
{/if}
<img <img
class="h-2/5 w-full rounded-tl rounded-tr" class="h-2/5 w-full rounded-tl rounded-tr"
src={bannerImage ?? '/images/classroomio-course-img-template.jpg'} src={bannerImage ?? '/images/classroomio-course-img-template.jpg'}
alt="Course Logo" alt="Course Logo"
/> />
<div class="p-3 pb-0"> <div class="px-3 py-5">
<h4 class="dark:text-white title text-md font-bold"> <h4 class="dark:text-white title text-md font-bold">
{title} {title}
</h4> </h4>
@@ -72,6 +81,10 @@
</p> </p>
<div class="flex justify-between"> <div class="flex justify-between">
<p class="dark:text-white">{totalLessons} lessons</p> <p class="dark:text-white">{totalLessons} lessons</p>
{#if isOnLandingPage}
<ArrowRight class="carbon-class" />
{:else}
<div class="rounded bg-{isPublished ? 'green' : 'red'}-200 py-1 px-2"> <div class="rounded bg-{isPublished ? 'green' : 'red'}-200 py-1 px-2">
{#if isPublished} {#if isPublished}
Published Published
@@ -79,6 +92,7 @@
Unpublished Unpublished
{/if} {/if}
</div> </div>
{/if}
</div> </div>
<p class="dark:text-white text-gray-500 text-xs mt-1"> <p class="dark:text-white text-gray-500 text-xs mt-1">
{!cost ? 'free' : formatter.format(cost)} {!cost ? 'free' : formatter.format(cost)}
@@ -92,7 +106,8 @@
min-height: 370px; min-height: 370px;
} }
a { a,
a:hover {
text-decoration: none; text-decoration: none;
} }
.title, .title,
+2 -2
View File
@@ -28,7 +28,7 @@
</div> </div>
{#if $user.isLoggedIn} {#if $user.isLoggedIn}
<li class=""> <li class="hidden">
<a <a
class="block" class="block"
aria-current={segment === undefined || segment === 'dashboard' aria-current={segment === undefined || segment === 'dashboard'
@@ -40,7 +40,7 @@
</a> </a>
</li> </li>
<li> <li class="hidden">
<a <a
class="block" class="block"
aria-current={segment === 'courses' ? 'page' : undefined} aria-current={segment === 'courses' ? 'page' : undefined}
+159
View File
@@ -1,4 +1,163 @@
<script> <script>
import { onMount } from 'svelte';
import { Search } from 'carbon-components-svelte';
import LogoFacebook from 'carbon-icons-svelte/lib/LogoFacebook24';
import LogoTwitter from 'carbon-icons-svelte/lib/LogoTwitter24';
import LogoLinkedin from 'carbon-icons-svelte/lib/LogoLinkedin24';
import Navigation from '../../Navigation/index.svelte';
import Box from '../../Box/index.svelte';
import Card from '../../Courses/components/Card/index.svelte';
import CardLoader from '../../Courses/components/Card/Loader.svelte';
import CoursesEmptyIcon from '../../Icons/CoursesEmptyIcon.svelte';
import { courses, courseMetaDeta } from '../../Courses/store';
import { getCourseBySiteName } from '../../../utils/services/org';
import { appStore } from '../../../utils/store/app';
export let segment;
let searchValue = '';
onMount(async () => {
console.log('sitename', $appStore.siteNameFromDomain);
$courseMetaDeta.isLoading = true;
const coursesResult = await getCourseBySiteName(
$appStore.siteNameFromDomain
);
courses.set(coursesResult);
$courseMetaDeta.isLoading = false;
});
</script> </script>
<main>
<Navigation {segment} />
<!-- Header Section -->
<header class="banner w-full flex items-center justify-center p-">
<div
class="w-full flex items-center justify-center flex-col-reverse md:flex-row"
>
<!-- Course Description -->
<div class="course-descr w-11/12 py-10">
<h1
class="dark:text-white text-5xl text-white text-center my-4 font-bold"
>
Explore courses
</h1>
<p class="dark:text-white text-md text-white mb-6 text-center">
Find courses you will love from best teachers all over the world🌎.
</p>
<Search placeholder="Find a course..." bind:value={searchValue} />
</div>
</div>
</header>
<section class="my-10 mx-auto max-w-6xl w-full">
<div class="flex items-center justify-evenly flex-wrap my-4 m-auto">
{#if $courseMetaDeta.isLoading}
<CardLoader />
<CardLoader />
<CardLoader />
{:else}
{#each $courses as courseData}
<Card
id={courseData.id}
slug={courseData.slug}
logo={courseData.logo}
bannerImage={courseData.banner_image}
title={courseData.title}
description={courseData.description}
role_id={courseData.role_id}
isPublished={courseData.is_published}
cost={courseData.cost}
currency={courseData.currency}
totalLessons={courseData.total_lessons}
isOnLandingPage={true}
/>
{:else}
<Box>
<CoursesEmptyIcon />
<h3 class="dark:text-white text-2xl my-5">No Courses Created</h3>
<p class="dark:text-white w-1/3 text-center">
We've got great courses coming your way, stay tuned!!!
</p>
</Box>
{/each}
{/if}
</div>
</section>
<footer
class="flex justify-center mt-10 w-full px-5 py-5 sm:py-10 border-b-0 border-r-0 border-t border-l-0 border-gray-300"
>
<ul class="flex w-11/12 items-center flex-col sm:flex-row">
<div class="logo">
<a
href="/"
title="Go to ClassroomIO Home"
id="logo"
data-hveid="8"
class="flex items-center"
>
<img
src="/logo-192.png"
alt="ClassroomIO logo"
class="rounded h-10 w-10 inline-block mx-auto"
data-atf="1"
/>
<h3 class="text-black ml-3">Classroomio</h3>
</a>
</div>
<span class="flex-grow" />
<div class="flex mt-5 sm:mt-0">
<li class="mx-2">
<a
href="facebook.com"
target="_blank"
title="Facebook"
id="logo-fb"
data-hveid="8"
>
<LogoFacebook />
</a>
</li>
<li class="mx-2">
<a
href="twitter.com"
target="_blank"
title="Twitter"
id="logo-tw"
data-hveid="8"
>
<LogoTwitter />
</a>
</li>
<li class="mx-2">
<a
href="linkedin.com"
target="_blank"
title="Linkedin"
id="logo-ln"
data-hveid="8"
>
<LogoLinkedin />
</a>
</li>
</div>
</ul>
</footer>
</main>
<style lang="scss">
.banner {
background-color: #040f2d;
min-height: 337px;
}
.course-descr {
max-width: 400px;
}
</style>
+14 -18
View File
@@ -15,6 +15,7 @@
import Tailwindcss from '../components/Tailwindcss.svelte'; import Tailwindcss from '../components/Tailwindcss.svelte';
import LandingNavigation from '../components/Navigation/index.svelte'; import LandingNavigation from '../components/Navigation/index.svelte';
import OrgNavigation from '../components/Navigation/app.svelte'; import OrgNavigation from '../components/Navigation/app.svelte';
import OrgLandingPage from '../components/Org/LandingPage/index.svelte';
import Snackbar from '../components/Snackbar/index.svelte'; import Snackbar from '../components/Snackbar/index.svelte';
import Backdrop from '../components/Backdrop/index.svelte'; import Backdrop from '../components/Backdrop/index.svelte';
import OrgSideBar from '../components/Org/SideBar.svelte'; import OrgSideBar from '../components/Org/SideBar.svelte';
@@ -39,7 +40,6 @@
let supabase = getSupabase(config); let supabase = getSupabase(config);
let path = $page.path.replace('/', ''); let path = $page.path.replace('/', '');
let theme = 'white'; let theme = 'white';
let isStudentDomain = false;
const delayedPreloading = derived(preloading, (currentPreloading, set) => { const delayedPreloading = derived(preloading, (currentPreloading, set) => {
setTimeout(() => set(currentPreloading), 250); setTimeout(() => set(currentPreloading), 250);
@@ -100,6 +100,9 @@
const orgRes = await getOrganizations($profile.id); const orgRes = await getOrganizations($profile.id);
if ($appStore.isStudentDomain) {
// student redirect
} else {
// If user coming to login page, then // If user coming to login page, then
if (isEmpty(orgRes.orgs)) { if (isEmpty(orgRes.orgs)) {
goto(ROUTE.ONBOARDING); goto(ROUTE.ONBOARDING);
@@ -112,14 +115,16 @@
} }
} }
} }
}
function getIsStudentDomain(host) { function setOrgBasedOnUrl(host) {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const debug = localStorage.getItem('role') === 'student'; const debug = localStorage.getItem('role') === 'student';
const matches = host.match(/([a-z0-9]+).*classroomio[.]com/); const matches = host.match(/([a-z0-9]+).*classroomio[.]com/);
const answer = Array.isArray(matches) ? !!matches[1] : false; const answer = Array.isArray(matches) ? !!matches[1] : false;
return debug || answer; $appStore.isStudentDomain = debug || answer;
$appStore.siteNameFromDomain = debug ? 'startupscoo' : matches?.[1] ?? '';
} }
} }
@@ -139,7 +144,7 @@
console.log( console.log(
'Welcome to ClassroomIO, we are grateful you chose us.', 'Welcome to ClassroomIO, we are grateful you chose us.',
$page.host, $page.host,
`\nIs student domain: ${isStudentDomain}` `\nIs student domain: ${$appStore.isStudentDomain}`
); );
// Disable GA on localhost // Disable GA on localhost
if (config && !config.isProd) { if (config && !config.isProd) {
@@ -183,21 +188,9 @@
}; };
}); });
// if (typeof window !== 'undefined' && typeof document !== 'undefined') {
// page.subscribe(() => {
// const from = window.location.pathname;
// const redirect = (href) => {
// goto(href);
// };
// if (!$user.isLoggedIn && !PUBLIC_ROUTES.includes(from)) {
// redirect('/login');
// }
// });
// }
$: path = $page.path.replace('/', ''); $: path = $page.path.replace('/', '');
$: theme = $appStore.isDark ? 'g100' : 'white'; $: theme = $appStore.isDark ? 'g100' : 'white';
$: isStudentDomain = getIsStudentDomain($page.host); $: setOrgBasedOnUrl($page.host);
</script> </script>
<svelte:window on:resize={handleResize} /> <svelte:window on:resize={handleResize} />
@@ -209,9 +202,11 @@
<Theme bind:theme /> <Theme bind:theme />
<Tailwindcss /> <Tailwindcss />
<!-- <Nav {segment} /> -->
<Snackbar /> <Snackbar />
{#if $appStore.isStudentDomain && !path}
<OrgLandingPage {segment} />
{:else}
<main class="dark:bg-gray-800"> <main class="dark:bg-gray-800">
{#if $preloading && $delayedPreloading} {#if $preloading && $delayedPreloading}
<Backdrop> <Backdrop>
@@ -247,6 +242,7 @@
{#if !['about', ''].includes(path)} {#if !['about', ''].includes(path)}
<!-- <Footer {segment} /> --> <!-- <Footer {segment} /> -->
{/if} {/if}
{/if}
<style> <style>
main { main {
+1 -1
View File
@@ -54,7 +54,7 @@ export async function fetchCourse(
console.log(`data`, data); console.log(`data`, data);
console.log(`fetchCourse => error`, error); console.log(`fetchCourse => error`, error);
// return this.redirect(301, '/courses'); // return this.redirect(301, '/courses');
return; return { data, error };
} }
return { return {
+21
View File
@@ -122,3 +122,24 @@ export async function getOrgAudience(orgId) {
error, error,
}; };
} }
export async function getCourseBySiteName(siteName) {
const { data, error } = await supabase
.from('course')
.select(
`
*,
group!inner(
organization!inner(id, name, siteName, avatar_url)
)
`
)
.eq('group.organization.siteName', siteName)
.eq('is_published', true);
if (error) {
return [];
}
return data;
}
+2
View File
@@ -2,4 +2,6 @@ import { writable } from 'svelte/store';
export const appStore = writable({ export const appStore = writable({
isDark: false, isDark: false,
isStudentDomain: false,
siteNameFromDomain: '',
}); });