feat: course-app v0.0.11 (#523)
* chore: remove bin * fix: restore bin * change dist to bin * publish only dist folder * publish with src folder. * fix ci * merge steps in CI * update workflow * deploy with src * update to v9 * delete unused code * complete landing page * complete landing page * feat: webflow template (#518) * created a new branch for webflow from debug/course-app * added webflow and reverted neccessary changes * fixed commit issues * clean up posthog * clean up classic * clean up minimal * clean up posthog * clean up webflow * v0.0.10 * markdown lint * feat: add prep template (#522) * working on the ielts template * working on the ielts template * finished the ielts template --------- Co-authored-by: emmanuel <[email protected]> * clean up examprep * v0.0.11 --------- Co-authored-by: Em_Dev <[email protected]>
@@ -1,8 +1,9 @@
|
||||
name: Publish @classroomio/course-app to NPM
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
@@ -16,10 +17,11 @@ jobs:
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies & publish
|
||||
- name: Install, Build & Publish
|
||||
working-directory: packages/course-app
|
||||
run: |
|
||||
pnpm i
|
||||
pnpm build
|
||||
pnpm publish --no-git-checks
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Virgil';
|
||||
src: url('/fonts/Virgil.woff2');
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<!-- Facebook Meta Tags -->
|
||||
<meta property="og:url" content="https://www.courseapp.oncws.com/" />
|
||||
<meta property="og:image" itemprop="image" content="https://cdn.courseapp.oncws.com/courseapp-og-image.png" />
|
||||
<meta property="og:image" itemprop="image" content="https://cdn.courseapp.oncws.com/courseapp-og.png" />
|
||||
<meta property="og:title" content="Courseapp by ClassroomIO – Create your SAAS Academy in Minutes" />
|
||||
<meta property="og:description"
|
||||
content="Series of templates available for you to clone, customise and launch your SAAS Academy. Scale your customer education effort today." />
|
||||
@@ -24,8 +24,7 @@
|
||||
<meta property="og:image:type" content="image/png" />
|
||||
<meta property="og:image:width" content="1920" />
|
||||
<meta property="og:image:height" content="1080" />
|
||||
<meta property="og:image:secure_url" itemprop="image"
|
||||
content="https://cdn.courseapp.oncws.com/courseapp-og-image.png" />
|
||||
<meta property="og:image:secure_url" itemprop="image" content="https://cdn.courseapp.oncws.com/courseapp-og.png" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:domain" content="courseapp.oncws.com" />
|
||||
@@ -33,7 +32,7 @@
|
||||
<meta name="twitter:title" content="Courseapp by ClassroomIO – Create your SAAS Academy in Minutes" />
|
||||
<meta name="twitter:description"
|
||||
content="Series of templates available for you to clone, customise and launch your SAAS Academy. Scale your customer education effort today." />
|
||||
<meta name="twitter:image" content="https://cdn.courseapp.oncws.com/courseapp-og-image.png" />
|
||||
<meta name="twitter:image" content="https://cdn.courseapp.oncws.com/courseapp-og.png" />
|
||||
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
export let href: string;
|
||||
export let target: string = '';
|
||||
export let rel: string = '';
|
||||
export let shimmerColor = '#ffffff';
|
||||
export let shimmerSize = '0.05em';
|
||||
export let shimmerDuration = '3s';
|
||||
export let borderRadius = '100px';
|
||||
export let background = 'rgba(0, 0, 0, 1)';
|
||||
export let onClick: () => void;
|
||||
let className: any = '';
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<button
|
||||
<a
|
||||
style:--spread="90deg"
|
||||
style:--shimmer-color={shimmerColor}
|
||||
style:--radius={borderRadius}
|
||||
@@ -23,7 +25,9 @@
|
||||
'transform-gpu transition-transform duration-300 ease-in-out active:translate-y-[1px]',
|
||||
className
|
||||
)}
|
||||
on:click={onClick}
|
||||
{href}
|
||||
{target}
|
||||
{rel}
|
||||
>
|
||||
<div class={cn('-z-30 blur-[2px]', 'absolute inset-0 overflow-visible [container-type:size]')}>
|
||||
<div
|
||||
@@ -59,4 +63,4 @@
|
||||
'absolute -z-20 [background:var(--bg)] [border-radius:var(--radius)] [inset:var(--cut)]'
|
||||
)}
|
||||
></div>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Sparkle } from './sparkle-text.svelte';
|
||||
@@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
interface TSparkle {
|
||||
id: string;
|
||||
x: string;
|
||||
y: string;
|
||||
color: string;
|
||||
delay: number;
|
||||
scale: number;
|
||||
lifespan: number;
|
||||
}
|
||||
|
||||
export let text: string = 'Hello World';
|
||||
export let colors = {
|
||||
first: '#9E7AFF',
|
||||
second: '#FE8BBB'
|
||||
};
|
||||
export let sparklesCount = 5;
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
|
||||
let sparkles: TSparkle[] = [];
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import Sparkle from './sparkle.svelte';
|
||||
|
||||
let generateStar = () => {
|
||||
let starX = `${Math.random() * 100}%`;
|
||||
let starY = `${Math.random() * 100}%`;
|
||||
let color = Math.random() > 0.5 ? colors.first : colors.second;
|
||||
let delay = Math.random() * 4;
|
||||
let scale = Math.random() * 1 + 0.3;
|
||||
let lifespan = Math.random() * 10 + 5;
|
||||
let id = `${starX}-${starY}-${Date.now()}`;
|
||||
return { id, x: starX, y: starY, color, delay, scale, lifespan };
|
||||
};
|
||||
|
||||
let initializeStars = async () => {
|
||||
let newSparkles = Array.from({ length: sparklesCount }, generateStar);
|
||||
sparkles = newSparkles;
|
||||
};
|
||||
|
||||
const updateStars = () => {
|
||||
let temp = sparkles.map((star) => {
|
||||
if (star.lifespan <= 0) {
|
||||
return generateStar();
|
||||
} else {
|
||||
return { ...star, lifespan: star.lifespan - 0.1 };
|
||||
}
|
||||
});
|
||||
sparkles = temp;
|
||||
};
|
||||
|
||||
let interval: NodeJS.Timer;
|
||||
onMount(async () => {
|
||||
initializeStars();
|
||||
interval = setInterval(updateStars, 100);
|
||||
});
|
||||
onDestroy(() => clearInterval(interval));
|
||||
</script>
|
||||
|
||||
<div
|
||||
style:--sparkles-first-color="{colors.first};"
|
||||
style:--sparkles-second-color="{colors.second};"
|
||||
class={className}
|
||||
{...$$restProps}
|
||||
>
|
||||
<span class="relative inline-block">
|
||||
{#each sparkles as item, _}
|
||||
<Sparkle {...item} />
|
||||
{/each}
|
||||
<strong>{text}</strong>
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { Motion } from "svelte-motion";
|
||||
|
||||
export let id;
|
||||
export let x;
|
||||
export let y;
|
||||
export let color;
|
||||
export let delay;
|
||||
export let scale;
|
||||
</script>
|
||||
|
||||
<Motion
|
||||
initial={{ opacity: 0, left: x, top: y }}
|
||||
animate={{
|
||||
opacity: [0, 1, 0],
|
||||
scale: [0, scale, 0],
|
||||
rotate: [75, 120, 150],
|
||||
}}
|
||||
transition={{ duration: 0.8, repeat: Infinity, delay }}
|
||||
let:motion
|
||||
>
|
||||
<svg
|
||||
{id}
|
||||
use:motion
|
||||
class="pointer-events-none absolute z-20"
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 21 21"
|
||||
>
|
||||
<path
|
||||
d="M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
</Motion>
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import ArrowUpRight from 'lucide-svelte/icons/arrow-up-right';
|
||||
|
||||
const getClass = (active: boolean) => (active ? 'text-foreground' : 'text-foreground/60');
|
||||
|
||||
let activeClass = $derived({
|
||||
template: getClass($page.url.href.includes('#templates')),
|
||||
setup: getClass($page.url.href.includes('#setup'))
|
||||
features: getClass($page.url.href.includes('#features'))
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -36,10 +37,10 @@
|
||||
</p>
|
||||
<div class="container flex h-14 max-w-screen-2xl items-center">
|
||||
<div class="mr-4 flex">
|
||||
<a href="/" class="mr-6 flex items-center space-x-2">
|
||||
<a href="/" class="mr-6 flex items-center">
|
||||
<img src="/logo-512.png" alt="Course App" class="h-8 w-8" />
|
||||
{' '}
|
||||
<span class="inline-block font-bold">courseapp</span>
|
||||
<span class="inline-block font-bold">ourseapp</span>
|
||||
</a>{' '}
|
||||
<nav class="hidden items-center gap-6 text-sm md:flex">
|
||||
<a
|
||||
@@ -48,8 +49,19 @@
|
||||
>
|
||||
Templates
|
||||
</a>{' '}
|
||||
<a href="#setup" class="hover:text-foreground/80 {activeClass.setup} transition-colors">
|
||||
Setup
|
||||
<a
|
||||
href="#features"
|
||||
class="hover:text-foreground/80 {activeClass.features} transition-colors"
|
||||
>
|
||||
Features
|
||||
</a>{' '}
|
||||
<a
|
||||
href="/npm"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="hover:text-foreground/80 text-foreground/60 flex items-center gap-1 transition-colors"
|
||||
>
|
||||
NPM <ArrowUpRight class="h-4 w-4" />
|
||||
</a>{' '}
|
||||
<!-- <a
|
||||
href="https://github.com/classroomio/classroomio"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
variant="link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="border-stroke-primary relative h-80 min-w-60 overflow-hidden rounded-md border border-solid p-0 transition-all duration-500 lg:h-96"
|
||||
class="border-stroke-primary relative h-80 min-w-60 overflow-hidden rounded-md border border-solid p-0 transition-all duration-500"
|
||||
>
|
||||
<span
|
||||
class="absolute inset-0 flex items-center justify-center bg-black/50 opacity-0 transition-all duration-500 hover:opacity-80"
|
||||
@@ -28,7 +28,7 @@
|
||||
</span>
|
||||
<img
|
||||
src={template.image}
|
||||
class="h-full w-full object-contain hover:opacity-10"
|
||||
class="h-full w-full rounded-lg object-contain hover:opacity-10"
|
||||
alt={template.name}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export const load = () => {
|
||||
throw redirect(307, 'https://www.npmjs.com/package/@classroomio/course-app');
|
||||
};
|
||||
@@ -10,6 +10,7 @@
|
||||
import { Separator } from '$lib/components/ui/separator';
|
||||
import { ShinnyText } from '$lib/components/animation/shinny-text';
|
||||
import { ShimmerButton } from '$lib/components/animation/shimmerbutton';
|
||||
import { Sparkle } from '$lib/components/animation/sparkle';
|
||||
import Copy from 'lucide-svelte/icons/copy';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
@@ -26,6 +27,12 @@
|
||||
url: 'https://posthog.courseapp.oncws.com',
|
||||
github: 'https://github.com/classroomio/classroomio'
|
||||
},
|
||||
{
|
||||
name: 'Webflow',
|
||||
image: 'https://cdn.courseapp.oncws.com/templates/webflow-template.png',
|
||||
url: 'https://webflow.courseapp.oncws.com',
|
||||
github: 'https://github.com/classroomio/classroomio'
|
||||
},
|
||||
{
|
||||
name: 'Classic',
|
||||
image: 'https://cdn.courseapp.oncws.com/templates/classic-template.png',
|
||||
@@ -40,16 +47,49 @@
|
||||
},
|
||||
{
|
||||
name: 'Minimal',
|
||||
image: 'https://cdn.courseapp.oncws.com/templates/minimal.png',
|
||||
image: 'https://cdn.courseapp.oncws.com/templates/minimal-template.png',
|
||||
url: 'https://minimal.courseapp.oncws.com',
|
||||
github: 'https://github.com/classroomio/classroomio'
|
||||
},
|
||||
{
|
||||
name: 'Bootcamps',
|
||||
image: 'https://cdn.courseapp.oncws.com/templates/bootcamp-template.png',
|
||||
url: 'https://bootcamp.courseapp.oncws.com',
|
||||
github: 'https://github.com/classroomio/classroomio'
|
||||
}
|
||||
];
|
||||
|
||||
const features = [
|
||||
'Beautiful templates you can choose from',
|
||||
'Create as many courses as you want',
|
||||
'Write courses with Markdown',
|
||||
'Embed videos, slides, and other media',
|
||||
'Deploy to Vercel, Netlify or wherever you want.'
|
||||
];
|
||||
|
||||
const examples = [
|
||||
{
|
||||
name: 'Webflow',
|
||||
url: 'https://university.webflow.com'
|
||||
},
|
||||
{
|
||||
name: 'Hubspot',
|
||||
url: 'https://academy.hubspot.com'
|
||||
},
|
||||
{
|
||||
name: 'Atlassian',
|
||||
url: 'https://university.atlassian.com'
|
||||
},
|
||||
{
|
||||
name: 'Framer',
|
||||
url: 'https://framer.university/'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<!-- Hero -->
|
||||
<section class="my-5 w-full md:mb-20 md:mt-0">
|
||||
<div class="">
|
||||
<section class="my-5 w-full md:my-0">
|
||||
<div>
|
||||
<CardContainer className="md:px-20">
|
||||
<CardBody
|
||||
className="max-w-4xl min-h-[50vh] flex flex-col justify-center px-30 gap-2 md:gap-5"
|
||||
@@ -64,27 +104,22 @@
|
||||
|
||||
<BlurFade delay={0.25 * 2}>
|
||||
<p class="text-md">
|
||||
With our prebuilt templates, you can setup a fully functional customer educational site
|
||||
for your product in minutes.<br />
|
||||
With our prebuilt templates, you can setup a fully functional customer educational
|
||||
website for your product in minutes.<br />
|
||||
<span>
|
||||
Similar to the likes of
|
||||
|
||||
<Button variant="link" class="h-fit p-0" href="https://university.webflow.com/">
|
||||
Webflow,
|
||||
</Button>
|
||||
<Button variant="link" class="h-fit p-0" href="https://academy.hubspot.com/">
|
||||
Hubspot,
|
||||
</Button>
|
||||
{#each examples as example}
|
||||
<Button
|
||||
variant="link"
|
||||
class="h-fit p-0"
|
||||
href="https://university.atlassian.com/student/activity/2227111-home#/page/670e8ce7a1887954d13a856a"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="mx-1 h-fit p-0 underline"
|
||||
href={example.url}
|
||||
>
|
||||
Atlassian &
|
||||
{example.name}
|
||||
</Button>
|
||||
<Button variant="link" class="h-fit p-0" href="https://framer.university/"
|
||||
>Framer</Button
|
||||
>
|
||||
{/each}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
@@ -113,10 +148,10 @@
|
||||
<!-- Hero End -->
|
||||
|
||||
<!-- Templates -->
|
||||
<section id="templates" class="my-5 w-full pt-0 md:my-10 md:pt-24">
|
||||
<section id="templates" class="my-5 w-full pt-0 md:my-0 md:pt-24">
|
||||
<div class="container">
|
||||
<h2 class="mb-5 text-center text-3xl font-semibold md:mb-20 md:text-start md:text-5xl">
|
||||
Browser Templates
|
||||
Browse Templates
|
||||
</h2>
|
||||
|
||||
<div class="grid max-w-6xl grid-cols-1 place-items-center gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
@@ -128,13 +163,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Templates End -->
|
||||
|
||||
<Separator class="my-20" />
|
||||
<Separator class="my-5 md:mt-20" />
|
||||
|
||||
<!-- Setup -->
|
||||
<section id="setup" class="mb-20 w-full">
|
||||
<!-- Features -->
|
||||
<section id="features" class="my-5 w-full px-4 pt-0 md:mb-10 md:mt-0 md:pt-24">
|
||||
<div class="font-drawn container mx-auto flex max-w-4xl flex-col items-center">
|
||||
<h2 class="mb-2 text-center text-3xl font-semibold md:mb-10 md:text-start md:text-5xl">
|
||||
Features
|
||||
</h2>
|
||||
|
||||
<ul>
|
||||
{#each features as feature}
|
||||
<li class="text-md mb-2 list-decimal md:text-2xl">
|
||||
<Sparkle text={feature} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Features End -->
|
||||
|
||||
<Separator class="my-5 md:my-20" />
|
||||
|
||||
<!-- Home CTA -->
|
||||
<section class="mb-20 w-full">
|
||||
<BlurFade delay={0.25}>
|
||||
<div class="container">
|
||||
<!-- <h2 class="mb-10 text-center text-5xl font-semibold">
|
||||
@@ -142,43 +196,39 @@
|
||||
</h2> -->
|
||||
|
||||
<div
|
||||
class="relative mx-auto h-fit w-full max-w-3xl overflow-hidden rounded-lg border bg-neutral-800 p-20 py-40 text-center shadow-2xl"
|
||||
class="relative mx-auto h-fit w-full overflow-hidden rounded-lg border bg-neutral-800 px-10 py-20 text-center shadow-2xl md:max-w-3xl md:px-20 md:py-40"
|
||||
>
|
||||
<Meteors number={30} />
|
||||
|
||||
<h1 class="mb-5 text-5xl font-semibold text-white">
|
||||
Start Customising Your University Site
|
||||
<div class="z-20">
|
||||
<h1 class="mb-5 text-2xl font-semibold text-white md:text-5xl">
|
||||
Kickstart Your Customer Education Journey
|
||||
</h1>
|
||||
|
||||
<div class="z-10 flex items-center justify-center">
|
||||
<ShimmerButton
|
||||
onClick={() => {
|
||||
window.location.href =
|
||||
'https://github.com/classroomio/classroomio/blob/a6af412b2401a8dc3f871773b96a24b2fa764f52/packages/course-app/README.md';
|
||||
}}
|
||||
class="shadow-2xl"
|
||||
>
|
||||
<div class=" flex items-center justify-center">
|
||||
<ShimmerButton href="/npm" target="_blank" rel="noopener noreferrer" class="shadow-2xl">
|
||||
<span
|
||||
class="whitespace-pre-wrap text-center text-sm font-medium leading-none tracking-tight text-white lg:text-lg dark:from-white dark:to-slate-900/10"
|
||||
>
|
||||
Go to Docs
|
||||
Learn More
|
||||
</span>
|
||||
</ShimmerButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BlurFade>
|
||||
</section>
|
||||
<!-- Home CTA End -->
|
||||
|
||||
<!-- Setup End -->
|
||||
|
||||
<Separator class="mt-20" />
|
||||
<Separator class="mt-5 md:mt-20" />
|
||||
|
||||
<!-- Footer -->
|
||||
<section class="my-10 w-full">
|
||||
<div class="container">
|
||||
<p class="text-center text-sm text-neutral-500">
|
||||
Made with ❤️ by <a href="https://git.new/class">classroomio</a>
|
||||
Made with ❤️ by <a href="https://git.new/class" target="_blank" rel="noopener noreferrer">
|
||||
classroomio
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -55,7 +55,17 @@ const config: Config = {
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [...fontFamily.sans]
|
||||
sans: [...fontFamily.sans],
|
||||
drawn: [
|
||||
'Virgil',
|
||||
'ui-sans-serif',
|
||||
'system-ui',
|
||||
'sans-serif',
|
||||
'Apple Color Emoji',
|
||||
'Segoe UI Emoji',
|
||||
'Segoe UI Symbol',
|
||||
'Noto Color Emoji'
|
||||
]
|
||||
},
|
||||
|
||||
animation: {
|
||||
|
||||
@@ -1,27 +1,177 @@
|
||||
# @classroomio/course-app
|
||||
<a href="https://courseapp.oncws.com/">
|
||||
<img alt="Create beautiful course websites to educate your customers" src="https://cdn.courseapp.oncws.com/courseapp-og.png" />
|
||||
<h1 align="center">CourseApp by ClassroomIO</h1>
|
||||
<p align="center">
|
||||
Create beautiful course websites to educate your customers.
|
||||
<br />
|
||||
<a href="https://courseapp.oncws.com">Website</a> | <a href="https://dub.sh/ciodiscord">Join Discord community</a>
|
||||
</p>
|
||||
</a>
|
||||
|
||||
Create a university site for your SAAS in seconds, ready to go. Just add your content.
|
||||
# CourseApp
|
||||
|
||||
## Usage
|
||||
Create a beautiful course website in seconds with our SvelteKit-powered CLI tool. Choose from multiple templates and customize your content through a simple file structure.
|
||||
|
||||
To create a template run:
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
npx @cio/course-app@latest name-of-your-project -t name-of-template --course
|
||||
pnpx @classroomio/course-app@latest
|
||||
```
|
||||
|
||||
### Commands
|
||||
|
||||
The CLI accepts the following arguments
|
||||
|
||||
- argument 1 : `name-of-project` this argument is the name of your project
|
||||
- argument 2 : `--template name-of-template` this argument is the name of the template you wish to use
|
||||
- argument 3 : `--courses` an option to use our demo courses
|
||||
You'll be prompted to:
|
||||
1. Enter a project name (or use `.` for current directory)
|
||||
2. Select a template
|
||||
3. Choose whether to include demo courses
|
||||
|
||||
## Available Templates
|
||||
|
||||
- cal
|
||||
- classic
|
||||
- minimalistic
|
||||
- posthog
|
||||
- webflow
|
||||
- `cal` - Cal.com inspired design
|
||||
- `classic` - Traditional course layout
|
||||
- `minimal` - Clean, minimalist design
|
||||
- `posthog` - PostHog-style interface
|
||||
- `webflow` - Webflow-inspired template
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
your-project/
|
||||
├── src/
|
||||
│ ├── courses/ # Your course content
|
||||
│ │ ├── course-slug/
|
||||
│ │ │ ├── metadata.json # Course details
|
||||
│ │ │ ├── section-1/
|
||||
│ │ │ │ ├── metadata.json # Section details
|
||||
│ │ │ │ ├── lesson-1.md # Lesson content
|
||||
│ │ │ │ └── lesson-2.md
|
||||
│ │ │ └── section-2/
|
||||
│ │ └── another-course/
|
||||
│ └── lib/
|
||||
│ └── components/ # Template-specific components
|
||||
```
|
||||
|
||||
## Course Configuration
|
||||
|
||||
### Course Metadata
|
||||
Each course needs a `metadata.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Your Course Title",
|
||||
"description": "Course description",
|
||||
"banner": "https://path-to-banner-image.jpg",
|
||||
"created_at": "24-11-2024",
|
||||
"cost": 0,
|
||||
"currency": "USD",
|
||||
"type": "paced"
|
||||
}
|
||||
```
|
||||
|
||||
### Section Metadata
|
||||
Each section folder requires a `metadata.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Section Title",
|
||||
"unlocked": true
|
||||
}
|
||||
```
|
||||
|
||||
### Lesson Content
|
||||
Lessons are written in Markdown (`.md`) files:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: 'Lesson Title'
|
||||
---
|
||||
|
||||
Your lesson content here...
|
||||
|
||||
## Supports Markdown Features
|
||||
|
||||
- Lists
|
||||
- Code blocks
|
||||
- Images
|
||||
- And more!
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
### Templates
|
||||
Each template has its own component set in `src/lib/components/[template-name]/`. The active template is determined by the `VITE_TEMPLATE` environment variable.
|
||||
|
||||
### Navigation & Site Settings
|
||||
|
||||
Update `src/lib/data/pages.json` to customize your site settings. The file contains an array of three main page configurations:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "shared-page",
|
||||
"title": "Shared",
|
||||
"slug": "#",
|
||||
"sections": [
|
||||
// Global components like SEO, navigation, and footer
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-page",
|
||||
"title": "Home Page",
|
||||
"slug": "/",
|
||||
"sections": [
|
||||
// Home page specific sections like hero, features, testimonials
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "courses-page",
|
||||
"title": "Courses Page",
|
||||
"slug": "/courses",
|
||||
"sections": [
|
||||
// Course page specific sections
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Each page configuration includes:
|
||||
|
||||
1. **Shared Page** (`shared-page`):
|
||||
- Contains global components used across all pages
|
||||
- Configures SEO settings, main navigation, and footer
|
||||
- Always present regardless of the current route
|
||||
|
||||
2. **Home Page** (`home-page`):
|
||||
- Landing page configuration (`/` route)
|
||||
- Includes sections like header, about, featured courses, instructors, FAQ, testimonials, and CTAs
|
||||
- Configurable marketing sections to showcase your platform
|
||||
|
||||
3. **Courses Page** (`courses-page`):
|
||||
- Course listing page configuration (`/courses` route)
|
||||
- Contains course-specific header and layout settings
|
||||
- Manages how courses are displayed and filtered
|
||||
|
||||
Each section can be toggled with the `show` property and customized through its `settings` object.
|
||||
|
||||
## API Routes
|
||||
|
||||
The project includes built-in API routes:
|
||||
|
||||
- `/api/courses` - Returns all courses with their metadata
|
||||
- Individual course data is dynamically loaded based on the URL structure
|
||||
|
||||
## Development
|
||||
|
||||
After creating your project:
|
||||
|
||||
```bash
|
||||
cd your-project
|
||||
pnpm install
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
## Next up
|
||||
|
||||
- More templates ;)
|
||||
- Create quizzes
|
||||
- Authentication
|
||||
- Learning paths
|
||||
- And more!
|
||||
|
||||
@@ -61,20 +61,21 @@ program
|
||||
]);
|
||||
courses = answer.course;
|
||||
}
|
||||
|
||||
spinner.start('Scaffolding project....');
|
||||
|
||||
try {
|
||||
console.log('\n');
|
||||
|
||||
await scaffoldTemplate({ template, courses, projectPath, projectName });
|
||||
|
||||
spinner.succeed(`\n\n${chalk.green('Done! Your project is ready.')}\n\n`);
|
||||
spinner.succeed(`${chalk.green('Done! Your template is ready.')}`);
|
||||
console.log(
|
||||
`To get started:\n\n${chalk.blue(`cd ${projectName}`)}\n${chalk.blue(
|
||||
`\nTo get started:\n\n${chalk.green(`cd ${projectName}`)}\n${chalk.green(
|
||||
'pnpm i'
|
||||
)}\n${chalk.blue('pnpm run dev')}`
|
||||
)}\n${chalk.green('pnpm run dev')}`
|
||||
);
|
||||
|
||||
console.log('\n\n', chalk.blue('Happy hacking ;)'));
|
||||
console.log(`\nDM me on X if you face any issues: https://x.com/rotimi_best`);
|
||||
} catch (error) {
|
||||
spinner.fail(chalk.red(`Failed to scaffold the project. ${error}`));
|
||||
process.exit(1);
|
||||
|
||||
@@ -20,11 +20,20 @@ const __dirname = path.dirname(__filename);
|
||||
const packageRoot = path.join(__dirname, '..');
|
||||
const spinner = ora();
|
||||
|
||||
async function rewriteComponentsFile(filePath: string, template: string): Promise<void> {
|
||||
const newContent = `export { components } from './${template}';`;
|
||||
|
||||
try {
|
||||
fs.writeFileSync(filePath, newContent);
|
||||
} catch (error) {
|
||||
console.error(`Error writing to file: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function scaffoldTemplate({
|
||||
template,
|
||||
courses,
|
||||
projectPath,
|
||||
projectName
|
||||
projectPath
|
||||
}: ScaffoldTemplateParams): Promise<void> {
|
||||
spinner.text = chalk.yellow('Getting project...');
|
||||
|
||||
@@ -69,23 +78,19 @@ export async function scaffoldTemplate({
|
||||
excludePath: coursePathToExclude
|
||||
});
|
||||
|
||||
// Update root import of components to reflect only selected template
|
||||
const componentsIndex = path.join(projectPath, 'src', 'lib', 'components', 'index.ts');
|
||||
await rewriteComponentsFile(componentsIndex, template);
|
||||
|
||||
// If the user wants demo courses, copy the courses folder
|
||||
spinner.text = chalk.yellow('Setting up courses folder...');
|
||||
await copyCoursesFolder({ templatePath: templateDirPath, projectPath, courses });
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Project ${projectName} created using template "${template}" ${
|
||||
courses ? 'with' : 'without'
|
||||
} demo courses.`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`You can ${
|
||||
courses
|
||||
? 'edit or create new courses in the courses folder'
|
||||
: 'create new courses in the courses folder'
|
||||
}`
|
||||
)
|
||||
);
|
||||
|
||||
// console.log(
|
||||
// chalk.yellow(
|
||||
// `Project ${projectName} created using template "${template}" ${
|
||||
// courses ? 'with' : 'without'
|
||||
// } demo courses.`
|
||||
// )
|
||||
// );
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@classroomio/course-app",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.11",
|
||||
"private": false,
|
||||
"main": "dist/create-template.js",
|
||||
"scripts": {
|
||||
"create": "node dist/create-template.js",
|
||||
"dev:run": "pnpm build && node dist/create-template.js",
|
||||
"build": "vite build && node scripts/postbuild.js"
|
||||
},
|
||||
"bin": {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"MD013": false
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "calcom",
|
||||
"name": "courseapp",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -36,12 +36,12 @@
|
||||
"vitest": "^2.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sveltejs/adapter-cloudflare": "^4.7.4",
|
||||
"carbon-icons-svelte": "^12.13.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"rehype-unwrap-images": "^1.0.0",
|
||||
"remark-toc": "^9.0.0",
|
||||
"shiki": "^1.23.1",
|
||||
"svelte-meta-tags": "^4.0.4"
|
||||
"svelte-meta-tags": "^4.0.4",
|
||||
"svelte-motion": "^0.12.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,15 @@
|
||||
--posthog-background: 70 16% 93%;
|
||||
--posthog-border: 68 8% 80%;
|
||||
|
||||
--minimal-primary: 214 33% 9%;
|
||||
--minimal-secondary: 212 34% 15% 1;
|
||||
--minimal-primary: 222 95% 41%;
|
||||
--minimal-secondary: 214 33% 9%;
|
||||
|
||||
--classic-primary: 300 98% 41%;
|
||||
--classic-secondary: 300 100% 95%;
|
||||
|
||||
--examprep-primary: 224 98% 37%;
|
||||
--webflow-primary: 224 93% 39%;
|
||||
--webflow-secondary: 224 93% 39%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
@@ -7,7 +7,6 @@ title: 'Why Analytics Matters'
|
||||
const increment = () => count++;
|
||||
</script>
|
||||
|
||||
|
||||
## Code
|
||||
|
||||
```ts
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { CheckmarkFilled } from 'carbon-icons-svelte';
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { homePage } from '$lib/utils/stores/pages';
|
||||
|
||||
const content = $derived(getPageSection($homePage, 'about'));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<section id="about">
|
||||
<div class="relative px-4 md:px-10 py-10 h-full w-full mx-auto xl:w-[80%]">
|
||||
<h1 class="text-center text-5xl mb-2 font-bold md:w-[60%] mx-auto">
|
||||
{content.settings.title}
|
||||
</h1>
|
||||
<p class="text-center dark:text-white text-lg font-semibold md:w-[70%] mb-8 mx-auto">
|
||||
{content.settings.subtitle}
|
||||
</p>
|
||||
<div>
|
||||
<section class="flex flex-wrap items-start justify-center w-full gap-6 py-4">
|
||||
{#each content.settings.benefits.list as item}
|
||||
<div
|
||||
class="flex flex-col gap-2 p-4 bg-white border border-[#282828] h-[130px] max-h-[180px] w-full max-w-full md:max-w-[320px] lg:max-w-[450px] rounded-lg overflow-hidden"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<CheckmarkFilled size={24} />
|
||||
<p class="text-lg capitalize line-clamp-1">{item.title}</p>
|
||||
</span>
|
||||
<p class="text-sm text-[#878787] line-clamp-3">{item.subtitle}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
@@ -9,38 +9,28 @@
|
||||
description: string;
|
||||
}
|
||||
|
||||
let {
|
||||
className = '',
|
||||
bannerImage = '',
|
||||
slug = '',
|
||||
title = '',
|
||||
description = ''
|
||||
}: Props = $props();
|
||||
let { className = '', bannerImage, slug = '', title = '', description = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bg-white border-2 border-black rounded-xl overflow-hidden w-full h-full pb-2 min-w-[80%] md:max-w-[350px] md:min-w-[300px] max-w-full space-y-4"
|
||||
class="h-full w-full min-w-[80%] max-w-full space-y-4 overflow-hidden rounded-xl border-2 border-black bg-white pb-2 md:min-w-[300px] md:max-w-[350px]"
|
||||
>
|
||||
<div class="relative space-y-2 text-black {className}">
|
||||
<div class="border-b-2 border-black border-dashed">
|
||||
<img
|
||||
src={bannerImage ? bannerImage : '/classroomio-course-img-template.jpg'}
|
||||
alt=""
|
||||
class="w-full h-44"
|
||||
/>
|
||||
<div class="border-b-2 border-dashed border-black">
|
||||
<img src={bannerImage || '/course-banner.jpg'} alt="" class="h-44 w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="px-3 py-2 overflow-hidden">
|
||||
<p class="text-lg font-semibold overflow-ellipsis line-clamp-1">
|
||||
<div class="overflow-hidden px-3 py-2">
|
||||
<p class="line-clamp-1 overflow-ellipsis text-lg font-semibold">
|
||||
{title}
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-sm text-[#656565] px-3 overflow-ellipsis line-clamp-3 text-justify">
|
||||
<p class="line-clamp-3 overflow-ellipsis px-3 text-justify text-sm text-[#656565]">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class=" w-full py-4 px-6">
|
||||
<div class=" w-full px-6 py-4">
|
||||
<PrimaryButton href={`/course/${slug}`} label="Go to Course" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { homePage } from '$lib/utils/stores/pages';
|
||||
import Accordion from './Accordion.svelte';
|
||||
|
||||
const content = $derived(getPageSection($homePage, 'faq'));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<section id="faq" class="relative px-2 pt-4 pb-20 h-full">
|
||||
<div class="absolute inset-0 bg-[#E5E7E0] opacity-60"></div>
|
||||
|
||||
<div class="relative">
|
||||
<h1 class="text-center text-5xl font-bold mb-4">
|
||||
{content.settings.title}
|
||||
</h1>
|
||||
<h1 class="text-center text-lg font-semibold mb-4 w-full md:w-[80%] lg:w-[60%] mx-auto">
|
||||
{content.settings.subtitle}
|
||||
</h1>
|
||||
<section class="py-4 px-2 space-y-10 w-full md:w-[80%] mx-auto">
|
||||
{#each content.settings.questions as faq}
|
||||
<Accordion title={faq.title} content={faq.content} />
|
||||
{/each}
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { sharedPage } from '$lib/utils/stores/pages';
|
||||
|
||||
@@ -7,20 +8,18 @@
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<nav class="w-full py-4 px-4 lg:px-14 bg-[#F4F4F4]">
|
||||
<nav class="w-full bg-[#F4F4F4] px-4 py-4 lg:px-14">
|
||||
<div
|
||||
class="bg-[#2E2E2F] rounded-lg p-4 text-white w-full flex flex-col md:flex-row items-start md:items-center gap-4 md:justify-between"
|
||||
class="flex w-full flex-col items-start gap-4 rounded-lg bg-[#2E2E2F] p-4 text-white md:flex-row md:items-center md:justify-between"
|
||||
>
|
||||
<a href="/#" class="p-2 bg-white rounded-lg">
|
||||
<img
|
||||
<Logo
|
||||
src={seo?.settings.logo}
|
||||
alt={`${seo?.settings.title} logo`}
|
||||
class="rounded bg-white inline-block mx-auto"
|
||||
data-atf="1"
|
||||
alt={seo?.settings.title}
|
||||
className="bg-white rounded-lg p-2 w-28"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<ul
|
||||
class="flex flex-col md:flex-row items-start md:items-center gap-4 -md:gap-8 font-semibold capitalize underline"
|
||||
class="-md:gap-8 flex flex-col items-start gap-4 font-semibold capitalize underline md:flex-row md:items-center"
|
||||
>
|
||||
{#if content.settings.twitter}
|
||||
<a href={content.settings.twitter} target="_blank" title="twitter">Twitter</a>
|
||||
@@ -38,7 +37,7 @@
|
||||
<a
|
||||
href="https://git.new/cio-template"
|
||||
target="_blank noopener noreferrer"
|
||||
class="flex w-full md:w-fit items-center justify-center lg:justify-start hover:underline text-white gap-1"
|
||||
class="flex w-full items-center justify-center gap-1 text-white hover:underline md:w-fit lg:justify-start"
|
||||
>
|
||||
<p class="text-base font-bold">Built with ❤️ by ClassroomIO</p>
|
||||
</a>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import PrimaryButton from './PrimaryButton.svelte';
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { homePage } from '$lib/utils/stores/pages';
|
||||
</script>
|
||||
|
||||
{#if $homePage}
|
||||
{@const content = getPageSection($homePage, 'header')}
|
||||
{#if content?.show}
|
||||
<section
|
||||
class="font-matter flex items-start md:items-center justify-center py-10 lg:py-20 px-5 lg:px-20 h-full bg-[#F4F4F4]"
|
||||
>
|
||||
<section
|
||||
class="flex flex-col-reverse lg:flex-row items-center justify-center lg:justify-between"
|
||||
>
|
||||
<div class="text-center lg:text-start space-y-6 w-full mt-4 lg:mt-0">
|
||||
<div
|
||||
class="bg-[#E5E7E0] py-1 px-4 md:border-b border-black rounded-sm w-fit mx-auto lg:mx-0"
|
||||
>
|
||||
<p class="uppercase font-bold text-xs lg:text-base text-[#0F163F]">
|
||||
{content.settings.title}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="text-4xl md:text-6xl font-black w-full mx-auto lg:mx-0 md:w-[90%] lg:w-[80%] capitalize"
|
||||
>
|
||||
{content.settings.titleHighlight}
|
||||
</p>
|
||||
<p class="font-semibold text-xl w-full md:w-[80%] mx-auto lg:mx-0 lg:w-[70%]">
|
||||
{content.settings.subtitle}
|
||||
</p>
|
||||
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
goto(content.settings.action.link);
|
||||
}}
|
||||
label={content.settings.action.label}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="relative w-[250px] h-[250px] lg:w-[500px] lg:h-[350px] flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
class="absolute -left-[15px] lg:-left-[60px] xl:-left-[100px] w-[270px] h-[270px] lg:w-[400px] xl:w-[520px] lg:h-[370px]"
|
||||
viewBox="0 0 360 360"
|
||||
>
|
||||
<circle
|
||||
cx="180"
|
||||
cy="180"
|
||||
r="170"
|
||||
fill="none"
|
||||
stroke="#282828"
|
||||
stroke-width="2"
|
||||
stroke-dasharray="15, 10"
|
||||
/>
|
||||
</svg>
|
||||
<img
|
||||
alt="landing page banner"
|
||||
src={content.settings.banner?.image
|
||||
? content.settings.banner?.image
|
||||
: '/calcom-banner-img.jpeg'}
|
||||
class="object-cover rounded-full w-[250px] h-[250px] lg:w-[500px] lg:h-[350px]"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -5,6 +5,7 @@
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import type { Section } from '$lib/utils/types/page';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
@@ -17,22 +18,13 @@
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class="relative md:sticky top-0 z-[3000] w-full flex items-center justify-between bg-[#F4F4F4] border-b border-[#D0D1C9] py-4 px-6"
|
||||
class="relative top-0 z-[3000] flex w-full items-center justify-between border-b border-[#D0D1C9] bg-[#F4F4F4] px-6 py-4 md:sticky"
|
||||
>
|
||||
<!-- Logo Section -->
|
||||
<a href="/" class="flex items-center flex-col" title={`${seo?.settings?.title}`} id="logo">
|
||||
{#if seo?.settings?.logo}
|
||||
<img
|
||||
src={seo?.settings?.logo || ''}
|
||||
alt={`${seo?.settings?.title || ''} logo`}
|
||||
class="rounded inline-block mx-auto"
|
||||
/>
|
||||
{/if}
|
||||
<h1 class="text-xl font-semibold">University</h1>
|
||||
</a>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} className="w-24" addAcademy />
|
||||
|
||||
<ul
|
||||
class="hidden lg:flex items-center gap-14 text-base font-semibold text-[#1F2937] list-none hover:no-underline"
|
||||
class="hidden list-none items-center gap-14 text-base font-semibold text-[#1F2937] hover:no-underline lg:flex"
|
||||
>
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li>
|
||||
@@ -52,7 +44,7 @@
|
||||
|
||||
<Button
|
||||
href="/courses"
|
||||
class="bg-[#141414] rounded-full hidden lg:flex items-center gap-1 font-semibold text-white text-base"
|
||||
class="hidden items-center gap-1 rounded-full bg-[#141414] text-base font-semibold text-white lg:flex"
|
||||
>
|
||||
Explore Courses
|
||||
</Button>
|
||||
@@ -64,17 +56,17 @@
|
||||
|
||||
<!-- Mobile Sidebar Menu (Visible only on mobile) -->
|
||||
<ul
|
||||
class={`fixed top-0 left-0 pt-10 h-full bg-white w-full transform hover:no-underline ${
|
||||
class={`fixed left-0 top-0 h-full w-full transform bg-white pt-10 hover:no-underline ${
|
||||
open ? 'translate-y-0 ' : '-translate-y-full'
|
||||
} transition-transform duration-300 ease-in-out lg:hidden text-base font-semibold text-[#1F2937] list-none cursor-pointer`}
|
||||
} cursor-pointer list-none text-base font-semibold text-[#1F2937] transition-transform duration-300 ease-in-out lg:hidden`}
|
||||
>
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 lg:hidden z-20">
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 z-20 lg:hidden">
|
||||
<Close size={24} />
|
||||
</button>
|
||||
|
||||
<div class="divide-y divide-solid border-b">
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li class="py-4 px-6">
|
||||
<li class="px-6 py-4">
|
||||
<a href={navItem.link} onclick={toggleMenu}>{navItem.title}</a>
|
||||
</li>
|
||||
{/each}
|
||||
@@ -82,7 +74,7 @@
|
||||
|
||||
<Button
|
||||
href="/courses"
|
||||
class="flex items-center mx-auto justify-center gap-1 py-4 px-6 bg-[#141414] w-fit my-4 font-semibold rounded-full text-white text-base"
|
||||
class="mx-auto my-4 flex w-fit items-center justify-center gap-1 rounded-full bg-[#141414] px-6 py-4 text-base font-semibold text-white"
|
||||
on:click={toggleMenu}
|
||||
>
|
||||
Explore Courses
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 3.4 MiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -6,6 +6,7 @@
|
||||
import { COURSE_TYPE } from '$lib/utils/constants/course';
|
||||
import type { CourseFilterItem, Course } from '$lib/utils/types/course';
|
||||
import type { Page } from '$lib/utils/types/page';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
@@ -41,8 +42,8 @@
|
||||
* Constants
|
||||
*/
|
||||
const section = $derived({
|
||||
header: getPageSection(data.page, 'header'),
|
||||
courses: getPageSection(data.page, 'courses')
|
||||
header: getPageSection(data.page, SECTION.HERO),
|
||||
courses: getPageSection(data.page, SECTION.COURSE)
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -66,20 +67,20 @@
|
||||
<div class="w-full">
|
||||
{#if section.header?.show}
|
||||
<section
|
||||
style="background-image: url('calcom-background.svg');"
|
||||
class="bg-cover bg-center flex items-center justify-center py-10 px-2 lg:px-14 min-h-[30vh]"
|
||||
id="courses-header"
|
||||
class="flex min-h-[30vh] items-center justify-center bg-cover bg-center px-2 py-10 lg:px-14"
|
||||
>
|
||||
<section class="flex flex-col text-center items-center gap-5 justify-center">
|
||||
<div class="space-y-6 w-full mb-4">
|
||||
<div class="bg-[#E5E7E0] py-1 px-4 md:border-b border-black rounded-sm w-fit mx-auto">
|
||||
<p class="uppercase font-bold text-xs lg:text-base text-[#0F163F]">
|
||||
<section class="flex flex-col items-center justify-center gap-5 text-center">
|
||||
<div class="mb-4 w-full space-y-6">
|
||||
<div class="mx-auto w-fit rounded-sm border-black bg-[#E5E7E0] px-4 py-1 md:border-b">
|
||||
<p class="text-xs font-bold uppercase text-[#0F163F] lg:text-base">
|
||||
{section.header.settings.title}
|
||||
</p>
|
||||
</div>
|
||||
<p class="w-full text-5xl font-bold md:w-[70%] mx-auto">
|
||||
<p class="mx-auto w-full text-5xl font-bold md:w-[70%]">
|
||||
{section.header.settings.titleHighlight}
|
||||
</p>
|
||||
<p class="w-full font-semibold text-lg md:w-[70%] mx-auto">
|
||||
<p class="mx-auto w-full text-lg font-semibold md:w-[70%]">
|
||||
{section.header.settings.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
@@ -88,15 +89,15 @@
|
||||
{/if}
|
||||
|
||||
{#if section.courses?.show}
|
||||
<section id="course" class="px-2 pt-4 pb-20 h-full lg:px-16">
|
||||
<div class="relative w-full flex flex-row items-start justify-start lg:gap-2 mt-10">
|
||||
<section id="course" class="h-full px-2 pb-20 pt-4 lg:px-16">
|
||||
<div class="relative mt-10 flex w-full flex-row items-start justify-start lg:gap-2">
|
||||
<!-- filter start -->
|
||||
<div class="hidden lg:block sticky top-0 min-w-max">
|
||||
<p class="font-semibold mb-4">Filter by</p>
|
||||
<div class="sticky top-0 hidden min-w-max lg:block">
|
||||
<p class="mb-4 font-semibold">Filter by</p>
|
||||
<div class="w-full space-y-2">
|
||||
{#each filter as item}
|
||||
<form
|
||||
class="space-x-2 text-[#3C4043] font-medium border border-[#D0D1C9] bg-[#F4F4F4] rounded-md pl-4 pr-8 py-4"
|
||||
class="space-x-2 rounded-md border border-[#D0D1C9] bg-[#F4F4F4] py-4 pl-4 pr-8 font-medium text-[#3C4043]"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -113,11 +114,11 @@
|
||||
</div>
|
||||
<!-- filter end -->
|
||||
|
||||
<div class="text-white rounded-3xl w-full h-full mx-auto">
|
||||
<div class="mx-auto h-full w-full rounded-3xl text-white">
|
||||
<!-- courses start -->
|
||||
<div class="w-full max-h-screen p-4 md:p-8">
|
||||
<div class="max-h-screen w-full p-4 md:p-8">
|
||||
{#if data.courses.length > 0}
|
||||
<section class="flex flex-wrap items-center justify-center gap-4 w-full">
|
||||
<section class="flex w-full flex-wrap items-center justify-center gap-4">
|
||||
{#each filteredCourses.slice(0, viewAll ? filteredCourses.length : 3) as course}
|
||||
<CourseCard
|
||||
slug={course.slug}
|
||||
@@ -128,12 +129,12 @@
|
||||
{/each}
|
||||
</section>
|
||||
{#if filteredCourses.length == 0}
|
||||
<div class="px-4 w-full mx-auto">
|
||||
<div class="mx-auto w-full px-4">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="px-4 w-full mx-auto">
|
||||
<div class="mx-auto w-full px-4">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -141,10 +142,10 @@
|
||||
<!-- courses end -->
|
||||
|
||||
{#if filteredCourses.length > 2}
|
||||
<div class="w-full flex items-center justify-center py-8">
|
||||
<div class="flex w-full items-center justify-center py-8">
|
||||
<Button
|
||||
on:click={() => (viewAll = !viewAll)}
|
||||
class="uppercase rounded-2xl py-6 hover:bg-white hover:text-black border-[1.5px] border-white text-white shadow-[0px_3px_white]"
|
||||
class="rounded-2xl border-[1.5px] border-white py-6 uppercase text-white shadow-[0px_3px_white] hover:bg-white hover:text-black"
|
||||
>
|
||||
{viewAll === true ? 'Show less' : 'View more courses'}
|
||||
</Button>
|
||||
@@ -156,3 +157,9 @@
|
||||
{/if}
|
||||
<!-- course end -->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#courses-header {
|
||||
background-image: url('$lib/components/cal/assets/calcom-background.svg');
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
import { courses } from '$lib/utils/stores/course';
|
||||
import { CheckmarkFilled } from 'carbon-icons-svelte';
|
||||
import Accordion from '../Accordion.svelte';
|
||||
import bannerImg from '../assets/banner.jpeg';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
/**
|
||||
* State
|
||||
@@ -21,11 +23,11 @@
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
const coursesSection = $derived(getPageSection($homePage, 'courses'));
|
||||
const aboutSection = $derived(getPageSection($homePage, 'about'));
|
||||
const faqSection = $derived(getPageSection($homePage, 'faq'));
|
||||
const testimonialSection = $derived(getPageSection($homePage, 'testimonial'));
|
||||
const footerNoteSection = $derived(getPageSection($homePage, 'cta'));
|
||||
const coursesSection = $derived(getPageSection($homePage, SECTION.COURSE));
|
||||
const aboutSection = $derived(getPageSection($homePage, SECTION.ABOUT));
|
||||
const faqSection = $derived(getPageSection($homePage, SECTION.FAQ));
|
||||
const testimonialSection = $derived(getPageSection($homePage, SECTION.TESTIMONIAL));
|
||||
const ctaSection = $derived(getPageSection($homePage, SECTION.CTA));
|
||||
|
||||
/**
|
||||
* Functions
|
||||
@@ -41,28 +43,28 @@
|
||||
|
||||
<!-- hero -->
|
||||
{#if $homePage}
|
||||
{@const content = getPageSection($homePage, 'header')}
|
||||
{@const content = getPageSection($homePage, SECTION.HERO)}
|
||||
{#if content?.show}
|
||||
<section
|
||||
class="font-matter flex items-start md:items-center justify-center py-10 lg:py-20 px-5 lg:px-20 h-full bg-[#F4F4F4]"
|
||||
class="font-matter flex h-full items-start justify-center bg-[#F4F4F4] px-5 py-10 md:items-center lg:px-20 lg:py-20"
|
||||
>
|
||||
<section
|
||||
class="flex flex-col-reverse lg:flex-row items-center justify-center lg:justify-between"
|
||||
class="flex flex-col-reverse items-center justify-center lg:flex-row lg:justify-between"
|
||||
>
|
||||
<div class="text-center lg:text-start space-y-6 w-full mt-4 lg:mt-0">
|
||||
<div class="mt-4 w-full space-y-6 text-center lg:mt-0 lg:text-start">
|
||||
<div
|
||||
class="bg-[#E5E7E0] py-1 px-4 md:border-b border-black rounded-sm w-fit mx-auto lg:mx-0"
|
||||
class="mx-auto w-fit rounded-sm border-black bg-[#E5E7E0] px-4 py-1 md:border-b lg:mx-0"
|
||||
>
|
||||
<p class="uppercase font-bold text-xs lg:text-base text-[#0F163F]">
|
||||
<p class="text-xs font-bold uppercase text-[#0F163F] lg:text-base">
|
||||
{content.settings.title}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="text-4xl md:text-6xl font-black w-full mx-auto lg:mx-0 md:w-[90%] lg:w-[80%] capitalize"
|
||||
class="mx-auto w-full text-4xl font-black capitalize md:w-[90%] md:text-6xl lg:mx-0 lg:w-[80%]"
|
||||
>
|
||||
{content.settings.titleHighlight}
|
||||
</p>
|
||||
<p class="font-semibold text-xl w-full md:w-[80%] mx-auto lg:mx-0 lg:w-[70%]">
|
||||
<p class="mx-auto w-full text-xl font-semibold md:w-[80%] lg:mx-0 lg:w-[70%]">
|
||||
{content.settings.subtitle}
|
||||
</p>
|
||||
|
||||
@@ -75,10 +77,10 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="relative w-[250px] h-[250px] lg:w-[500px] lg:h-[350px] flex items-center justify-center"
|
||||
class="relative flex h-[250px] w-[250px] items-center justify-center lg:h-[350px] lg:w-[500px]"
|
||||
>
|
||||
<svg
|
||||
class="absolute -left-[15px] lg:-left-[60px] xl:-left-[100px] w-[270px] h-[270px] lg:w-[400px] xl:w-[520px] lg:h-[370px]"
|
||||
class="absolute -left-[15px] h-[270px] w-[270px] lg:-left-[60px] lg:h-[370px] lg:w-[400px] xl:-left-[100px] xl:w-[520px]"
|
||||
viewBox="0 0 360 360"
|
||||
>
|
||||
<circle
|
||||
@@ -93,10 +95,8 @@
|
||||
</svg>
|
||||
<img
|
||||
alt="landing page banner"
|
||||
src={content.settings.banner?.image
|
||||
? content.settings.banner?.image
|
||||
: '/calcom-banner-img.jpeg'}
|
||||
class="object-cover rounded-full w-[250px] h-[250px] lg:w-[500px] lg:h-[350px]"
|
||||
src={content.settings.banner?.image || bannerImg}
|
||||
class="h-[250px] w-[250px] rounded-full object-cover lg:h-[350px] lg:w-[500px]"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
@@ -106,26 +106,26 @@
|
||||
|
||||
<!-- courses -->
|
||||
{#if coursesSection?.show}
|
||||
<section id="course" class="flex items-center px-2 md:px-8 py-12 h-full bg-[#E5E7E0]">
|
||||
<div class="text-white py-8 bg-black rounded-3xl w-full lg:w-[90%] mx-auto">
|
||||
<section id="course" class="flex h-full items-center bg-[#E5E7E0] px-2 py-12 md:px-8">
|
||||
<div class="mx-auto w-full rounded-3xl bg-black py-8 text-white lg:w-[90%]">
|
||||
<div
|
||||
class="flex flex-col md:flex-row items-center justify-center md:items-start gap-2 md:justify-between px-4 lg:px-8"
|
||||
class="flex flex-col items-center justify-center gap-2 px-4 md:flex-row md:items-start md:justify-between lg:px-8"
|
||||
>
|
||||
<h1
|
||||
class="text-white text-start text-2xl md:text-3xl lg:text-5xl mb-2 font-bold w-full md:w-[80%] lg:w-[60%]"
|
||||
class="mb-2 w-full text-start text-2xl font-bold text-white md:w-[80%] md:text-3xl lg:w-[60%] lg:text-5xl"
|
||||
>
|
||||
{coursesSection.settings.title}
|
||||
</h1>
|
||||
|
||||
<div class="hidden md:flex gap-2 items-center">
|
||||
<div class="hidden items-center gap-2 md:flex">
|
||||
<button
|
||||
class="w-fit flex items-center justify-center border border-white rounded-full p-2 bg-transparent hover:bg-white hover:text-black"
|
||||
class="flex w-fit items-center justify-center rounded-full border border-white bg-transparent p-2 hover:bg-white hover:text-black"
|
||||
onclick={scrollLeft}
|
||||
>
|
||||
<ArrowLeft />
|
||||
</button>
|
||||
<button
|
||||
class="w-fit flex items-center justify-center border border-white rounded-full p-2 bg-transparent hover:bg-white hover:text-black"
|
||||
class="flex w-fit items-center justify-center rounded-full border border-white bg-transparent p-2 hover:bg-white hover:text-black"
|
||||
onclick={scrollRight}
|
||||
>
|
||||
<ArrowRight />
|
||||
@@ -133,11 +133,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full pl-4 md:pl-8 overflow-x-hidden">
|
||||
<div class="w-full overflow-x-hidden pl-4 md:pl-8">
|
||||
{#if $courses.length > 0}
|
||||
<section
|
||||
bind:this={scrollContainer}
|
||||
class="flex items-center overflow-x-auto gap-4 py-4 pr-2 w-full scrollbar-hide"
|
||||
class="scrollbar-hide flex w-full items-center gap-4 overflow-x-auto py-4 pr-2"
|
||||
>
|
||||
{#each $courses as course}
|
||||
<CourseCard
|
||||
@@ -160,15 +160,15 @@
|
||||
{/each} -->
|
||||
</section>
|
||||
{:else}
|
||||
<div class="w-full mx-auto">
|
||||
<div class="mx-auto w-full">
|
||||
<EmptyState className="dark:bg-[#232429] dark:border-[#EAEAEA]" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center w-full pt-4">
|
||||
<div class="flex w-full items-center justify-center pt-4">
|
||||
<Button
|
||||
class="uppercase rounded-2xl py-6 px-10 bg-transparent border-[1.5px] border-white text-white shadow-[-1px_3px_#FFFFFF]"
|
||||
class="rounded-2xl border-[1.5px] border-white bg-transparent px-10 py-6 uppercase text-white shadow-[-1px_3px_#FFFFFF]"
|
||||
href="/courses"
|
||||
>
|
||||
Explore courses
|
||||
@@ -180,24 +180,24 @@
|
||||
<!-- about -->
|
||||
{#if aboutSection?.show}
|
||||
<section id="about">
|
||||
<div class="relative px-4 md:px-10 py-10 h-full w-full mx-auto xl:w-[80%]">
|
||||
<h1 class="text-center text-5xl mb-2 font-bold md:w-[60%] mx-auto">
|
||||
<div class="relative mx-auto h-full w-full px-4 py-10 md:px-10 xl:w-[80%]">
|
||||
<h1 class="mx-auto mb-2 text-center text-5xl font-bold md:w-[60%]">
|
||||
{aboutSection.settings.title}
|
||||
</h1>
|
||||
<p class="text-center dark:text-white text-lg font-semibold md:w-[70%] mb-8 mx-auto">
|
||||
<p class="mx-auto mb-8 text-center text-lg font-semibold dark:text-white md:w-[70%]">
|
||||
{aboutSection.settings.subtitle}
|
||||
</p>
|
||||
<div>
|
||||
<section class="flex flex-wrap items-start justify-center w-full gap-6 py-4">
|
||||
<section class="flex w-full flex-wrap items-start justify-center gap-6 py-4">
|
||||
{#each aboutSection.settings.benefits.list as item}
|
||||
<div
|
||||
class="flex flex-col gap-2 p-4 bg-white border border-[#282828] h-[130px] max-h-[180px] w-full max-w-full md:max-w-[320px] lg:max-w-[450px] rounded-lg overflow-hidden"
|
||||
class="flex h-[130px] max-h-[180px] w-full max-w-full flex-col gap-2 overflow-hidden rounded-lg border border-[#282828] bg-white p-4 md:max-w-[320px] lg:max-w-[450px]"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<CheckmarkFilled size={24} />
|
||||
<p class="text-lg capitalize line-clamp-1">{item.title}</p>
|
||||
<p class="line-clamp-1 text-lg capitalize">{item.title}</p>
|
||||
</span>
|
||||
<p class="text-sm text-[#878787] line-clamp-3">{item.subtitle}</p>
|
||||
<p class="line-clamp-3 text-sm text-[#878787]">{item.subtitle}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
@@ -208,17 +208,17 @@
|
||||
|
||||
<!-- faq -->
|
||||
{#if faqSection?.show}
|
||||
<section id="faq" class="relative px-2 pt-4 pb-20 h-full">
|
||||
<section id="faq" class="relative h-full px-2 pb-20 pt-4">
|
||||
<div class="absolute inset-0 bg-[#E5E7E0] opacity-60"></div>
|
||||
|
||||
<div class="relative">
|
||||
<h1 class="text-center text-5xl font-bold mb-4">
|
||||
<h1 class="mb-4 text-center text-5xl font-bold">
|
||||
{faqSection.settings.title}
|
||||
</h1>
|
||||
<h1 class="text-center text-lg font-semibold mb-4 w-full md:w-[80%] lg:w-[60%] mx-auto">
|
||||
<h1 class="mx-auto mb-4 w-full text-center text-lg font-semibold md:w-[80%] lg:w-[60%]">
|
||||
{faqSection.settings.subtitle}
|
||||
</h1>
|
||||
<section class="py-4 px-2 space-y-10 w-full md:w-[80%] mx-auto">
|
||||
<section class="mx-auto w-full space-y-10 px-2 py-4 md:w-[80%]">
|
||||
{#each faqSection.settings.questions as faq}
|
||||
<Accordion title={faq.title} content={faq.content} />
|
||||
{/each}
|
||||
@@ -229,18 +229,18 @@
|
||||
|
||||
<!-- testimonial -->
|
||||
{#if testimonialSection?.show}
|
||||
<section id="testimonial" class="bg-[#F4F4F4] px-4 lg:p-14 h-fit">
|
||||
<section class="coolumns-1 md:columns-3 space-y-6 overflow-visible">
|
||||
<section id="testimonial" class="h-fit bg-[#F4F4F4] px-4 lg:p-14">
|
||||
<section class="coolumns-1 space-y-6 overflow-visible md:columns-3">
|
||||
{#each testimonialSection.settings.list as item}
|
||||
<div class="break-inside-avoid overflow-visible">
|
||||
<section
|
||||
class="cursor-pointer flex flex-col relative overflow-visible px-6 pt-6 gap-4 p-4 bg-white border border-[#D7D7D7] rounded-3xl w-full h-fit max-w-full sm:max-w-[320px] xl:max-w-[400px]"
|
||||
class="relative flex h-fit w-full max-w-full cursor-pointer flex-col gap-4 overflow-visible rounded-3xl border border-[#D7D7D7] bg-white p-4 px-6 pt-6 sm:max-w-[320px] xl:max-w-[400px]"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<img
|
||||
src={item.banner ?? '/classroomio-course-img-template.jpg'}
|
||||
src={item.banner ?? '/course-banner.jpg'}
|
||||
alt=""
|
||||
class="w-10 h-10 rounded-full"
|
||||
class="h-10 w-10 rounded-full"
|
||||
/>
|
||||
<span class="flex flex-col items-start">
|
||||
<p class="text-sm font-bold">{item.name}</p>
|
||||
@@ -259,30 +259,31 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- footernote -->
|
||||
{#if footerNoteSection?.show}
|
||||
<section class="w-full bg-[#F4F4F4] py-16 px-4 lg:px-10">
|
||||
<!-- CTA -->
|
||||
{#if ctaSection?.show}
|
||||
<section class="w-full bg-[#F4F4F4] px-4 py-16 lg:px-10">
|
||||
<div
|
||||
class="relative flex flex-col md:flex-row min-h-40 justify-center border-2 border-[#141414] rounded-xl divide-x divide-transparent w-full lg:w-[80%] mx-auto"
|
||||
class="min-h-40 relative mx-auto flex w-full flex-col justify-center divide-x divide-transparent rounded-xl border-2 border-[#141414] md:flex-row lg:w-[80%]"
|
||||
>
|
||||
<div
|
||||
class="bg-[#E5E7E0] text-center md:text-start rounded-t-xl md:rounded-l-xl flex justify-center capitalize items-center py-20 px-4 lg:px-10 w-full md:w-[65%] min-h-40 text-4xl font-bold"
|
||||
class="min-h-40 flex w-full items-center justify-center rounded-t-xl bg-[#E5E7E0] px-4 py-20 text-center text-4xl font-bold capitalize md:w-[65%] md:rounded-l-xl md:text-start lg:px-10"
|
||||
>
|
||||
<h1 class="leading-normal">
|
||||
{footerNoteSection.settings.title}<br />{footerNoteSection.settings.titleHighlight}
|
||||
{ctaSection.settings.title}<br />{ctaSection.settings.titleHighlight}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-b-xl md:rounded-r-xl flex items-center justify-center py-16 px-4 bg-white w-full md:w-[35%] min-h-40 text-lg font-semibold"
|
||||
class="min-h-40 flex w-full items-center justify-center rounded-b-xl bg-white px-4 py-16 text-lg font-semibold md:w-[35%] md:rounded-r-xl"
|
||||
>
|
||||
<p>
|
||||
{footerNoteSection.settings.subtitle}
|
||||
{ctaSection.settings.subtitle}
|
||||
</p>
|
||||
<Button
|
||||
class="absolute -bottom-5 uppercase rounded-2xl py-6 bg-white hover:bg-white hover:text-black border-[1.5px] border-[#141414] text-[#141414] shadow-[0px_3px_#141414]"
|
||||
class="absolute -bottom-5 rounded-2xl border-[1.5px] border-[#141414] bg-white py-6 uppercase text-[#141414] shadow-[0px_3px_#141414] hover:bg-white hover:text-black"
|
||||
href={ctaSection.settings?.button?.link}
|
||||
>
|
||||
{footerNoteSection.settings.buttonLabel}
|
||||
{ctaSection.settings?.button?.label}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="hover:scale-95 border border-[#EAEAEA] bg-[#FCFCFC] dark:border-neutral-600 rounded w-full max-w-[300px] space-y-4 p-4"
|
||||
class="w-full max-w-[300px] space-y-4 rounded border border-[#EAEAEA] bg-[#FCFCFC] p-4 hover:scale-95 dark:border-neutral-600"
|
||||
>
|
||||
<img src="/classroomio-course-img-template.jpg" alt="tutor" class="object-cover w-full h-full" />
|
||||
<img src="/course-banner.jpg" alt="tutor" class="h-full w-full object-cover" />
|
||||
<div class="w-full space-y-2">
|
||||
<p class="text-lg font-semibold line-clamp-2">
|
||||
<p class="line-clamp-2 text-lg font-semibold">
|
||||
How to Choose the Right Online Course to Boost Your Career
|
||||
</p>
|
||||
<span class="inline-flex items-center gap-1 text-[#282828]">
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
<script lang="ts">
|
||||
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';
|
||||
|
||||
const COURSE_TYPE = {
|
||||
LIVE_CLASS: 'live',
|
||||
SELF_PACED: 'paced'
|
||||
};
|
||||
|
||||
interface Props {
|
||||
bannerImage: string | undefined;
|
||||
slug?: string;
|
||||
@@ -26,76 +17,37 @@
|
||||
description = '',
|
||||
cost = 0,
|
||||
totalLessons = 0,
|
||||
currency = 'USD',
|
||||
type
|
||||
currency = 'USD'
|
||||
}: Props = $props();
|
||||
let target: any = $state();
|
||||
|
||||
function getCourseUrl() {
|
||||
return `/course/${slug}`;
|
||||
}
|
||||
const COURSE_TAG: Record<
|
||||
string,
|
||||
{
|
||||
style: string;
|
||||
label: string;
|
||||
icon: any;
|
||||
iconStyle?: string;
|
||||
}
|
||||
> = {
|
||||
[COURSE_TYPE.LIVE_CLASS]: {
|
||||
style: '',
|
||||
label: 'live',
|
||||
icon: RadioButtonChecked,
|
||||
iconStyle: 'text-red-700'
|
||||
},
|
||||
[COURSE_TYPE.SELF_PACED]: {
|
||||
style: '',
|
||||
label: 'paced',
|
||||
icon: UserProfileIcon,
|
||||
iconStyle: 'text-primary-700'
|
||||
},
|
||||
SPECIALIZATION: {
|
||||
style: '',
|
||||
label: 'specialized',
|
||||
icon: GrowthIcon
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<a
|
||||
rel="prefetch"
|
||||
bind:this={target}
|
||||
href={getCourseUrl()}
|
||||
class="text-black border border-gray dark:border-neutral-600 rounded w-full h-fit md:h-[350px] min-w-[250px] max-w-[300px] relative hover:scale-95 transition-all ease-in-out"
|
||||
class="border-gray relative h-fit w-full min-w-[250px] max-w-[300px] rounded border text-black transition-all ease-in-out hover:scale-95 dark:border-neutral-600 md:h-[350px]"
|
||||
>
|
||||
<div class="p-4">
|
||||
<div class="relative mb-5">
|
||||
<img
|
||||
src={bannerImage || '/classroomio-course-img-template.jpg'}
|
||||
src={bannerImage || '/course-banner.jpg'}
|
||||
alt="Course Logo"
|
||||
class="h-[170px] w-full rounded dark:border dark:border-neutral-600 relative"
|
||||
class="relative h-[170px] w-full rounded dark:border dark:border-neutral-600"
|
||||
/>
|
||||
{#if type}
|
||||
{@const tag = COURSE_TAG[type]}
|
||||
<span
|
||||
class="absolute bottom-2 left-2 z-10 text-xs capitalize bg-primary-50 rounded-sm p-1 flex items-center gap-1 font-mono"
|
||||
>
|
||||
<tag.icon size={16} class={tag.iconStyle} />
|
||||
{tag.label}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<h3 class="text-xl dark:text-white font-semibold line-clamp-1">{title}</h3>
|
||||
<p class="mt-2 text-sm text-gray-500 dark:text-gray-300 description">
|
||||
<h3 class="line-clamp-1 text-xl font-semibold dark:text-white">{title}</h3>
|
||||
<p class="description mt-2 text-sm text-gray-500 dark:text-gray-300">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="py-4 mx-2 border-t border-gray dark:border-neutral-600 flex justify-between">
|
||||
<div class="border-gray mx-2 flex justify-between border-t py-4 dark:border-neutral-600">
|
||||
<div>
|
||||
<p class="text-xs pl-2 dark:text-white">
|
||||
<p class="pl-2 text-xs dark:text-white">
|
||||
{totalLessons}
|
||||
lessons
|
||||
</p>
|
||||
|
||||
@@ -5,65 +5,71 @@
|
||||
import LogoLinkedin from 'carbon-icons-svelte/lib/LogoLinkedin.svelte';
|
||||
import LogoInstagram from 'carbon-icons-svelte/lib/LogoInstagram.svelte';
|
||||
import { LogoYoutube } from 'carbon-icons-svelte';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const content = $derived(getPageSection($sharedPage, 'footer'));
|
||||
const seo = $derived(getPageSection($sharedPage, 'seo'));
|
||||
const content = $derived(getPageSection($sharedPage, SECTION.FOOTER));
|
||||
const seo = $derived(getPageSection($sharedPage, SECTION.SEO));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<nav
|
||||
class="bg-white w-full flex flex-col md:flex-row items-start md:items-center gap-4 md:justify-between py-4 px-6"
|
||||
class="flex w-full flex-col items-start gap-4 bg-white px-6 py-4 md:flex-row md:items-center md:justify-between"
|
||||
>
|
||||
<div class="logo w-full md:w-fit flex items-center justify-between">
|
||||
<a href="/" title={`Go to ${seo?.settings.title} Home`} id="logo" data-hveid="8">
|
||||
<img
|
||||
src={seo?.settings.logo}
|
||||
alt={`${seo?.settings.title} logo`}
|
||||
class="rounded w-9 inline-block mx-auto"
|
||||
data-atf="1"
|
||||
/>
|
||||
</a>
|
||||
<div class="logo flex w-full items-center justify-between md:w-fit">
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} className="" />
|
||||
|
||||
<a href="/#" class="md:hidden flex items-center gap-1">
|
||||
<p class=" text-[#0233BD] text-base underline font-semibold">Built on ClassroomIO</p>
|
||||
<a
|
||||
href="https://classroomio.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-1 md:hidden"
|
||||
>
|
||||
<p class=" text-base font-semibold text-[#0233BD] underline">Built on ClassroomIO</p>
|
||||
</a>
|
||||
</div>
|
||||
<ul
|
||||
class="my-4 md:my-0 mx-auto flex flex-row items-center justify-center gap-8 underline border rounded-full px-6 py-2"
|
||||
class="mx-auto my-4 flex flex-row items-center justify-center gap-8 rounded-full border px-6 py-2 underline md:my-0"
|
||||
>
|
||||
{#if content.settings.instagram}
|
||||
<a href={content.settings.instagram} target="_blank" title="instragram"
|
||||
><LogoInstagram class="fill-red-500 " size={24} /></a
|
||||
>
|
||||
<a href={content.settings.instagram} target="_blank" title="instragram">
|
||||
<LogoInstagram class="fill-red-500 " size={24} />
|
||||
</a>
|
||||
{/if}
|
||||
{#if content.settings.twitter}
|
||||
<a href={content.settings.twitter} target="_blank" title="twitter"><LogoX /></a>
|
||||
{/if}
|
||||
{#if content.settings?.youtube}
|
||||
<a href={content.settings?.youtube} target="_blank"
|
||||
><LogoYoutube class="fill-red-700" size={24} /></a
|
||||
>{/if}
|
||||
<a href={content.settings?.youtube} target="_blank">
|
||||
<LogoYoutube class="fill-red-700" size={24} />
|
||||
</a>
|
||||
{/if}
|
||||
{#if content.settings?.discord}
|
||||
<a href={content.settings.discord} target="_blank" title="discord"
|
||||
><LogoDiscord class="fill-blue-800" size={24} /></a
|
||||
>
|
||||
<a href={content.settings.discord} target="_blank" title="discord">
|
||||
<LogoDiscord class="fill-blue-800" size={24} />
|
||||
</a>
|
||||
{/if}
|
||||
{#if content.settings.linkedin}
|
||||
<a href={content.settings.linkedin} target="_blank" title="linkedin"
|
||||
><LogoLinkedin class="fill-blue-800" size={24} /></a
|
||||
>
|
||||
<a href={content.settings.linkedin} target="_blank" title="linkedin">
|
||||
<LogoLinkedin class="fill-blue-800" size={24} />
|
||||
</a>
|
||||
{/if}
|
||||
{#if content.settings.facebook}
|
||||
<a href={content.settings.facebook} target="_blank" title="facebook"
|
||||
><LogoFacebook class="fill-blue-800" size={24} /></a
|
||||
>
|
||||
<a href={content.settings.facebook} target="_blank" title="facebook">
|
||||
<LogoFacebook class="fill-blue-800" size={24} />
|
||||
</a>
|
||||
{/if}
|
||||
</ul>
|
||||
<a href="/#" class="hidden md:flex items-center gap-1">
|
||||
<p class=" text-[#0233BD] text-base underline font-semibold">Built on ClassroomIO</p>
|
||||
<a
|
||||
href="https://classroomio.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="hidden items-center gap-1 md:flex"
|
||||
>
|
||||
<p class=" text-base font-semibold text-[#0233BD] underline">Built on ClassroomIO</p>
|
||||
</a>
|
||||
</nav>
|
||||
{/if}
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex flex-col md:flex-row items-start gap-3 border max-h-full max-w-[300px] md:max-h-[250px] md:max-w-[500px] lg:max-w-[400px] xl:max-w-[550px] rounded-md p-3"
|
||||
class="flex max-h-full max-w-[300px] flex-col items-start gap-3 rounded-md border p-3 md:max-h-[250px] md:max-w-[500px] md:flex-row lg:max-w-[400px] xl:max-w-[550px]"
|
||||
>
|
||||
<div class="bg-[#FFE8FF] w-full h-full rounded-md p-2">
|
||||
<div class="h-full w-full rounded-md bg-[#FFE8FF] p-2">
|
||||
<img
|
||||
src={banner ? banner : './classroomio-course-img-template.jpg'}
|
||||
src={banner ? banner : './course-banner.jpg'}
|
||||
alt="tutor"
|
||||
class="object-cover w-full h-full rounded"
|
||||
class="h-full w-full rounded object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full space-y-1">
|
||||
<p class="text-xl font-semibold">{name}</p>
|
||||
<RatingComponent {rating} />
|
||||
<p class="text-[#656565] text-sm line-clamp-5 md:line-clamp-4 xl:line-clamp-6">
|
||||
<p class="line-clamp-5 text-sm text-[#656565] md:line-clamp-4 xl:line-clamp-6">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ArrowRight, ArrowUpRight, Close } from 'carbon-icons-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
import Menu from 'carbon-icons-svelte/lib/Menu.svelte';
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
@@ -18,19 +17,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav class={`relative w-full flex items-center justify-between py-4 px-6 bg-white dark:bg-black`}>
|
||||
<nav
|
||||
class="relative flex w-full items-center justify-between border-b-2 bg-white px-6 py-4 dark:bg-black"
|
||||
>
|
||||
<!-- Logo Section -->
|
||||
<div class="logo">
|
||||
<a href="/" title={`${seo?.settings?.title}`} id="logo">
|
||||
{#if seo?.settings?.logo}
|
||||
<img
|
||||
src={seo?.settings?.logo}
|
||||
alt={`${seo.settings.title} logo`}
|
||||
class="rounded w-9 inline-block mx-auto"
|
||||
/>
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} className="w-24" addAcademy />
|
||||
|
||||
<!-- Mobile Menu Button (Visible only on mobile and when logged in) -->
|
||||
|
||||
@@ -39,16 +30,17 @@
|
||||
</button>
|
||||
|
||||
<!-- Desktop Navigation Menu (Hidden on mobile) -->
|
||||
<ul class="hidden lg:flex items-center space-x-8 text-base font-bold text-[#1F2937] list-none">
|
||||
<ul class="hidden list-none items-center space-x-8 text-base font-bold text-[#1F2937] lg:flex">
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li class="hover:text-[#CE02CE]">
|
||||
<a
|
||||
href={navItem.link}
|
||||
target={navItem.redirect ? '_blank' : undefined}
|
||||
class="flex items-center gap-1"
|
||||
>{navItem.title}
|
||||
>
|
||||
{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight size={12} />
|
||||
<ArrowUpRight />
|
||||
{/if}
|
||||
</a>
|
||||
</li>
|
||||
@@ -57,34 +49,34 @@
|
||||
|
||||
<!-- Mobile Sidebar Menu (Visible only on mobile) -->
|
||||
<ul
|
||||
class={`fixed top-0 left-0 pt-10 h-full bg-white w-full transform hover:no-underline ${
|
||||
class={`fixed left-0 top-0 h-full w-full transform bg-white pt-10 hover:no-underline ${
|
||||
open ? 'translate-y-0' : '-translate-y-full'
|
||||
} transition-transform duration-300 ease-in-out lg:hidden text-base font-bold text-[#1F2937] list-none cursor-pointer`}
|
||||
} cursor-pointer list-none text-base font-bold text-[#1F2937] transition-transform duration-300 ease-in-out lg:hidden`}
|
||||
>
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 lg:hidden z-20">
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 z-20 lg:hidden">
|
||||
<Close size={24} />
|
||||
</button>
|
||||
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li class="py-4 px-6 border-b">
|
||||
<li class="border-b px-6 py-4">
|
||||
<a href={navItem.link} onclick={toggleMenu} class="flex items-center gap-1"
|
||||
>{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight size={12} />
|
||||
<ArrowUpRight />
|
||||
{/if}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
<a href="/courses" class="flex items-center gap-1 py-4 px-6 border-b" onclick={toggleMenu}>
|
||||
<p class="font-bold text-[#1F2937] text-base">Start Learning</p>
|
||||
<a href="/courses" class="flex items-center gap-1 border-b px-6 py-4" onclick={toggleMenu}>
|
||||
<p class="text-base font-bold text-[#1F2937]">Start Learning</p>
|
||||
<ArrowRight size={16} class="fill-[#1F2937] font-bold" />
|
||||
</a>
|
||||
</ul>
|
||||
|
||||
<!-- Learn with Me Button (Visible on desktop when logged in) -->
|
||||
|
||||
<a href="/courses" class="hidden lg:flex items-center gap-1">
|
||||
<p class="font-bold text-[#1F2937] text-base">Start Learning</p>
|
||||
<a href="/courses" class="hidden items-center gap-1 lg:flex">
|
||||
<p class="text-base font-bold text-[#1F2937]">Start Learning</p>
|
||||
<ArrowRight size={16} class="fill-[#1F2937] font-bold" />
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
@@ -15,7 +15,7 @@
|
||||
<Button
|
||||
{href}
|
||||
class={cn(
|
||||
'bg-classic rounded text-white font-semibold p-2 hover:scale-90 hover:bg-classic',
|
||||
'bg-classic rounded text-white font-semibold p-2 hover:bg-classic/90 hover:scale-95 transition-all duration-300',
|
||||
className
|
||||
)}
|
||||
onclick={onClick}
|
||||
|
||||
@@ -9,20 +9,16 @@
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="flex flex-col justify-between relative border border-[#EAEAEA] px-6 pt-6 space-y-4 bg-[#FDFDFD] rounded w-full h-full lg:h-[280px] max-w-[400px] xl:max-w-[500px]"
|
||||
class="relative flex h-full w-full max-w-[400px] flex-col justify-between space-y-4 rounded border border-[#EAEAEA] bg-[#FDFDFD] px-6 pt-6 lg:h-[280px] xl:max-w-[500px]"
|
||||
>
|
||||
<img src="/quote.svg" alt="" class="w-6 h-6 rounded-full absolute top-6 left-8" />
|
||||
<img src="/quote.svg" alt="" class="absolute left-8 top-6 h-6 w-6 rounded-full" />
|
||||
<div>
|
||||
<p class="my-6 font-bold line-clamp-5">
|
||||
<p class="my-6 line-clamp-5 font-bold">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center p-4 gap-4 border-t border-[#EAEAEA]">
|
||||
<img
|
||||
src={banner ? banner : '/classroomio-course-img-template.jpg'}
|
||||
alt=""
|
||||
class="w-6 h-6 rounded-full"
|
||||
/>
|
||||
<div class="flex items-center gap-4 border-t border-[#EAEAEA] p-4">
|
||||
<img src={banner ? banner : '/course-banner.jpg'} alt="" class="h-6 w-6 rounded-full" />
|
||||
<span>
|
||||
<p class="text-sm font-bold">{name}</p>
|
||||
</span>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import type { Course, CourseFilterItem } from '@/utils/types/course';
|
||||
import type { Page } from '$lib/utils/types/page';
|
||||
import PrimaryButton from '../PrimaryButton.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
@@ -41,8 +43,8 @@
|
||||
* Constants
|
||||
*/
|
||||
const section = $derived({
|
||||
header: getPageSection(data.page, 'header'),
|
||||
courses: getPageSection(data.page, 'courses')
|
||||
header: getPageSection(data.page, SECTION.HERO),
|
||||
courses: getPageSection(data.page, SECTION.COURSE)
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -65,31 +67,33 @@
|
||||
<section class="overflow-x-hidden">
|
||||
{#if section.header?.show}
|
||||
<div
|
||||
class="h-full pt-20 pb-36 flex flex-col items-center justify-center text-center w-full bg-gradient-to-r from-[#f0a5f11a] via-white to-[#f6e7f61a]"
|
||||
class="flex h-full w-full flex-col items-center justify-center bg-gradient-to-r from-[#F785F9] via-white to-[#fff] pb-36 pt-20 text-center"
|
||||
>
|
||||
<h1 class="font-bold text-5xl mb-4 w-full md:w-[50%] text-center">
|
||||
<h1 class="mb-4 w-full text-center text-5xl font-bold md:w-[50%]">
|
||||
{section.header.settings.title}
|
||||
<span class="text-[#CE02CE]/80 font-bold text-5xl">
|
||||
<span class="text-classic/80 text-5xl font-bold">
|
||||
{section.header.settings.titleHighlight}</span
|
||||
>
|
||||
</h1>
|
||||
<p class="md:text-xl text-[#878787] w-full md:w-[60%] lg:w-[40%] text-center">
|
||||
<p class="w-full text-center text-[#878787] md:w-[60%] md:text-xl lg:w-[40%]">
|
||||
{section.header.settings.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if section.courses?.show}
|
||||
<section id="course" class="px-4 pt-4 pb-20 h-full bg-white">
|
||||
<h1 class="text-center text-3xl text-[#3F3F3F] font-bold mb-6 w-full md:w-[60%] mx-auto">
|
||||
<section id="course" class="h-full bg-white px-4 pb-20 pt-4">
|
||||
<h1
|
||||
class="mx-auto my-10 mb-20 w-full text-center text-3xl font-bold text-[#3F3F3F] md:w-[60%]"
|
||||
>
|
||||
{section.courses.settings.title}
|
||||
</h1>
|
||||
<div class="w-full md:w-[90%] mx-auto">
|
||||
<div class="mx-auto w-full md:w-[90%]">
|
||||
{#if data.courses.length > 0}
|
||||
<div class="w-full flex gap-4 lg:ml-[5%]">
|
||||
<div class="hidden lg:block w-fit space-y-2">
|
||||
<p class="font-medium text-[#3C4043] uppercase">Filter</p>
|
||||
<div class="w-max border border-[#EAEAEA] p-6 space-y-8">
|
||||
<div class="flex w-full gap-4 lg:ml-[5%]">
|
||||
<div class="hidden w-fit space-y-2 lg:block">
|
||||
<p class="font-medium uppercase text-[#3C4043]">Filter</p>
|
||||
<div class="w-max space-y-8 border border-[#EAEAEA] p-6">
|
||||
{#each filter as item}
|
||||
<form class="space-x-2 text-[#3C4043]">
|
||||
<input
|
||||
@@ -104,7 +108,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<section
|
||||
class="grid place-items-center grid-cols-1 md:grid-cols-2 xl:grid-cols-3 w-full"
|
||||
class="grid w-full grid-cols-1 place-items-center md:grid-cols-2 xl:grid-cols-3"
|
||||
>
|
||||
{#each filteredCourses.slice(0, viewAll ? filteredCourses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
@@ -119,22 +123,23 @@
|
||||
/>
|
||||
{/each}
|
||||
{#if filteredCourses.length == 0}
|
||||
<div class="px-4 w-full mx-auto">
|
||||
<div class="mx-auto w-full px-4">
|
||||
<EmptyState headerClassName="text-[#CE02CE]" />
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{#if filteredCourses.length > 3}
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<Button
|
||||
class="text-lg font-semibold text-white !bg-[#CE02CE]"
|
||||
on:click={() => (viewAll = !viewAll)}>View more programs</Button
|
||||
>
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
class="text-lg font-semibold text-white "
|
||||
label="View more programs"
|
||||
onClick={() => (viewAll = !viewAll)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="px-4 w-full lg:w-[70%] mx-auto">
|
||||
<div class="mx-auto w-full px-4 lg:w-[70%]">
|
||||
<EmptyState headerClassName="text-[#CE02CE]" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -11,55 +11,49 @@
|
||||
import Accordion from '../Accordion.svelte';
|
||||
import TestimonialCard from '../TestimonialCard.svelte';
|
||||
import BlogCard from '../BlogCard.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const faqSection = $derived(getPageSection($homePage, 'faq'));
|
||||
const testimonialSection = $derived(getPageSection($homePage, 'testimonial'));
|
||||
const aboutSection = $derived(getPageSection($homePage, 'about'));
|
||||
const courseSection = $derived(getPageSection($homePage, 'courses'));
|
||||
const instructorSection = $derived(getPageSection($homePage, 'instructors'));
|
||||
const footerNoteSection = $derived(getPageSection($homePage, 'cta'));
|
||||
const faqSection = $derived(getPageSection($homePage, SECTION.FAQ));
|
||||
const testimonialSection = $derived(getPageSection($homePage, SECTION.TESTIMONIAL));
|
||||
const aboutSection = $derived(getPageSection($homePage, SECTION.ABOUT));
|
||||
const courseSection = $derived(getPageSection($homePage, SECTION.COURSE));
|
||||
const instructorSection = $derived(getPageSection($homePage, SECTION.INSTRUCTORS));
|
||||
const ctaSection = $derived(getPageSection($homePage, SECTION.CTA));
|
||||
|
||||
const DISPLAY_COURSE = {
|
||||
ALL: 'all',
|
||||
COURSE: 'course',
|
||||
PATH: 'path'
|
||||
};
|
||||
let viewAll = $state(false);
|
||||
let type = DISPLAY_COURSE.ALL;
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<!-- <Hero /> -->
|
||||
{#if $homePage}
|
||||
{@const content = getPageSection($homePage, 'header')}
|
||||
{@const content = getPageSection($homePage, SECTION.HERO)}
|
||||
{#if content?.show}
|
||||
<section class="flex items-start justify-center pb-20 lg:pt-10 pt-4 px-6 md:px-14 h-full">
|
||||
<section class="flex flex-col-reverse md:flex-row items-start gap-10 md:justify-between">
|
||||
<div class="space-y-6 w-full">
|
||||
<p class="text-4xl xl:text-6xl font-bold w-full xl:w-[90%]">
|
||||
<section class="flex h-full items-start justify-center px-6 pb-20 md:px-14">
|
||||
<section class="flex flex-col-reverse items-center gap-10 md:flex-row md:justify-between">
|
||||
<div class="flex h-full w-full flex-col gap-4 pt-4">
|
||||
<p class="w-full text-4xl font-bold xl:w-[90%] xl:text-6xl">
|
||||
{content.settings.title}
|
||||
<span class="text-[#CE02CE]">
|
||||
{content.settings.titleHighlight}
|
||||
</span>
|
||||
</p>
|
||||
<p class="w-full lg:w-[70%] text-[#878787] xl:text-lg">
|
||||
<p class="w-full text-[#878787] lg:w-[70%] xl:text-lg">
|
||||
{content.settings.subtitle}
|
||||
</p>
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
goto(content.settings.action.link);
|
||||
}}
|
||||
href={content.settings.action.link}
|
||||
label={content.settings.action.label}
|
||||
class="w-fit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-md md:h-[300px] xl:h-[500px] max-h-full w-full md:w-1/2 lg:w-4/5 xl:w-[800px] md:max-w-[800px] flex"
|
||||
class="flex max-h-full w-full md:w-1/2 md:max-w-[800px] lg:w-4/5 xl:h-[500px] xl:w-[800px]"
|
||||
>
|
||||
<img
|
||||
style="min-width:280px; min-height:200px"
|
||||
alt="landing page banner"
|
||||
src={content.settings.banner.image ? content.settings.banner.image : banner}
|
||||
class="object-cover mt-2 h-full w-full rounded-md md:mt-0"
|
||||
src={content.settings.banner.image || banner}
|
||||
class="mt-2 h-full min-h-[200px] w-full min-w-[280px] object-cover md:mt-0"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
@@ -72,20 +66,20 @@
|
||||
{#if aboutSection?.show}
|
||||
<section
|
||||
id="about"
|
||||
class="flex items-start justify-center px-6 md:px-10 lg:px-14 h-full bg-white border-b-2 pb-20 lg:pt-20 pt-4"
|
||||
class="flex h-full items-start justify-center border-b-2 bg-white px-6 pb-20 pt-4 md:px-10 lg:px-14 lg:pt-20"
|
||||
>
|
||||
<section class="flex flex-col lg:flex-row gap-4 items-center lg:items-start justify-center">
|
||||
<div class="w-full lg:w-[60%] space-y-4">
|
||||
<section class="flex flex-col items-center justify-center gap-4 lg:flex-row lg:items-start">
|
||||
<div class="w-full space-y-4 lg:w-[60%]">
|
||||
<p class="text-4xl font-bold text-[#3F3F3F]">{aboutSection.settings.title}</p>
|
||||
<p class="w-full lg:w-[80%] text-base leading-7 text-[#878787]">
|
||||
<p class="w-full text-base leading-7 text-[#878787] lg:w-[80%]">
|
||||
{aboutSection.settings.content}
|
||||
</p>
|
||||
</div>
|
||||
{#if aboutSection.settings.benefits}
|
||||
<div class="max-w-[400px] min-h-fit">
|
||||
<div class="min-h-fit max-w-[400px]">
|
||||
{#each aboutSection.settings.benefits.list as item, index}
|
||||
<div
|
||||
class="benefit-card w-full text-center max-w-[200px] p-4 font-semibold mb-9 border-b-4 border-[#CE02CE] bg-white rounded-b-lg shadow-lg"
|
||||
class="benefit-card mb-9 w-full max-w-[200px] rounded-b-lg border-b-4 border-[#CE02CE] bg-white p-4 text-center font-semibold shadow-lg"
|
||||
class:left={index % 2 === 0}
|
||||
class:right={index % 2 !== 0}
|
||||
>
|
||||
@@ -97,7 +91,7 @@
|
||||
<img
|
||||
src={aboutSection.settings.imageUrl}
|
||||
alt="Our Story"
|
||||
class="rounded-2xl max-h-[450px]"
|
||||
class="max-h-[450px] rounded-2xl"
|
||||
/>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -106,17 +100,17 @@
|
||||
|
||||
<!-- courses -->
|
||||
{#if courseSection?.show}
|
||||
<section id="course" class="px-4 py-6 pb-20 h-full bg-white">
|
||||
<h1 class="text-center text-3xl text-[#3F3F3F] font-bold mb-4">
|
||||
<section id="course" class="h-full bg-white px-4 py-6 pb-20">
|
||||
<h1 class="mb-4 text-center text-3xl font-bold text-[#3F3F3F]">
|
||||
{courseSection.settings.title}
|
||||
</h1>
|
||||
<div class="w-full md:w-[90%] mx-auto">
|
||||
<div class="mx-auto w-full md:w-[90%]">
|
||||
{#if $courses.length > 0}
|
||||
<section class="flex flex-wrap items-center mx-auto w-fit gap-4">
|
||||
<section class="mx-auto flex w-fit flex-wrap items-center gap-4">
|
||||
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
slug={courseData.slug}
|
||||
bannerImage={courseData.banner || '/classroomio-course-img-template.jpg'}
|
||||
bannerImage={courseData.banner || '/course-banner.jpg'}
|
||||
title={courseData.title}
|
||||
type={courseData.type}
|
||||
description={courseData.description}
|
||||
@@ -127,16 +121,16 @@
|
||||
{/each}
|
||||
</section>
|
||||
{#if $courses.length > 3}
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
class="text-lg font-semibold text-white bg-[#CE02CE]"
|
||||
class="bg-[#CE02CE] text-lg font-semibold text-white"
|
||||
onClick={() => (viewAll = !viewAll)}
|
||||
label="View more programs"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="px-4 w-full lg:w-[70%] mx-auto">
|
||||
<div class="mx-auto w-full px-4 lg:w-[70%]">
|
||||
<EmptyState headerClassName="text-[#CE02CE]" />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -145,19 +139,19 @@
|
||||
{/if}
|
||||
<!-- instructors -->
|
||||
{#if instructorSection?.show}
|
||||
<section class="px-4 lg:px-14 pt-4 pb-20 h-full bg-white">
|
||||
<div class="w-full xl:w-[90%] mx-auto">
|
||||
<h1 class=" text-center lg:text-start text-3xl text-[#3F3F3F] font-bold mb-4">
|
||||
<section class="h-full bg-white px-4 pb-20 pt-4 lg:px-14">
|
||||
<div class="mx-auto w-full xl:w-[90%]">
|
||||
<h1 class=" mb-4 text-center text-3xl font-bold text-[#3F3F3F] lg:text-start">
|
||||
Meet some of our Instructors
|
||||
</h1>
|
||||
<section class="grid place-items-center grid-cols-1 md:grid-cols-2 gap-2 w-full">
|
||||
<section class="grid w-full grid-cols-1 place-items-center gap-2 md:grid-cols-2">
|
||||
{#each instructorSection.settings.list as item}
|
||||
<InstructorCard name={item.name} description={item.description} rating={item.rating} />
|
||||
{/each}
|
||||
</section>
|
||||
<div class="w-full flex justify-center md:justify-end px-4 mt-6">
|
||||
<div class="mt-6 flex w-full justify-center px-4 md:justify-end">
|
||||
<PrimaryButton
|
||||
class="text-lg font-semibold text-white p-6 rounded"
|
||||
class="rounded p-6 text-lg font-semibold text-white"
|
||||
label="Start learning & Explore courses"
|
||||
/>
|
||||
</div>
|
||||
@@ -166,11 +160,11 @@
|
||||
{/if}
|
||||
<!-- faq -->
|
||||
{#if faqSection?.show}
|
||||
<section class="px-4 py-6 pb-20 h-full bg-[#F9F9F9]">
|
||||
<h1 class="text-center text-3xl text-[#3F3F3F] font-bold mb-4">
|
||||
<section class="h-full bg-[#F9F9F9] px-4 py-6 pb-20">
|
||||
<h1 class="mb-4 text-center text-3xl font-bold text-[#3F3F3F]">
|
||||
{faqSection.settings.title}
|
||||
</h1>
|
||||
<section class="p-2 space-y-10 w-full md:w-[80%] mx-auto">
|
||||
<section class="mx-auto w-full space-y-10 p-2 md:w-[80%]">
|
||||
{#each faqSection.settings.questions as faq}
|
||||
<Accordion title={faq.title} content={faq.content} />
|
||||
{/each}
|
||||
@@ -179,39 +173,39 @@
|
||||
{/if}
|
||||
<!-- testimonial -->
|
||||
{#if testimonialSection?.show}
|
||||
<section id="testimonial" class="px-4 lg:px-14 pt-4 pb-20 h-full bg-white">
|
||||
<h1 class="text-center text-3xl text-[#3F3F3F] font-bold mb-4">
|
||||
<section id="testimonial" class="h-full bg-white px-4 pb-20 pt-4 lg:px-14">
|
||||
<h1 class="mb-4 text-center text-3xl font-bold text-[#3F3F3F]">
|
||||
Words from our past learners
|
||||
</h1>
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 w-full">
|
||||
<section class="grid w-full grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{#each testimonialSection.settings.list as item}
|
||||
<TestimonialCard description={item.description} name={item.name} />
|
||||
{/each}
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
<!-- footerNote -->
|
||||
{#if footerNoteSection?.show}
|
||||
<section class="flex flex-col items-center justify-between px-6 lg:px-10 py-20 bg-classic">
|
||||
<div class="flex items-center justify-center w-full">
|
||||
<p class="text-4xl text-white text-center font-bold w-full">
|
||||
{footerNoteSection.settings.title}
|
||||
<!-- cta -->
|
||||
{#if ctaSection?.show}
|
||||
<section class="bg-classic flex flex-col items-center justify-between px-6 py-20 lg:px-10">
|
||||
<div class="flex w-full items-center justify-center">
|
||||
<p class="w-full text-center text-4xl font-bold text-white">
|
||||
{ctaSection.settings.title}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
onClick={() => goto('/courses')}
|
||||
class="rounded-sm font-bold text-lg bg-classic-secondary border hover:scale-95 hover:bg-classic-secondary border-gray-100"
|
||||
label={footerNoteSection.settings.buttonLabel}
|
||||
href={ctaSection.settings?.button?.link}
|
||||
class="bg-classic-secondary hover:bg-classic-secondary text-classic rounded-sm border border-gray-100 text-lg font-bold hover:scale-95"
|
||||
label={ctaSection.settings?.button?.label}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- blog -->
|
||||
<section id="course" class="px-4 py-6 pb-20 h-full bg-white">
|
||||
<h1 class="text-center text-3xl text-[#3F3F3F] font-bold mb-4">Latest blog Post</h1>
|
||||
<section class="flex flex-wrap items-center mx-auto gap-2 w-full">
|
||||
<section id="course" class="hidden h-full bg-white px-4 py-6 pb-20">
|
||||
<h1 class="mb-4 text-center text-3xl font-bold text-[#3F3F3F]">Latest blog Post</h1>
|
||||
<section class="mx-auto flex w-full flex-wrap items-center gap-2">
|
||||
<BlogCard />
|
||||
<BlogCard />
|
||||
<BlogCard />
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { Add, Subtract } from 'carbon-icons-svelte';
|
||||
interface Props {
|
||||
title?: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
let { title = '', content = '' }: Props = $props();
|
||||
|
||||
let isOpen = $state(false);
|
||||
let contentHeight = $state('0px'); // Track the height of the content
|
||||
let contentRef: HTMLDivElement = $state(); // Reference to the content div
|
||||
|
||||
function toggleAccordion() {
|
||||
isOpen = !isOpen;
|
||||
|
||||
if (isOpen) {
|
||||
// Measure and set the height when opening
|
||||
|
||||
contentHeight = `${contentRef?.scrollHeight}px`;
|
||||
} else {
|
||||
// Set to 0 height when closing
|
||||
contentHeight = '0px';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-2 bg-white shadow-md">
|
||||
<!-- Accordion Header -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="flex cursor-pointer items-center justify-between rounded bg-white p-4"
|
||||
onclick={toggleAccordion}
|
||||
>
|
||||
<p class="text-lg font-semibold">{title}</p>
|
||||
{#if isOpen}
|
||||
<Subtract class="text-xl transition-transform duration-200 ease-in" />
|
||||
{:else}
|
||||
<Add class="text-xl transition-transform duration-200 ease-in" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Accordion Content -->
|
||||
<div
|
||||
bind:this={contentRef}
|
||||
style="max-height: {contentHeight}; transition: max-height 0.3s ease-in-out; overflow: hidden;"
|
||||
>
|
||||
<div class="rounded-b bg-white p-4">
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script>
|
||||
export let className = '';
|
||||
export let buttonClass = '';
|
||||
|
||||
export let slug = '';
|
||||
export let title = '';
|
||||
export let description = '';
|
||||
|
||||
function getCourseUrl() {
|
||||
return `/course/${slug}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- only custom colors work for shadow -->
|
||||
<div
|
||||
class="border-examprep w-full rounded-lg border-2 p-4 shadow-[-12px_12px_0px_#0233BD] md:h-[300px] md:w-[350px] {className}"
|
||||
>
|
||||
<div class="flex h-full w-full flex-col justify-between gap-4 rounded-lg">
|
||||
<div class="flex-1 space-y-2">
|
||||
<p class="text-examprep line-clamp-2 font-serif text-xl font-semibold uppercase">{title}</p>
|
||||
<p class="font-inter line-clamp-4 text-gray-600">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<a href={getCourseUrl()} class="hover:no-underline">
|
||||
<div
|
||||
class="bg-examprep w-full cursor-pointer p-3 text-start font-medium uppercase text-white transition-all duration-200 hover:scale-90 hover:no-underline {buttonClass}"
|
||||
>
|
||||
Start course
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import emptyBanner from './assets/expert-empty.svg';
|
||||
interface Props {
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
subtitleClassName?: string;
|
||||
}
|
||||
|
||||
let { className = '', headerClassName = '', subtitleClassName = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="{className} my-2 flex w-full items-center justify-center rounded-md border p-2 py-4"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col items-center justify-center gap-6 p-2 text-center md:flex-row md:text-start"
|
||||
>
|
||||
<img src={emptyBanner} alt="empty" />
|
||||
<div>
|
||||
<h3 class="font-semibold {headerClassName}">"No Course yet"</h3>
|
||||
<p class="text-sm md:text-base {subtitleClassName}">
|
||||
Looks like courses have not been added yet.<br /> Kindly check back later
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script>
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
import Logo from '../ui/_custom/Logo.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const content = $derived(getPageSection($sharedPage, SECTION.FOOTER));
|
||||
const seo = $derived(getPageSection($sharedPage, SECTION.SEO));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<nav
|
||||
class="flex w-full flex-col items-start gap-4 bg-white px-6 py-4 md:flex-row md:items-center md:justify-between"
|
||||
>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} />
|
||||
<ul class="-md:gap-8 flex flex-col items-start gap-4 underline md:flex-row md:items-center">
|
||||
{#if content.settings.twitter}
|
||||
<a href={content.settings.twitter} target="_blank" title="twitter">Twitter</a>
|
||||
{/if}
|
||||
{#if content.settings?.youtube}
|
||||
<a href={content.settings?.youtube} target="_blank">Youtube</a>{/if}
|
||||
|
||||
{#if content.settings.linkedin}
|
||||
<a href={content.settings.linkedin} target="_blank" title="linkedin">LinkedIn</a>
|
||||
{/if}
|
||||
{#if content.settings.facebook}
|
||||
<a href={content.settings.facebook} target="_blank" title="facebook">facebook</a>
|
||||
{/if}
|
||||
</ul>
|
||||
<a
|
||||
href="https://classroomio.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-1"
|
||||
>
|
||||
<p class=" text-examprep text-base font-semibold underline">Built on ClassroomIO</p>
|
||||
</a>
|
||||
</nav>
|
||||
{/if}
|
||||
@@ -0,0 +1,97 @@
|
||||
<script>
|
||||
import { ArrowRight, ArrowUpRight, Close } from 'carbon-icons-svelte';
|
||||
import Menu from 'carbon-icons-svelte/lib/Menu.svelte';
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
import PrimaryButton from './PrimaryButton.svelte';
|
||||
import Logo from '../ui/_custom/Logo.svelte';
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
const seo = $derived(getPageSection($sharedPage, 'seo'));
|
||||
const content = $derived(getPageSection($sharedPage, 'navigation'));
|
||||
|
||||
// Function to toggle the mobile menu
|
||||
function toggleMenu() {
|
||||
open = !open;
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav class="relative flex h-16 w-full items-center justify-between border-b pl-6">
|
||||
<!-- Logo Section -->
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} addAcademy />
|
||||
|
||||
<!-- Mobile Menu Button (Visible only on mobile) -->
|
||||
<button onclick={toggleMenu} class="px-6 py-4 lg:hidden">
|
||||
<Menu size={24} />
|
||||
</button>
|
||||
|
||||
<!-- Desktop Navigation Menu (Hidden on mobile) -->
|
||||
<ul
|
||||
class="hidden list-none items-center space-x-8 text-base font-bold text-gray-700 hover:no-underline lg:flex"
|
||||
>
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li>
|
||||
<a
|
||||
href={navItem.link}
|
||||
target={navItem.redirect ? '_blank' : undefined}
|
||||
class="flex items-center gap-1"
|
||||
>{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight />
|
||||
{/if}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<!-- Mobile Sidebar Menu (Visible only on mobile) -->
|
||||
<ul
|
||||
class={`fixed left-0 top-0 h-full w-full transform bg-white hover:no-underline ${
|
||||
open ? 'translate-y-0' : '-translate-y-full'
|
||||
} cursor-pointer list-none text-base font-bold text-gray-700 transition-transform duration-300 ease-in-out lg:hidden`}
|
||||
>
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 z-20 lg:hidden">
|
||||
<Close size={24} />
|
||||
</button>
|
||||
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li class="border-b px-6 py-4">
|
||||
<a
|
||||
href={navItem.link}
|
||||
target={navItem.redirect ? '_blank' : undefined}
|
||||
class="flex items-center gap-1"
|
||||
onclick={toggleMenu}
|
||||
>{navItem.title}{#if navItem.redirect}
|
||||
<ArrowUpRight />
|
||||
{/if}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
<PrimaryButton
|
||||
href="/courses"
|
||||
class="flex items-center gap-1 border-b px-6 py-4 text-base font-bold text-gray-700"
|
||||
onClick={toggleMenu}
|
||||
label="Learn with us"
|
||||
>
|
||||
<ArrowRight size={16} class="fill-gray-700 font-bold" />
|
||||
</PrimaryButton>
|
||||
</ul>
|
||||
|
||||
<PrimaryButton
|
||||
href="/courses"
|
||||
label="Learn with us"
|
||||
class="hidden h-full w-fit px-6 py-6 text-base font-normal text-white hover:no-underline lg:flex"
|
||||
>
|
||||
<ArrowRight size={20} class="fill-white" />
|
||||
</PrimaryButton>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
position: relative;
|
||||
}
|
||||
ul {
|
||||
z-index: 10;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
label: string;
|
||||
href?: string | undefined;
|
||||
class?: string | undefined;
|
||||
children?: any;
|
||||
}
|
||||
const { onClick, children, label, href, class: className, ...restProps }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<Button
|
||||
{href}
|
||||
class={cn(
|
||||
'bg-examprep text-white font-semibold p-2 hover:bg-examprep rounded-none hover:bg-examprep/90 hover:scale-95 transition-all duration-300',
|
||||
className
|
||||
)}
|
||||
onclick={onClick}
|
||||
{...restProps}
|
||||
>
|
||||
{label}
|
||||
{#if children}
|
||||
{@render children?.()}
|
||||
{/if}
|
||||
</Button>
|
||||
|
After Width: | Height: | Size: 3.3 MiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 443 KiB |
@@ -0,0 +1,15 @@
|
||||
import type { Component } from 'svelte';
|
||||
import CoursesPage from './pages/Course.svelte';
|
||||
import HomePage from './pages/Home.svelte';
|
||||
import Navigation from './Navigation.svelte';
|
||||
import Footer from './Footer.svelte';
|
||||
import Button from './PrimaryButton.svelte';
|
||||
import type { TemplateComponents } from '$lib/utils/types/template';
|
||||
|
||||
export const components: TemplateComponents = {
|
||||
navigation: Navigation as unknown as Component,
|
||||
footer: Footer as unknown as Component,
|
||||
button: Button as unknown as Component,
|
||||
coursespage: CoursesPage as unknown as Component,
|
||||
homepage: HomePage as unknown as Component
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
<script lang="ts">
|
||||
import EmptyState from '../EmptyState.svelte';
|
||||
import PrimaryButton from '../PrimaryButton.svelte';
|
||||
import { courses } from '@/utils/stores/course';
|
||||
import CourseCard from '../CourseCard.svelte';
|
||||
import type { Course, CourseFilterItem } from '@/utils/types/course';
|
||||
import type { Page } from '$lib/utils/types/page';
|
||||
import { COURSE_TYPE } from '@/utils/constants/course';
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
page: Page;
|
||||
sharedPage: Page;
|
||||
courses: Course[];
|
||||
};
|
||||
}
|
||||
|
||||
let { data }: Props = $props();
|
||||
|
||||
/**
|
||||
* State
|
||||
*/
|
||||
let viewAll = $state(false);
|
||||
|
||||
let filter: CourseFilterItem[] = $state([
|
||||
{
|
||||
title: 'Self Paced',
|
||||
type: COURSE_TYPE.PACED,
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
title: 'Live Sessions',
|
||||
type: COURSE_TYPE.LIVE,
|
||||
checked: false
|
||||
}
|
||||
]);
|
||||
|
||||
let filteredCourses = $state([...data.courses]);
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
const section = $derived({
|
||||
header: getPageSection(data.page, SECTION.HERO),
|
||||
courses: getPageSection(data.page, SECTION.COURSE)
|
||||
});
|
||||
|
||||
/**
|
||||
* Functions
|
||||
*/
|
||||
function applyFilter() {
|
||||
const activeFilters = new Set(
|
||||
filter
|
||||
.filter((filterItem) => filterItem.checked)
|
||||
.map((filterItem) => filterItem.type.toLowerCase())
|
||||
);
|
||||
|
||||
filteredCourses =
|
||||
activeFilters.size === 0
|
||||
? data.courses
|
||||
: data.courses.filter((course) => activeFilters.has(course.type.toLowerCase()));
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="overflow-x-hidden">
|
||||
{#if section.header?.show}
|
||||
<section class="flex h-[50vh] items-center justify-center py-20">
|
||||
<section class="flex flex-col items-start gap-10 p-4 text-center md:items-center">
|
||||
<div class="w-full space-y-6">
|
||||
<p
|
||||
class="mx-auto w-full font-serif text-3xl font-medium text-gray-800 md:w-[60%] md:text-4xl"
|
||||
>
|
||||
{section.header.settings.title}
|
||||
</p>
|
||||
|
||||
<p class="mx-auto w-full font-normal text-gray-500 md:w-[70%]">
|
||||
{section.header.settings.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
{#if section.courses?.show}
|
||||
<div class="border-b bg-white px-2 py-10 md:px-10 lg:px-20">
|
||||
<div class="mb-4 flex flex-col items-start justify-between md:flex-row md:items-center">
|
||||
<span class="mb-4 space-y-2 px-4 text-start lg:mb-0">
|
||||
<p class="mb-4 font-serif text-2xl md:text-3xl">{section.courses.settings.title}</p>
|
||||
<p class=" w-full text-sm font-medium text-gray-500 md:w-[80%]">
|
||||
{section.courses.settings.subtitle}
|
||||
</p>
|
||||
</span>
|
||||
<div class=" w-full md:w-fit">
|
||||
<div class="flex w-full items-center gap-1 bg-slate-100 p-2 md:gap-4">
|
||||
<p class="whitespace-nowrap text-sm font-medium md:text-base">Filter by</p>
|
||||
<div class="flex w-full flex-row items-center bg-white p-1 md:w-fit">
|
||||
{#each filter as item}
|
||||
<label
|
||||
for={item.title}
|
||||
class="flex flex-row items-center whitespace-nowrap rounded-md bg-white p-2 text-xs font-medium text-gray-800"
|
||||
>
|
||||
<input type="checkbox" name={item.title} class="text-examprep mr-2" />
|
||||
|
||||
{item.title}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{#if $courses.length > 0}
|
||||
<section class="flex flex-wrap items-center justify-center gap-5 p-4 md:justify-start">
|
||||
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
slug={courseData.slug}
|
||||
title={courseData.title}
|
||||
description={courseData.description}
|
||||
/>
|
||||
{/each}
|
||||
</section>
|
||||
{#if $courses.length > 3}
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
label="VIEW MORE PREPCOURSES"
|
||||
class="rounded-none text-lg font-semibold"
|
||||
onClick={() => (viewAll = !viewAll)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="px-10">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
@@ -0,0 +1,250 @@
|
||||
<script>
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { homePage } from '@/utils/stores/pages';
|
||||
import { courses } from '$lib/utils/stores/course';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { CheckmarkFilled } from 'carbon-icons-svelte';
|
||||
import defaultBanner from '../assets/org-banner.png';
|
||||
import defaultAvatar from '../assets/examprep-user.png';
|
||||
|
||||
import PrimaryButton from '../PrimaryButton.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import CourseCard from '../CourseCard.svelte';
|
||||
import EmptyState from '../EmptyState.svelte';
|
||||
import Accordion from '../Accordion.svelte';
|
||||
|
||||
const coursesSection = $derived(getPageSection($homePage, 'courses'));
|
||||
const aboutSection = $derived(getPageSection($homePage, 'about'));
|
||||
const faqSection = $derived(getPageSection($homePage, 'faq'));
|
||||
const testimonialSection = $derived(getPageSection($homePage, 'testimonial'));
|
||||
const ctaSection = $derived(getPageSection($homePage, 'cta'));
|
||||
|
||||
let currentIndex = $state(0);
|
||||
let viewAll = $state(false);
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
*/
|
||||
|
||||
function goToSlide(index) {
|
||||
currentIndex = index;
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<!-- hero -->
|
||||
{#if $homePage}
|
||||
{@const content = getPageSection($homePage, 'header')}
|
||||
{#if content?.show}
|
||||
<section
|
||||
class="flex h-full items-start justify-center bg-slate-100 px-10 pb-20 pt-4 lg:px-20 lg:pt-20"
|
||||
>
|
||||
<section
|
||||
class="flex flex-col-reverse items-start gap-5 md:flex-row md:items-center md:justify-between"
|
||||
>
|
||||
<div class="w-full space-y-6">
|
||||
<p
|
||||
class="text-2xl font-semibold text-gray-700 first-line:w-full xl:w-[80%] xl:text-5xl"
|
||||
>
|
||||
{content.settings.title}
|
||||
</p>
|
||||
<p class="font-inter w-full text-gray-500 lg:w-[70%] xl:text-lg">
|
||||
{content.settings.subtitle}
|
||||
</p>
|
||||
<PrimaryButton
|
||||
class="px-6 font-semibold uppercase"
|
||||
label={content.settings.action.label}
|
||||
onClick={() => {
|
||||
goto(content.settings.action.link);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex max-h-full w-full rounded-md shadow-[-12px_12px_0px_#0233BD] md:h-[400px] md:w-full lg:max-w-[800px] xl:w-1/2"
|
||||
>
|
||||
<img
|
||||
style="min-width:280px; min-height:200px"
|
||||
alt="landing page banner"
|
||||
src={content.settings?.banner?.image
|
||||
? content.settings?.banner?.image
|
||||
: defaultBanner}
|
||||
class="mt-2 h-full max-h-[550px] w-full max-w-[800px] rounded-md object-cover md:mt-0"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<!-- testimonial -->
|
||||
{#if testimonialSection?.show}
|
||||
<section class="overflow-x-hidden bg-slate-100 px-4 py-6">
|
||||
<div
|
||||
class="bg-examprep relative mx-auto flex h-[220px] w-full items-center text-center text-white md:w-[80%] lg:w-[70%]"
|
||||
>
|
||||
<!-- Pointer at the top -->
|
||||
<div
|
||||
class="bg-examprep absolute left-[50px] top-[-25px] h-10 w-16 -translate-x-1/2 transform"
|
||||
style="clip-path: polygon(50% 0%, 0% 100%, 100% 100%);"
|
||||
></div>
|
||||
|
||||
<!-- Testimonial content with fly transition (from right) -->
|
||||
<div
|
||||
class="mx-auto flex h-full w-full flex-row overflow-x-hidden p-4 md:w-[90%] lg:w-[80%]"
|
||||
>
|
||||
{#each testimonialSection.settings.list as testimonial, index}
|
||||
{#if currentIndex === index}
|
||||
<div
|
||||
transition:slide={{ duration: 300, easing: quintOut, axis: 'x' }}
|
||||
class="flex min-w-full flex-1 flex-col items-center justify-between md:h-full"
|
||||
>
|
||||
<p class=" line-clamp-4 text-base font-semibold italic md:line-clamp-3 md:text-lg">
|
||||
"{testimonial.description}"
|
||||
</p>
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<img
|
||||
src={testimonial.image ? testimonial.image : defaultAvatar}
|
||||
alt="profile"
|
||||
class="mr-4 h-14 w-14 rounded-full object-cover"
|
||||
/>
|
||||
<div class="font-inter text-start">
|
||||
<strong>{testimonial.name}</strong><br />
|
||||
<span class="text-sm uppercase text-gray-300">{testimonial.role}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dots for testimonial navigation -->
|
||||
<div class="mt-4 flex justify-center space-x-2">
|
||||
{#each testimonialSection.settings.list as _, index}
|
||||
<button
|
||||
class="h-3 w-3 cursor-pointer rounded-full transition-opacity duration-300 {currentIndex ===
|
||||
index
|
||||
? 'bg-blue-900'
|
||||
: 'bg-blue-900 opacity-50'}"
|
||||
onclick={() => goToSlide(index)}
|
||||
aria-label={`Go to slide ${index + 1}`}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- courses -->
|
||||
{#if coursesSection?.show}
|
||||
<section id="course" class="h-full bg-white px-4 pb-20 pt-4 md:px-20">
|
||||
<h1 class="text-center font-serif text-3xl">{coursesSection.settings.title}</h1>
|
||||
<p class="font-inter mx-auto mb-8 w-full text-center font-medium md:w-[50%]">
|
||||
{coursesSection.settings.subtitle}
|
||||
</p>
|
||||
|
||||
{#if $courses.length > 0}
|
||||
<section class="mx-auto flex w-fit flex-wrap items-center justify-center gap-5 p-4">
|
||||
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
slug={courseData.slug}
|
||||
title={courseData.title}
|
||||
description={courseData.description}
|
||||
/>
|
||||
{/each}
|
||||
</section>
|
||||
{#if $courses.length > 3}
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
label="VIEW MORE PREPCOURSES"
|
||||
class="rounded-none text-lg font-semibold text-white"
|
||||
onClick={() => (viewAll = !viewAll)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="px-10">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- about -->
|
||||
{#if aboutSection?.show}
|
||||
<section id="course" class="h-full bg-white px-4 pb-20 pt-4 md:px-10">
|
||||
<h1 class="mx-auto w-full text-center font-serif text-3xl text-gray-800 md:w-[50%]">
|
||||
{aboutSection.settings.title}
|
||||
</h1>
|
||||
<p class="text-inter mx-auto mb-8 w-[60%] text-center font-medium text-gray-500">
|
||||
{aboutSection.settings.subtitle}
|
||||
</p>
|
||||
|
||||
<section class="flex flex-wrap items-center justify-center gap-8 p-4">
|
||||
{#each aboutSection.settings.benefits.list as item}
|
||||
<div
|
||||
class="flex h-fit max-h-[300px] max-w-full flex-col justify-start gap-4 rounded-lg border-2 border-gray-200 bg-white p-4 md:h-[150px] md:max-h-[200px] md:max-w-[500px]"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<CheckmarkFilled size={24} class="fill-blue-400" />
|
||||
<p class="font-serif text-xl">{item.title}</p>
|
||||
</span>
|
||||
<p class="font-inter text-base text-gray-400">{item.subtitle}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- learningpath -->
|
||||
<!-- <LearningPath /> -->
|
||||
|
||||
<!-- faq -->
|
||||
{#if faqSection?.show}
|
||||
<section class="h-full space-y-5 bg-slate-100 px-4 pb-20 pt-8">
|
||||
<h1 class="text-center font-serif text-3xl text-gray-700">{faqSection.settings.title}</h1>
|
||||
<section class="mx-auto w-full space-y-10 p-4 md:w-[80%]">
|
||||
{#each faqSection.settings.questions as faq}
|
||||
<Accordion title={faq.title} content={faq.content} />
|
||||
{/each}
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- cta -->
|
||||
{#if ctaSection?.show}
|
||||
<section
|
||||
class="flex flex-col items-center justify-between space-y-4 bg-blue-800 px-6 py-20 lg:px-10"
|
||||
>
|
||||
<div class="flex w-full items-center justify-center">
|
||||
<p
|
||||
class="w-full text-center font-serif text-4xl font-bold capitalize text-white lg:w-[70%]"
|
||||
>
|
||||
{ctaSection.settings.title}
|
||||
</p>
|
||||
</div>
|
||||
<p class="w-full text-center text-white lg:w-[50%]">
|
||||
{ctaSection.settings.subtitle}
|
||||
</p>
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
label={ctaSection.settings.button.label}
|
||||
href={ctaSection.settings.button.redirect && ctaSection.settings.button.link}
|
||||
class=" text-examprep bg-white text-lg font-bold uppercase hover:bg-white"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style>
|
||||
/* Optional smooth transition effect for text */
|
||||
:global(.testimonial-text-enter-active, .testimonial-text-leave-active) {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
:global(.testimonial-text-enter, .testimonial-text-leave-to) {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,8 @@ import { components as CalComponents } from './cal';
|
||||
import { components as PosthogComponents } from './posthog';
|
||||
import { components as ClassicComponents } from './classic';
|
||||
import { components as MinimalComponents } from './minimal';
|
||||
import { components as ExamprepComponents } from './examprep';
|
||||
import { components as WebflowComponents } from './webflow';
|
||||
|
||||
function getComponents() {
|
||||
switch (import.meta.env.VITE_TEMPLATE) {
|
||||
@@ -11,6 +13,10 @@ function getComponents() {
|
||||
return ClassicComponents;
|
||||
case 'minimal':
|
||||
return MinimalComponents;
|
||||
case 'examprep':
|
||||
return ExamprepComponents;
|
||||
case 'webflow':
|
||||
return WebflowComponents;
|
||||
default:
|
||||
return CalComponents;
|
||||
}
|
||||
|
||||
@@ -28,18 +28,18 @@
|
||||
</script>
|
||||
|
||||
<div class="h-fit w-full min-w-[250px] max-w-[300px] space-y-4">
|
||||
<div class="border border-[#EAEAEA] rounded-sm space-y-4 px-4 {className}">
|
||||
<div class="py-4 border-b overflow-hidden">
|
||||
<p class="text-xl font-semibold p border-[#EAEAEA] overflow-ellipsis line-clamp-1">
|
||||
<div class="space-y-4 rounded-sm border border-[#EAEAEA] px-4 {className}">
|
||||
<div class="overflow-hidden border-b py-4">
|
||||
<p class="p line-clamp-1 overflow-ellipsis border-[#EAEAEA] text-xl font-semibold">
|
||||
{title}
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-md overflow-hidden">
|
||||
<p class=" text-[#878787] overflow-ellipsis line-clamp-3 text-justify">
|
||||
<div class="overflow-hidden rounded-md">
|
||||
<p class=" line-clamp-3 overflow-ellipsis text-justify text-[#878787]">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<span class="flex justify-between items-center py-4">
|
||||
<span class="flex items-center justify-between py-4">
|
||||
<p>{lessons} Lessons</p>
|
||||
<p class="font-bold text-[#0233BD]">
|
||||
{!cost ? 'Free' : currency == 'USD' ? `$ ${cost}` : `N ${cost}`}
|
||||
@@ -48,9 +48,9 @@
|
||||
</div>
|
||||
<a
|
||||
href={getCourseUrl()}
|
||||
class="flex items-center justify-between w-full border border-[#EAEAEA] rounded-sm p-3 cursor-pointer hover:no-underline hover:scale-90 transition-all duration-200 {buttonClass}"
|
||||
class="flex w-full cursor-pointer items-center justify-between rounded-sm border border-[#EAEAEA] p-3 transition-all duration-200 hover:scale-95 hover:no-underline {buttonClass}"
|
||||
>
|
||||
<p class="font-bold text-[#0233BD] uppercase">Learn More</p>
|
||||
<p class="font-bold uppercase text-[#0233BD]">Learn More</p>
|
||||
<ArrowRight class="text-[#0233BD]" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const content = $derived(getPageSection($sharedPage, 'footer'));
|
||||
const seo = $derived(getPageSection($sharedPage, 'seo'));
|
||||
const content = $derived(getPageSection($sharedPage, SECTION.FOOTER));
|
||||
const seo = $derived(getPageSection($sharedPage, SECTION.SEO));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<nav
|
||||
class="bg-white w-full flex flex-col md:flex-row items-start md:items-center gap-4 md:justify-between py-4 px-6"
|
||||
class="flex w-full flex-col items-start gap-4 bg-white px-6 py-4 md:flex-row md:items-center md:justify-between"
|
||||
>
|
||||
<div class="logo">
|
||||
<a href="/" title={`Go to ${seo?.settings.title} Home`} id="logo" data-hveid="8">
|
||||
<img
|
||||
src={seo?.settings.logo}
|
||||
alt={`${seo?.settings.title} logo`}
|
||||
class="rounded w-9 inline-block mx-auto"
|
||||
data-atf="1"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} />
|
||||
<ul
|
||||
class="flex flex-col md:flex-row items-start md:items-center gap-4 -md:gap-8 font-semibold capitalize underline"
|
||||
class="-md:gap-8 flex flex-col items-start gap-4 font-semibold capitalize underline md:flex-row md:items-center"
|
||||
>
|
||||
{#if content.settings.twitter}
|
||||
<a href={content.settings.twitter} target="_blank" title="twitter">Twitter</a>
|
||||
@@ -36,7 +29,12 @@
|
||||
<a href={content.settings.facebook} target="_blank" title="facebook">facebook</a>
|
||||
{/if}
|
||||
</ul>
|
||||
<a href="/#" class="flex items-center hover:underline text-[#0233BD] dark:text-white gap-1">
|
||||
<a
|
||||
href="https://classroomio.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-minimal flex items-center gap-1 hover:underline dark:text-white"
|
||||
>
|
||||
<p class="text-base font-semibold">Built on ClassroomIO</p>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ArrowRight, ArrowUpRight, Close } from 'carbon-icons-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
@@ -17,17 +16,9 @@
|
||||
const content = $derived(getPageSection($sharedPage, 'navigation'));
|
||||
</script>
|
||||
|
||||
<nav class={`relative w-full flex items-center justify-between py-4 px-6 bg-white dark:bg-black`}>
|
||||
<nav class={`relative flex w-full items-center justify-between bg-white px-6 py-4 dark:bg-black`}>
|
||||
<!-- Logo Section -->
|
||||
<div class="logo">
|
||||
<a href="/" title={`${seo?.settings.title} home`} id="logo">
|
||||
<img
|
||||
src={seo?.settings.logo}
|
||||
alt={`${seo?.settings.title} logo`}
|
||||
class="rounded w-9 inline-block mx-auto"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} className="w-24" addAcademy />
|
||||
|
||||
<!-- Mobile Menu Button (Visible only on mobile and when logged in) -->
|
||||
|
||||
@@ -37,14 +28,14 @@
|
||||
|
||||
<!-- Desktop Navigation Menu (Hidden on mobile) -->
|
||||
<ul
|
||||
class="hidden lg:flex items-center space-x-8 text-base font-bold text-[#1F2937] list-none hover:no-underline"
|
||||
class="hidden list-none items-center space-x-8 text-base font-bold text-[#1F2937] hover:no-underline lg:flex"
|
||||
>
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li>
|
||||
<a href={navItem.link} class="flex items-center gap-1">
|
||||
{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight size={12} />
|
||||
<ArrowUpRight />
|
||||
{/if}
|
||||
</a>
|
||||
</li>
|
||||
@@ -53,34 +44,34 @@
|
||||
|
||||
<!-- Mobile Sidebar Menu (Visible only on mobile) -->
|
||||
<ul
|
||||
class={`fixed top-0 left-0 pt-10 h-full bg-white w-full transform hover:no-underline ${
|
||||
class={`fixed left-0 top-0 h-full w-full transform bg-white pt-10 hover:no-underline ${
|
||||
open ? 'translate-y-0' : '-translate-y-full'
|
||||
} transition-transform duration-300 ease-in-out lg:hidden text-base font-bold text-[#1F2937] list-none cursor-pointer`}
|
||||
} cursor-pointer list-none text-base font-bold text-[#1F2937] transition-transform duration-300 ease-in-out lg:hidden`}
|
||||
>
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 lg:hidden z-20">
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 z-20 lg:hidden">
|
||||
<Close size={24} />
|
||||
</button>
|
||||
|
||||
{#each content?.settings.navItem as navItem}
|
||||
<li class="py-4 px-6 border-b">
|
||||
<a href={navItem.link} onclick={toggleMenu} class="flex item-center gap-1">
|
||||
<li class="border-b px-6 py-4">
|
||||
<a href={navItem.link} onclick={toggleMenu} class="item-center flex gap-1">
|
||||
{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight size={12} />
|
||||
<ArrowUpRight />
|
||||
{/if}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
<a href="/courses" class="flex items-center gap-1 py-4 px-6 border-b" onclick={toggleMenu}>
|
||||
<p class="font-bold text-[#1F2937] text-base">Learn with me</p>
|
||||
<a href="/courses" class="flex items-center gap-1 border-b px-6 py-4" onclick={toggleMenu}>
|
||||
<p class="text-base font-bold text-[#1F2937]">Learn with me</p>
|
||||
<ArrowRight size={16} class="fill-[#1F2937] font-bold" />
|
||||
</a>
|
||||
</ul>
|
||||
|
||||
<!-- Learn with Me Button (Visible on desktop when logged in) -->
|
||||
|
||||
<a href="/courses" class="hidden lg:flex items-center gap-1">
|
||||
<p class="font-bold text-[#1F2937] text-base">Learn with me</p>
|
||||
<a href="/courses" class="hidden items-center gap-1 lg:flex">
|
||||
<p class="text-base font-bold text-[#1F2937]">Learn with me</p>
|
||||
<ArrowRight size={16} class="fill-[#1F2937] font-bold" />
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
@@ -16,7 +16,7 @@
|
||||
{href}
|
||||
onclick={onClick}
|
||||
class={cn(
|
||||
'rounded-none uppercase bg-minimal hover:bg-minimal p-3 hover:scale-95 font-bold',
|
||||
'rounded-none uppercase bg-minimal hover:bg-minimal p-3 font-bold hover:bg-minimal/90 hover:scale-95 transition-all duration-300',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
||||
|
After Width: | Height: | Size: 909 KiB |
@@ -8,6 +8,7 @@
|
||||
import PrimaryButton from '../PrimaryButton.svelte';
|
||||
import { courses } from '@/utils/stores/course';
|
||||
import CourseCard from '../CourseCard.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
@@ -43,8 +44,8 @@
|
||||
* Constants
|
||||
*/
|
||||
const section = $derived({
|
||||
header: getPageSection(data.page, 'header'),
|
||||
courses: getPageSection(data.page, 'courses')
|
||||
header: getPageSection(data.page, SECTION.HERO),
|
||||
courses: getPageSection(data.page, SECTION.COURSE)
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -64,26 +65,19 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="bg-[#101720] font-ibm overflow-hidden">
|
||||
<main class="font-ibm">
|
||||
{#if section.header?.show}
|
||||
<div class="relative h-full p-2 md:p-10">
|
||||
<div
|
||||
class="absolute inset-0 bg-cover bg-center"
|
||||
style="background-image: url('/classroomio-course-img-template.jpg');"
|
||||
></div>
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-r from-[#0233BD99] via-[#00000033] to-[#FFFFFF00]"
|
||||
></div>
|
||||
<div class="relative flex items-center justify-center h-full text-white px-4 py-20 md:px-10">
|
||||
<div class="text-white space-y-6 w-full">
|
||||
<p class="text-2xl md:text-4xl font-mono font-semibold w-full md:w-[60%] lg:w-[40%]">
|
||||
<div id="course-banner" class="h-full bg-cover bg-center md:h-[600px] md:p-10">
|
||||
<div class="relative flex h-full items-center justify-center px-4 py-20 text-white md:px-10">
|
||||
<div class="w-full space-y-6 text-white">
|
||||
<h1 class="w-full font-mono text-2xl font-semibold md:w-[60%] md:text-6xl">
|
||||
{section.header.settings.title}
|
||||
</p>
|
||||
</h1>
|
||||
<p class="w-full md:w-[60%] lg:w-[40%]">{section.header.settings.subtitle}</p>
|
||||
<div class="flex items-center gap-2 md:gap-4">
|
||||
<PrimaryButton
|
||||
onClick={() => goto(section.header?.settings.action.link)}
|
||||
class="uppercase bg-blue-900 px-6 py-3 font-semibold hover:bg-blue-900 hover:scale-95 rounded-none"
|
||||
class="rounded-none bg-blue-900 px-6 py-3 font-semibold uppercase hover:scale-95 hover:bg-blue-900"
|
||||
label={section.header.settings.action.label}
|
||||
/>
|
||||
</div>
|
||||
@@ -93,19 +87,19 @@
|
||||
{/if}
|
||||
|
||||
{#if section.courses?.show}
|
||||
<div class="bg-white py-10 px-2 md:px-8">
|
||||
<p class="dark:text-white text-3xl text-center font-semibold pb-10 w-full md:w-[70%] mx-auto">
|
||||
<div class="w-full border-b px-2 py-10 md:px-8">
|
||||
<p class="mx-auto w-full pb-10 text-center text-3xl font-semibold dark:text-white md:w-2/4">
|
||||
{section.courses.settings.title}
|
||||
</p>
|
||||
<div>
|
||||
<div class="py-10">
|
||||
{#if $courses.length > 0}
|
||||
<div class="w-full lg:flex items-start gap-4 lg:ml-[5%]">
|
||||
<div class="hidden lg:block w-max">
|
||||
<p class="font-medium mb-2">Filter by</p>
|
||||
<div class="mx-auto max-w-5xl items-start gap-8 lg:flex">
|
||||
<div class="hidden w-max lg:block">
|
||||
<p class="mb-2 font-medium">Filter by</p>
|
||||
<div class="w-max space-y-2">
|
||||
{#each filter as item}
|
||||
<form
|
||||
class="space-x-2 text-[#3C4043] font-medium border border-[#EAEAEA] bg-[#FDFDFD] rounded-md px-4 py-4"
|
||||
class="space-x-2 rounded-md border border-[#EAEAEA] bg-[#FDFDFD] px-4 py-4 font-medium text-[#3C4043]"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -119,7 +113,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<section
|
||||
class="grid place-items-center grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
|
||||
class="grid grid-cols-1 place-items-center gap-4 md:grid-cols-2 lg:grid-cols-3"
|
||||
>
|
||||
{#each filteredCourses.slice(0, viewAllCourses ? filteredCourses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
@@ -135,23 +129,23 @@
|
||||
</section>
|
||||
|
||||
{#if filteredCourses.length == 0}
|
||||
<div class="px-4 w-full mx-auto">
|
||||
<div class="mx-auto w-full px-4">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if filteredCourses.length > 3}
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
class="uppercase bg-blue-900 p-2"
|
||||
class="bg-blue-900 p-2 uppercase"
|
||||
onClick={() => (viewAllCourses = !viewAllCourses)}
|
||||
label="VIEW COURSES"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="px-4 w-full lg:w-[70%] mx-auto">
|
||||
<div class="mx-auto w-full px-4 lg:w-[70%]">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -159,3 +153,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style>
|
||||
#course-banner {
|
||||
background-image: url('$lib/components/minimal/assets/courses-pg-bg.png');
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,59 +3,58 @@
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import PrimaryButton from '../PrimaryButton.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import defaultBanner from '../assets/classroomio-course-img-template.jpg';
|
||||
import { courses } from '@/utils/stores/course';
|
||||
import CourseCard from '../CourseCard.svelte';
|
||||
import EmptyState from '../EmptyState.svelte';
|
||||
import TestimonialCard from '../TestimonialCard.svelte';
|
||||
import Accordion from '../Accordion.svelte';
|
||||
import testimonial from '../assets/testimonial.svg';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const courseSection = $derived(getPageSection($homePage, 'about'));
|
||||
const learnSection = $derived(getPageSection($homePage, 'about'));
|
||||
const testimonialSection = $derived(getPageSection($homePage, 'testimonial'));
|
||||
const faqSection = $derived(getPageSection($homePage, 'faq'));
|
||||
const footerNoteSection = $derived(getPageSection($homePage, 'cta'));
|
||||
const courseSection = $derived(getPageSection($homePage, SECTION.COURSE));
|
||||
const learnSection = $derived(getPageSection($homePage, SECTION.ABOUT));
|
||||
const testimonialSection = $derived(getPageSection($homePage, SECTION.TESTIMONIAL));
|
||||
const faqSection = $derived(getPageSection($homePage, SECTION.FAQ));
|
||||
const ctaSection = $derived(getPageSection($homePage, SECTION.CTA));
|
||||
|
||||
let viewAll = $state(false);
|
||||
</script>
|
||||
|
||||
<!-- for some reason the custom color doesnt work for this template -->
|
||||
<main class="bg-[#101720]">
|
||||
<main class="bg-minimal-secondary">
|
||||
<!-- hero -->
|
||||
{#if $homePage}
|
||||
{@const content = getPageSection($homePage, 'header')}
|
||||
{@const content = getPageSection($homePage, SECTION.HERO)}
|
||||
{#if content?.show}
|
||||
<section class="flex items-center justify-center py-5 px-5 md:py-14 md:px-14 max-h-full">
|
||||
<section class="flex items-center justify-between gap-4">
|
||||
<div class="text-white space-y-6 w-full">
|
||||
<div class="bg-[#DCFCFFED] py-1 px-3 md:border rounded-sm w-[90%]">
|
||||
<p class="text-center uppercase font-bold text-sm text-[#0F163F]">
|
||||
<section class="flex max-h-full items-center justify-center px-5 py-5 md:px-14 md:py-14">
|
||||
<section class="mx-auto flex max-w-6xl items-center justify-between gap-4">
|
||||
<div class="w-full max-w-[50%] space-y-6 text-white">
|
||||
<div class="w-fit rounded-sm bg-emerald-100 px-3 py-1 md:border">
|
||||
<p class="text-minimal text-center text-sm font-bold uppercase">
|
||||
{content.settings?.title}
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-4xl font-bold w-full md:w-[70%] capitalize">
|
||||
<h1 class="w-full text-5xl font-bold capitalize">
|
||||
{content.settings?.titleHighlight}
|
||||
</p>
|
||||
<p class="w-full md:w-[70%]">
|
||||
</h1>
|
||||
<p class="w-full">
|
||||
{content.settings?.subtitle}
|
||||
</p>
|
||||
<PrimaryButton
|
||||
class="bg-[#0542CC]"
|
||||
class="bg-minimal"
|
||||
onClick={() => {
|
||||
goto(content.settings?.action.link);
|
||||
}}
|
||||
label={content.settings?.action.label}
|
||||
/>
|
||||
</div>
|
||||
<div class="hidden rounded-md h-[280px] max-h-[400px] w-5/6 md:w-full lg:block">
|
||||
|
||||
<div class="hidden h-[280px] max-h-[400px] w-5/6 rounded-md md:w-full lg:block">
|
||||
<img
|
||||
style="min-width:280px; min-height:200px"
|
||||
alt="landing page banner"
|
||||
src={content.settings?.banner?.image
|
||||
? content.settings?.banner?.image
|
||||
: defaultBanner}
|
||||
class="mt-2 h-full max-h-[400px] w-full max-w-[500px] rounded-md md:mt-0 object-cover"
|
||||
src={content.settings?.banner?.image || '/course-banner.jpg'}
|
||||
class="mt-2 h-full max-h-[400px] w-full max-w-[500px] rounded-md object-cover md:mt-0"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
@@ -63,29 +62,30 @@
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<!-- learn -->
|
||||
<!-- Learn -->
|
||||
|
||||
{#if learnSection?.show}
|
||||
<section
|
||||
id="about"
|
||||
class="flex items-start justify-center px-5 md:px-14 h-full bg-white py-20 mx-auto"
|
||||
class="mx-auto flex h-full items-start justify-center border-b bg-white px-5 py-20 md:px-14"
|
||||
>
|
||||
<section class="flex flex-col lg:flex-row gap-8 items-start justify-center">
|
||||
<div class="w-full lg:w-[60%] space-y-4">
|
||||
<p class="text-4xl font-bold">{learnSection.settings.title}</p>
|
||||
<p class="w-full lg:w-[80%] text-base leading-7 text-[#878787]">
|
||||
<section class="flex flex-col items-start justify-center gap-8 lg:flex-row">
|
||||
<div class="w-full space-y-4 lg:w-2/4">
|
||||
<p class="max-w-[500px] text-4xl font-bold">{learnSection.settings.title}</p>
|
||||
<p class="w-full text-base leading-7 text-gray-500 lg:w-[80%]">
|
||||
{learnSection.settings.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
{#if learnSection.settings.benefits}
|
||||
<div class="w-full lg:w-fit space-y-8">
|
||||
<div class="w-full space-y-8 lg:w-fit">
|
||||
<span
|
||||
class="bg-[#DCFCFF] py-1 px-3 uppercase border border-[#0233BD] rounded-sm text-center font-bold text-base text-[#0233BD]"
|
||||
>{learnSection.settings.benefits.title}</span
|
||||
class="border-minimal text-minimal bg-minimal/10 rounded-sm border px-3 py-1 text-center text-base font-bold uppercase"
|
||||
>
|
||||
<ul class="space-y-6 text-base text-[#696969] font-medium">
|
||||
{learnSection.settings.benefits.title}
|
||||
</span>
|
||||
<ul class="space-y-6 text-base font-medium text-gray-600">
|
||||
{#each learnSection.settings.benefits.list as items}
|
||||
<li class="border-l border-[#0233BD] pl-3">{items.title}</li>
|
||||
<li class="border-minimal border-l pl-3">{items.title}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -93,26 +93,26 @@
|
||||
<img
|
||||
src={learnSection.settings.imageUrl}
|
||||
alt="Our Story"
|
||||
class="rounded-2xl max-h-[450px]"
|
||||
class="max-h-[450px] rounded-2xl"
|
||||
/>
|
||||
{/if}
|
||||
</section>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- courses -->
|
||||
<!-- Courses -->
|
||||
|
||||
{#if courseSection?.show}
|
||||
<section id="course" class="px-5 pt-10 space-y-10 py-36 h-full bg-white">
|
||||
<h1 class="text-center text-3xl font-bold mb-4 w-full md:w-[70%] mx-auto">
|
||||
<section id="course" class="h-full space-y-10 bg-white px-5 pb-36 pt-10">
|
||||
<h2 class="mx-auto mb-4 w-full text-center text-4xl font-bold md:w-[70%]">
|
||||
{courseSection.settings.title}
|
||||
</h1>
|
||||
<div class="w-full md:w-[90%] mx-auto">
|
||||
</h2>
|
||||
|
||||
<div class="mx-auto w-full md:w-[90%]">
|
||||
{#if $courses.length > 0}
|
||||
<section class="flex flex-wrap items-center w-fit mx-auto gap-4 p-4">
|
||||
<section class="mx-auto flex w-fit flex-wrap items-center justify-center gap-4 p-4">
|
||||
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
className="bg-[#FDFDFD]"
|
||||
slug={courseData.slug}
|
||||
title={courseData.title}
|
||||
description={courseData.description}
|
||||
@@ -123,7 +123,7 @@
|
||||
{/each}
|
||||
</section>
|
||||
{#if $courses.length > 3}
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
class="p-2"
|
||||
onClick={() => (viewAll = !viewAll)}
|
||||
@@ -132,7 +132,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="px-4 w-full lg:w-[70%] mx-auto">
|
||||
<div class="mx-auto w-full px-4 lg:w-[70%]">
|
||||
<EmptyState />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -140,16 +140,17 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- testimonial -->
|
||||
<!-- Testimonial -->
|
||||
|
||||
{#if testimonialSection?.show}
|
||||
<section id="testimonial" class="relative pt-10 h-full text-white">
|
||||
<div class="absolute -top-20 md:-top-32 left-[5%] w-[90%]">
|
||||
<img src={testimonial} alt="testimonial" class="w-full max-h-[500px] md:max-h-[300px]" />
|
||||
<section class="relative h-full pt-10 text-white">
|
||||
<div class="absolute -top-20 left-[5%] w-[90%] md:-top-32">
|
||||
<img src={testimonial} alt="testimonial" class="max-h-[500px] w-full md:max-h-[300px]" />
|
||||
</div>
|
||||
<div class="px-5 py-20 md:py-40 space-y-10">
|
||||
<h1 class="text-center text-3xl font-bold mb-4">Student testimonial</h1>
|
||||
<div class="w-full md:w-[90%] mx-auto">
|
||||
<section class="grid place-items-center grid-cols-1 md:grid-cols-3 w-fit mx-auto gap-4">
|
||||
<div class="space-y-10 px-5 py-20 md:py-40">
|
||||
<h1 class="mb-4 text-center text-3xl font-bold">Student testimonial</h1>
|
||||
<div class="mx-auto w-full md:w-[90%]">
|
||||
<section class="mx-auto grid w-fit grid-cols-1 place-items-center gap-4 md:grid-cols-3">
|
||||
{#each testimonialSection.settings.list as item}
|
||||
<TestimonialCard name={item.name} description={item.description} role={item.role} />
|
||||
{/each}
|
||||
@@ -159,11 +160,11 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- faq -->
|
||||
<!-- FAQ -->
|
||||
{#if faqSection?.show}
|
||||
<section class="px-5 py-20 space-y-10 h-full bg-[#F9F9F9]">
|
||||
<h1 class="text-center text-3xl font-bold mb-4">{faqSection.settings.title}</h1>
|
||||
<section class="p-4 space-y-10 w-full md:w-[80%] mx-auto">
|
||||
<section class="h-full space-y-10 bg-gray-100 px-5 py-20">
|
||||
<h1 class="mb-4 text-center text-3xl font-bold">{faqSection.settings.title}</h1>
|
||||
<section class="mx-auto w-full space-y-10 p-4 md:w-[80%]">
|
||||
{#each faqSection.settings.questions as faq}
|
||||
<Accordion title={faq.title} content={faq.content} />
|
||||
{/each}
|
||||
@@ -171,23 +172,23 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<!-- footerNote -->
|
||||
{#if footerNoteSection?.show}
|
||||
<!-- cta -->
|
||||
{#if ctaSection?.show}
|
||||
<section
|
||||
class="flex flex-col md:flex-row items-center justify-between px-6 lg:px-10 py-20 bg-blue-800"
|
||||
class="flex flex-col items-center justify-between bg-blue-800 px-6 py-20 md:flex-row lg:px-10"
|
||||
>
|
||||
<div class="flex items-center justify-center w-full">
|
||||
<div class="flex w-full items-center justify-center">
|
||||
<p
|
||||
class="text-4xl text-white text-center md:text-start font-bold w-full lg:w-[70%] capitalize"
|
||||
class="w-full text-center text-4xl font-bold capitalize text-white md:text-start lg:w-[70%]"
|
||||
>
|
||||
{footerNoteSection.settings.title}
|
||||
{ctaSection.settings.title}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<PrimaryButton
|
||||
onClick={() => goto('/courses')}
|
||||
class="py-2 bg-white text-blue-700 hover:bg-white"
|
||||
label={footerNoteSection.settings.buttonLabel}
|
||||
href={ctaSection.settings?.button?.link}
|
||||
class="bg-white py-2 text-blue-700 hover:bg-white"
|
||||
label={ctaSection.settings?.button?.label}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import FaqMark from './static/icons/FaqMark.svelte';
|
||||
import FaqMark from './assets/icons/FaqMark.svelte';
|
||||
|
||||
interface Props {
|
||||
faq: any;
|
||||
@@ -33,26 +33,26 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="z-10 min-w-60 max-w-72 h-72 perspective group"
|
||||
class="min-w-60 max-w-72 perspective group z-10 h-72"
|
||||
bind:this={card}
|
||||
onmousemove={handleMouseMove}
|
||||
onmouseleave={handleMouseLeave}
|
||||
role="none"
|
||||
>
|
||||
<div class={`inner-card ${index % 2 === 0 ? 'rotate-3 ' : '-rotate-3 '} `}>
|
||||
<span class="z-10 absolute w-2 h-2 rounded-full {iconColor} top-4 left-4"></span>
|
||||
<span class="z-10 absolute w-[2px] h-[2px] rounded-full {iconColor} top-8 left-8"></span>
|
||||
<span class="z-10 absolute w-2 h-2 rounded-full {iconColor} bottom-4 right-4"></span>
|
||||
<span class="z-10 absolute w-[2px] h-[2px] rounded-full {iconColor} bottom-8 right-8"></span>
|
||||
<span class="absolute z-10 h-2 w-2 rounded-full {iconColor} left-4 top-4"></span>
|
||||
<span class="absolute z-10 h-[2px] w-[2px] rounded-full {iconColor} left-8 top-8"></span>
|
||||
<span class="absolute z-10 h-2 w-2 rounded-full {iconColor} bottom-4 right-4"></span>
|
||||
<span class="absolute z-10 h-[2px] w-[2px] rounded-full {iconColor} bottom-8 right-8"></span>
|
||||
<div
|
||||
class="front text-black shadow-md flex flex-col items-center justify-center p-4 {background}"
|
||||
class="front flex flex-col items-center justify-center p-4 text-black shadow-md {background}"
|
||||
>
|
||||
<FaqMark color="#3B82F6" />
|
||||
<h3 class="text-lg font-bold text-center">{faq.title}</h3>
|
||||
<h3 class="text-center text-lg font-bold">{faq.title}</h3>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="back text-black text-center text-sm shadow-md flex items-center justify-center p-4 {background}"
|
||||
class="back flex items-center justify-center p-4 text-center text-sm text-black shadow-md {background}"
|
||||
>
|
||||
<p class="line-clamp-5">{faq.content}</p>
|
||||
</div>
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
<script lang="ts">
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { sharedPage } from '$lib/utils/stores/pages';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const content = $derived(getPageSection($sharedPage, 'footer'));
|
||||
const seo = $derived(getPageSection($sharedPage, 'seo'));
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
const content = $derived(getPageSection($sharedPage, SECTION.FOOTER));
|
||||
const seo = $derived(getPageSection($sharedPage, SECTION.SEO));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<nav
|
||||
class="w-full h-40 bg-posthog-background border-t border-posthog-border flex flex-col md:flex-row items-start md:items-center gap-4 md:justify-between py-4 px-6"
|
||||
class="bg-posthog-background border-posthog-border flex h-40 w-full flex-col items-start gap-4 border-t px-6 py-4 md:flex-row md:items-center md:justify-between"
|
||||
>
|
||||
<div class="logo">
|
||||
<a href="/" title={`Go to ${seo?.settings.title} Home`} id="logo" data-hveid="8">
|
||||
<img
|
||||
src={seo?.settings.logo}
|
||||
alt={`${seo?.settings.title} logo`}
|
||||
class="w-24"
|
||||
data-atf="1"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} className="w-24" />
|
||||
<ul
|
||||
class="flex flex-col md:flex-row items-start md:items-center gap-4 -md:gap-8 font-semibold capitalize underline"
|
||||
class="-md:gap-8 flex flex-col items-start gap-4 font-semibold capitalize underline md:flex-row md:items-center"
|
||||
>
|
||||
{#if content.settings.twitter}
|
||||
<a href={content.settings.twitter} target="_blank" title="twitter">Twitter</a>
|
||||
@@ -42,7 +31,7 @@
|
||||
</ul>
|
||||
<a
|
||||
href="https://classroomio.com"
|
||||
class="flex items-center hover:underline text-[#0233BD] dark:text-white gap-1"
|
||||
class="flex items-center gap-1 text-blue-700 hover:underline dark:text-white"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import type { Section } from '@/utils/types/page';
|
||||
import PrimaryButton from './PrimaryButton.svelte';
|
||||
import { ArrowUpRight } from 'carbon-icons-svelte';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
@@ -20,19 +21,10 @@
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class={`relative w-full flex items-center justify-between border-b border-posthog-border py-3 px-6`}
|
||||
class={`border-posthog-border relative flex w-full items-center justify-between border-b px-6 py-3`}
|
||||
>
|
||||
<!-- Logo Section -->
|
||||
<a href="/" class="flex items-center flex-col" title={`${seo?.settings?.title}`} id="logo">
|
||||
{#if seo?.settings?.logo}
|
||||
<img
|
||||
src={seo?.settings?.logo || ''}
|
||||
alt={`${seo?.settings?.title || ''} logo`}
|
||||
class="w-24"
|
||||
/>
|
||||
{/if}
|
||||
<h1 class="text-lg font-semibold">University</h1>
|
||||
</a>
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} className="w-24" addAcademy />
|
||||
|
||||
<!-- Mobile Menu Button (Visible only on mobile and when logged in) -->
|
||||
<button onclick={toggleMenu} class="lg:hidden">
|
||||
@@ -40,14 +32,14 @@
|
||||
</button>
|
||||
|
||||
<!-- Desktop Navigation Menu (Hidden on mobile) -->
|
||||
<ul class="hidden lg:flex items-center space-x-8 list-none hover:no-underline">
|
||||
<ul class="hidden list-none items-center space-x-8 hover:no-underline lg:flex">
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<a
|
||||
href={navItem.link}
|
||||
target={navItem.redirect ? '_blank' : undefined}
|
||||
class="text-sm text-gray-500 hover:text-black"
|
||||
>
|
||||
<li class="hover:no-underline flex items-center gap-1">
|
||||
<li class="flex items-center gap-1 hover:no-underline">
|
||||
{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight size={16} />
|
||||
@@ -62,17 +54,17 @@
|
||||
<!-- Mobile Sidebar Menu (Visible only on mobile) -->
|
||||
|
||||
<ul
|
||||
class={`fixed top-0 left-0 pt-10 h-full bg-white dark:bg-black w-full transform hover:no-underline ${
|
||||
class={`fixed left-0 top-0 h-full w-full transform bg-white pt-10 hover:no-underline dark:bg-black ${
|
||||
open ? 'translate-y-0' : '-translate-y-full'
|
||||
} transition-transform duration-300 ease-in-out lg:hidden text-base font-bold text-[#1F2937] dark:text-white list-none cursor-pointer`}
|
||||
} cursor-pointer list-none text-base font-bold text-[#1F2937] transition-transform duration-300 ease-in-out dark:text-white lg:hidden`}
|
||||
>
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 lg:hidden z-20">
|
||||
<button onclick={toggleMenu} class="absolute right-4 top-4 z-20 lg:hidden">
|
||||
<Close size={24} />
|
||||
</button>
|
||||
|
||||
{#each content?.settings.navItems as navItem}
|
||||
<li class="py-4 px-6 border-b dark:border-gray-200">
|
||||
<a href={navItem.link} onclick={toggleMenu} class="flex item-center gap-1"
|
||||
<li class="border-b px-6 py-4 dark:border-gray-200">
|
||||
<a href={navItem.link} onclick={toggleMenu} class="item-center flex gap-1"
|
||||
>{navItem.title}
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight size={16} />
|
||||
@@ -81,13 +73,13 @@
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
<div class="border-b dark:border-gray-200 py-4 px-6">
|
||||
<div class="border-b px-6 py-4 dark:border-gray-200">
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
goto('/courses');
|
||||
toggleMenu;
|
||||
}}
|
||||
class="bg-[#F7A50180] hover:bg-[#f7a501ad] dark:bg-[#F7A501] hover:no-underline rounded-lg flex items-center py-2 px-3 border w-fit border-[#4A4C524D] font-bold text-black text-base"
|
||||
class="flex w-fit items-center rounded-lg border border-[#4A4C524D] bg-[#F7A50180] px-3 py-2 text-base font-bold text-black hover:bg-[#f7a501ad] hover:no-underline dark:bg-[#F7A501]"
|
||||
label="Explore Course"
|
||||
/>
|
||||
</div>
|
||||
@@ -95,7 +87,7 @@
|
||||
|
||||
<!-- Learn with Me Button (Visible on desktop when logged in) -->
|
||||
|
||||
<div class="hidden lg:flex items-center gap-2">
|
||||
<div class="hidden items-center gap-2 lg:flex">
|
||||
<PrimaryButton onClick={() => goto('/courses')} label="Explore Course" />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
@@ -14,12 +14,12 @@
|
||||
|
||||
<div class="relative inline-block group">
|
||||
<span
|
||||
class="absolute inset-0 -bottom-1 ring-1 ring-posthog bg-posthog rounded-md transition-transform duration-200 ease-in-out"
|
||||
class="absolute inset-0 -bottom-1 ring-1 ring-posthog-secondary bg-posthog-secondary rounded-md transition-transform duration-200 ease-in-out"
|
||||
></span>
|
||||
<Button
|
||||
{href}
|
||||
class={cn(
|
||||
'bg-white group-hover:bg-white text-black ring-1 ring-posthog font-bold p-2 rounded-md relative z-10 transition-transform duration-200 ease-in-out hover:-translate-y-[2px]',
|
||||
'bg-white group-hover:bg-white text-black ring-1 ring-posthog-secondary font-bold p-2 rounded-md relative z-10 transition-transform duration-200 ease-in-out hover:-translate-y-[2px]',
|
||||
className
|
||||
)}
|
||||
onclick={onClick}
|
||||
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 431 KiB After Width: | Height: | Size: 431 KiB |
@@ -6,6 +6,7 @@
|
||||
import type { CourseFilterItem, Course } from '$lib/utils/types/course';
|
||||
import type { Page } from '$lib/utils/types/page';
|
||||
import { COURSE_TYPE } from '@/utils/constants/course';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
@@ -41,8 +42,8 @@
|
||||
* Constants
|
||||
*/
|
||||
const section = $derived({
|
||||
header: getPageSection(data.page, 'header'),
|
||||
courses: getPageSection(data.page, 'courses')
|
||||
header: getPageSection(data.page, SECTION.HERO),
|
||||
courses: getPageSection(data.page, SECTION.COURSE)
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,18 +3,19 @@
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { homePage } from '@/utils/stores/pages';
|
||||
import PrimaryButton from '../PrimaryButton.svelte';
|
||||
import analytics from '../static/analytics.png';
|
||||
import analytics from '../assets/analytics.png';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { courses } from '@/utils/stores/course';
|
||||
import CourseCard from '../CourseCard.svelte';
|
||||
import EmptyState from '../EmptyState.svelte';
|
||||
import { CheckmarkFilled } from 'carbon-icons-svelte';
|
||||
import FaqCard from '../FaqCard.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const coursesSection = $derived(getPageSection($homePage, 'courses'));
|
||||
const aboutSection = $derived(getPageSection($homePage, 'about'));
|
||||
const faqSection = $derived(getPageSection($homePage, 'faq'));
|
||||
const footerNoteSection = $derived(getPageSection($homePage, 'cta'));
|
||||
const coursesSection = $derived(getPageSection($homePage, SECTION.COURSE));
|
||||
const aboutSection = $derived(getPageSection($homePage, SECTION.ABOUT));
|
||||
const faqSection = $derived(getPageSection($homePage, SECTION.FAQ));
|
||||
const ctaSection = $derived(getPageSection($homePage, SECTION.CTA));
|
||||
|
||||
function getCourseUrl() {
|
||||
return '/courses';
|
||||
@@ -26,20 +27,20 @@
|
||||
<div class="bg-posthog-background">
|
||||
<!-- hero -->
|
||||
{#if $homePage}
|
||||
{@const content = getPageSection($homePage, 'header')}
|
||||
{@const content = getPageSection($homePage, SECTION.HERO)}
|
||||
{#if content?.show}
|
||||
<section
|
||||
class="font-matter flex items-center justify-center py-14 px-10 md:px-14 min-h-full overflow-hidden"
|
||||
class="font-matter flex min-h-full items-center justify-center overflow-hidden px-10 py-14 md:px-14"
|
||||
>
|
||||
<section
|
||||
class="flex flex-col-reverse md:flex-col text-center items-center gap-5 justify-center"
|
||||
class="flex flex-col-reverse items-center justify-center gap-5 text-center md:flex-col"
|
||||
>
|
||||
<div class="space-y-6 w-full mb-4">
|
||||
<p class="text-3xl md:text-5xl font-bold w-full md:w-[80%] lg:w-[70%] mx-auto">
|
||||
<div class="mb-4 w-full space-y-6">
|
||||
<p class="mx-auto w-full text-3xl font-bold md:w-[80%] md:text-5xl lg:w-[70%]">
|
||||
{content.settings.title}
|
||||
</p>
|
||||
|
||||
<p class="w-full text-lg md:w-[80%] lg:w-[70%] mx-auto">
|
||||
<p class="mx-auto w-full text-lg md:w-[80%] lg:w-[70%]">
|
||||
{content.settings.subtitle}
|
||||
</p>
|
||||
<PrimaryButton
|
||||
@@ -50,13 +51,13 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class=" relative rounded-lg w-full md:w-[800px] md:max-w-[80vw] lg:max-w-[80%] flex">
|
||||
<span class="absolute w-2 h-2 rounded-full bg-red-500 -top-14 left-3"></span>
|
||||
<span class="absolute w-2 h-2 rounded-full bg-white top-10 -left-10"></span>
|
||||
<span class="absolute w-2 h-2 rounded-full bg-yellow-500 -top-10 -right-3"></span>
|
||||
<span class="absolute w-2 h-2 rounded-full bg-blue-800 top-14 -right-10"></span>
|
||||
<div class=" relative flex w-full rounded-lg md:w-[800px] md:max-w-[80vw] lg:max-w-[80%]">
|
||||
<span class="absolute -top-14 left-3 h-2 w-2 rounded-full bg-red-500"></span>
|
||||
<span class="absolute -left-10 top-10 h-2 w-2 rounded-full bg-white"></span>
|
||||
<span class="absolute -right-3 -top-10 h-2 w-2 rounded-full bg-yellow-500"></span>
|
||||
<span class="absolute -right-10 top-14 h-2 w-2 rounded-full bg-blue-800"></span>
|
||||
|
||||
<div class="w-full h-full flex items-center justify-center">
|
||||
<div class="flex h-full w-full items-center justify-center">
|
||||
<img alt="Hero Banner" src={analytics} class="w-full rounded-lg object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,15 +68,15 @@
|
||||
|
||||
<!-- courses -->
|
||||
{#if coursesSection?.show}
|
||||
<section id="course" class="px-4 pt-4 pb-20 h-full md:px-10">
|
||||
<h1 class="text-center text-3xl mb-8 font-bold w-full md:w-[90%] lg:w-[70%] mx-auto">
|
||||
<section id="course" class="h-full px-4 pb-20 pt-4 md:px-10">
|
||||
<h1 class="mx-auto mb-8 w-full text-center text-3xl font-bold md:w-[90%] lg:w-[70%]">
|
||||
{coursesSection.settings.title}
|
||||
<span class="text-[#F54E00] dark:text-[#EB9D2A]"
|
||||
>{coursesSection.settings.titleHighlight}</span
|
||||
>
|
||||
</h1>
|
||||
{#if $courses.length > 0}
|
||||
<section class="flex flex-wrap items-center justify-center mx-auto gap-4">
|
||||
<section class="mx-auto flex flex-wrap items-center justify-center gap-4">
|
||||
{#each $courses.slice(0, viewAll ? $courses.length : 3) as courseData}
|
||||
<CourseCard
|
||||
slug={courseData.slug}
|
||||
@@ -85,17 +86,17 @@
|
||||
{/each}
|
||||
</section>
|
||||
{#if $courses.length > 3}
|
||||
<div class="w-full flex items-center justify-center my-5">
|
||||
<div class="my-5 flex w-full items-center justify-center">
|
||||
<Button
|
||||
on:click={() => (viewAll = !viewAll)}
|
||||
class="dark:text-black bg-white text-black capitalize ring-1 ring-[#B17816] dark:bg-[#EB9D2A] hover:scale-95 hover:bg-[#EB9D2A] shadow-[0px_3px_#B17816] font-bold mb-4"
|
||||
class="mb-4 bg-white font-bold capitalize text-black shadow-[0px_3px_#B17816] ring-1 ring-[#B17816] hover:scale-95 hover:bg-[#EB9D2A] dark:bg-[#EB9D2A] dark:text-black"
|
||||
>
|
||||
{viewAll === true ? 'Show less' : 'View more courses'}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="w-full lg:w-[70%] mx-auto">
|
||||
<div class="mx-auto w-full lg:w-[70%]">
|
||||
<EmptyState className="dark:bg-[#232429] dark:border-[#EAEAEA]" />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -106,24 +107,24 @@
|
||||
{#if aboutSection?.show}
|
||||
<section
|
||||
id="about"
|
||||
class="px-4 md:px-10 pt-4 pb-20 h-full w-full mx-auto md:w-[90%] lg:w-[80%]"
|
||||
class="mx-auto h-full w-full px-4 pb-20 pt-4 md:w-[90%] md:px-10 lg:w-[80%]"
|
||||
>
|
||||
<h1 class="text-start text-3xl mb-2 font-bold md:w-[50%]">{aboutSection.settings.title}</h1>
|
||||
<p class="text-start text-[#3D3D3D] dark:text-white text-sm font-semibold md:w-[70%] mb-8">
|
||||
<h1 class="mb-2 text-start text-3xl font-bold md:w-[50%]">{aboutSection.settings.title}</h1>
|
||||
<p class="mb-8 text-start text-sm font-semibold text-[#3D3D3D] dark:text-white md:w-[70%]">
|
||||
{aboutSection.settings.subtitle}
|
||||
</p>
|
||||
<div>
|
||||
<p class="text-xl font-bold capitalize mb-6">{aboutSection.settings.benefits.title}</p>
|
||||
<section class="grid place-items-center grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<p class="mb-6 text-xl font-bold capitalize">{aboutSection.settings.benefits.title}</p>
|
||||
<section class="grid grid-cols-1 place-items-center gap-4 md:grid-cols-2">
|
||||
{#each aboutSection.settings.benefits.list as item}
|
||||
<div
|
||||
class="flex flex-col gap-2 p-4 bg-[#E5E7E0] dark:bg-[#232429] border border-[#D0D1C9] h-[150px] max-h-[200px] w-full max-w-full md:max-w-[320px] lg:max-w-[450px] rounded-lg overflow-hidden"
|
||||
class="flex h-[150px] max-h-[200px] w-full max-w-full flex-col gap-2 overflow-hidden rounded-lg border border-[#D0D1C9] bg-[#E5E7E0] p-4 dark:bg-[#232429] md:max-w-[320px] lg:max-w-[450px]"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<CheckmarkFilled size={24} class=" fill-[#F54E00] dark:fill-[#EB9D2A]" />
|
||||
<p class="text-lg font-bold capitalize line-clamp-1">{item.title}</p>
|
||||
<p class="line-clamp-1 text-lg font-bold capitalize">{item.title}</p>
|
||||
</span>
|
||||
<p class="text-sm line-clamp-4">{item.subtitle}</p>
|
||||
<p class="line-clamp-4 text-sm">{item.subtitle}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
@@ -134,8 +135,8 @@
|
||||
<!-- faq -->
|
||||
{#if faqSection?.show}
|
||||
<div id="faq">
|
||||
<h1 class="text-center text-3xl mb-8 font-bold">{faqSection.settings.title}</h1>
|
||||
<div class="flex flex-wrap items-center justify-center mx-auto gap-8 p-4">
|
||||
<h1 class="mb-8 text-center text-3xl font-bold">{faqSection.settings.title}</h1>
|
||||
<div class="mx-auto flex flex-wrap items-center justify-center gap-8 p-4">
|
||||
{#each faqSection.settings.questions as faq, index}
|
||||
<FaqCard {index} {faq} />
|
||||
{/each}
|
||||
@@ -143,33 +144,33 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- footerNote -->
|
||||
{#if footerNoteSection?.show}
|
||||
<section class="w-full h-full p-4 md:p-20">
|
||||
<!-- cta -->
|
||||
{#if ctaSection?.show}
|
||||
<section class="h-full w-full p-4 md:p-20">
|
||||
<section
|
||||
class="mx-auto w-fit md:w-[80%] flex flex-col items-center justify-between px-2 lg:px-5 py-20 space-y-4 bg-[#E5E7E0] dark:bg-[#232429] rounded-md"
|
||||
class="mx-auto flex w-fit flex-col items-center justify-between space-y-4 rounded-md bg-[#E5E7E0] px-2 py-20 dark:bg-[#232429] md:w-[80%] lg:px-5"
|
||||
>
|
||||
<div class="flex items-center justify-center w-full">
|
||||
<div class="flex w-full items-center justify-center">
|
||||
<p
|
||||
class="text-4xl text-center xl:text-start font-bold w-full mx-auto lg:w-[90%] capitalize"
|
||||
class="mx-auto w-full text-center text-4xl font-bold capitalize lg:w-[90%] xl:text-start"
|
||||
>
|
||||
{footerNoteSection.settings.title}
|
||||
{ctaSection.settings.title}
|
||||
|
||||
<span class="text-[#F54E00]">{footerNoteSection.settings.titleHighlight}</span>
|
||||
<span class="text-[#F54E00]">{ctaSection.settings.titleHighlight}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-full lg:w-[90%] flex flex-col lg:flex-row items-center lg:items-end gap-4 lg:gap-0 lg:justify-between"
|
||||
class="flex w-full flex-col items-center gap-4 lg:w-[90%] lg:flex-row lg:items-end lg:justify-between lg:gap-0"
|
||||
>
|
||||
<p class="text-center lg:text-start text-sm font-semibold w-full lg:w-[50%]">
|
||||
{footerNoteSection.settings.subtitle}
|
||||
<p class="w-full text-center text-sm font-semibold lg:w-[50%] lg:text-start">
|
||||
{ctaSection.settings.subtitle}
|
||||
</p>
|
||||
|
||||
<div class="w-fit">
|
||||
<PrimaryButton
|
||||
href={getCourseUrl()}
|
||||
class="px-8 transition bg-white hover:bg-white text-black"
|
||||
label={footerNoteSection.settings.buttonLabel}
|
||||
href={ctaSection.settings?.button?.link}
|
||||
class="bg-white px-8 text-black transition hover:bg-white"
|
||||
label={ctaSection.settings?.button?.label}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
let { src, alt, className = '', addAcademy = false } = $props();
|
||||
</script>
|
||||
|
||||
{#snippet logo(src = '/logo.png', alt = 'logo', className = '')}
|
||||
<a href="/#" class={cn('w-20', className)}>
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
class="mx-auto inline-block rounded text-white"
|
||||
/>
|
||||
</a>
|
||||
{/snippet}
|
||||
|
||||
{#if addAcademy}
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
{@render logo(src, alt, className)}
|
||||
<span class="mt-1 text-lg font-semibold italic">Academy</span>
|
||||
</div>
|
||||
{:else}
|
||||
{@render logo(src, alt, className)}
|
||||
{/if}
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils';
|
||||
export let width: string | number = 16;
|
||||
export let height: string | number = 16;
|
||||
export let x = 0;
|
||||
export let y = 0;
|
||||
export let cx = 1;
|
||||
export let cy = 1;
|
||||
export let cr = 1;
|
||||
export let fillColor = 'rgb(163 163 163 / 0.8)';
|
||||
|
||||
let className: any = '';
|
||||
export { className as class };
|
||||
let id = crypto.randomUUID().toString().slice(0, 10); // generating a unique ID for Components
|
||||
</script>
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class={cn('pointer-events-none absolute inset-0 h-full w-full ', className)}
|
||||
{...$$restProps}
|
||||
fill={fillColor}
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
{id}
|
||||
{width}
|
||||
{height}
|
||||
patternUnits="userSpaceOnUse"
|
||||
patternContentUnits="userSpaceOnUse"
|
||||
{x}
|
||||
{y}
|
||||
>
|
||||
<circle id="pattern-circle" {cx} {cy} r={cr} />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" stroke-width={0} fill={`url(#${id})`} />
|
||||
</svg>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as DotPattern } from './dot-pattern.svelte';
|
||||
@@ -38,7 +38,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="spark mask-gradient animate-flip before:animate-kitrotate absolute inset-0 h-full w-full overflow-hidden rounded dark:[mask:linear-gradient(white,_transparent_50%)] before:absolute before:aspect-square before:w-[200%] before:rotate-[-90deg] before:dark:bg-[conic-gradient(from_0deg,transparent_0_340deg,white_360deg)] before:bg-[conic-gradient(from_0deg,black_360deg,transparent_0_340deg)] before:content-[''] before:[inset:0_auto_auto_50%] before:[translate:-50%_-15%]"
|
||||
></span>
|
||||
class="spark mask-gradient animate-flip before:animate-kitrotate absolute inset-0 h-full w-full overflow-hidden rounded [mask:linear-gradient(white,_transparent_50%)] before:absolute before:aspect-square before:w-[200%] before:rotate-[-90deg] before:dark:bg-[conic-gradient(from_0deg,transparent_0_340deg,white_360deg)] before:bg-[conic-gradient(from_0deg,black_360deg,transparent_0_340deg)] before:content-[''] before:[inset:0_auto_auto_50%] before:[translate:-50%_-15%]"
|
||||
></span>
|
||||
</span>
|
||||
<span
|
||||
class="backdrop absolute inset-px rounded bg-neutral-950 transition-colors"
|
||||
style="--duration: {duration}; --delay: {delay};"
|
||||
></span>
|
||||
></span>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||
import Check from "lucide-svelte/icons/check";
|
||||
import Minus from "lucide-svelte/icons/minus";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
type $$Props = CheckboxPrimitive.Props;
|
||||
type $$Events = CheckboxPrimitive.Events;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Label as LabelPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
type $$Props = LabelPrimitive.Props;
|
||||
type $$Events = LabelPrimitive.Events;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
children
|
||||
}: Props = $props();
|
||||
|
||||
|
||||
let gradSize = useMotionValue(gradientSize);
|
||||
let gradColor = useMotionValue(gradientColor);
|
||||
let mouseX = useMotionValue(-gradientSize);
|
||||
@@ -46,32 +45,31 @@
|
||||
let bg = useMotionTemplate`radial-gradient(${gradSize}px circle at ${mouseX}px ${mouseY}px, ${gradColor}, transparent 100%)`;
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
onmousemove={handleMouseMove}
|
||||
onmouseleave={handleMouseLeave}
|
||||
class={cn(
|
||||
'group relative flex w-fit rounded bg-neutral-100 dark:bg-neutral-900 justify-center',
|
||||
className
|
||||
)}
|
||||
onmousemove={handleMouseMove}
|
||||
onmouseleave={handleMouseLeave}
|
||||
class={cn('group relative flex w-fit justify-center rounded bg-neutral-900', className)}
|
||||
>
|
||||
<CardAnimatedBorder index={index + 1}>
|
||||
<CardAnimatedBorder index={index + 1}>
|
||||
<div class="relative z-10">
|
||||
|
||||
{@render children?.()}
|
||||
|
||||
</div>
|
||||
|
||||
<Motion
|
||||
style={{
|
||||
background: bg,
|
||||
opacity: gradientOpacity
|
||||
}}
|
||||
|
||||
let:motion
|
||||
>
|
||||
{#snippet children({ motion })}
|
||||
<div
|
||||
use:motion
|
||||
class="pointer-events-none absolute inset-px rounded opacity-0 transition-opacity duration-300 group-hover:opacity-100"
|
||||
></div>
|
||||
{/snippet}
|
||||
</Motion>
|
||||
</CardAnimatedBorder>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,16 +1,15 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
interface Props {
|
||||
class?: $$Props["class"];
|
||||
[key: string]: any
|
||||
class?: $$Props['class'];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
let { class: className = undefined, ...rest }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div class={cn("bg-muted animate-pulse rounded-md", className)} {...rest}></div>
|
||||
<div class={cn('bg-muted animate-pulse rounded-md', className)} {...rest}></div>
|
||||
|
||||
@@ -24,16 +24,20 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="hover:dark:bg-[#282828] space-y-2 shadow-md bg-white dark:bg-inherit border dark:border-[#282828] rounded"
|
||||
class="hover:bg-[#282828] text-white space-y-2 shadow-md bg-[#282828] {isOpen
|
||||
? 'bg-[#282828]'
|
||||
: 'bg-inherit'} border border-[#282828] rounded"
|
||||
>
|
||||
<!-- Accordion Header -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="flex justify-between items-center cursor-pointer p-4 bg-white dark:bg-inherit rounded"
|
||||
class="flex justify-between items-center cursor-pointer p-4 bg-inherit rounded"
|
||||
onclick={toggleAccordion}
|
||||
>
|
||||
<p class="font-semibold text-lg">{title}</p>
|
||||
<!-- Icon changes based on the state -->
|
||||
<div class="flex items-center justify-center p-[2px] dark:bg-[#232429]">
|
||||
<div class="flex items-center justify-center p-[2px] bg-[#232429]">
|
||||
{#if isOpen}
|
||||
<Subtract class="text-xl transition-transform duration-200 ease-in" />
|
||||
{:else}
|
||||
@@ -47,7 +51,7 @@
|
||||
bind:this={contentRef}
|
||||
style="max-height: {contentHeight}; transition: max-height 0.3s ease-in-out; overflow: hidden;"
|
||||
>
|
||||
<div class="p-4 bg-white dark:bg-inherit rounded-b">
|
||||
<div class="p-4 bg-inherit rounded-b">
|
||||
<p>
|
||||
{content}
|
||||
</p>
|
||||
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import PrimaryButton from './PrimaryButton.svelte';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
bannerImage: string | undefined;
|
||||
slug?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
let { className = '', bannerImage, slug = '', title = '', description = '' }: Props = $props();
|
||||
|
||||
function getCourseUrl() {
|
||||
return `/course/${slug}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="h-fit w-full min-w-[250px] max-w-[320px] cursor-pointer space-y-4 md:min-w-[200px] md:max-w-[300px]"
|
||||
>
|
||||
<div
|
||||
class="relative space-y-4 rounded-sm border border-[#282828] px-2 py-2 hover:bg-[#282828] {className} shadow"
|
||||
>
|
||||
<div class="overflow-hidden rounded-md">
|
||||
<img
|
||||
src={bannerImage ? bannerImage : '/course-banner.jpg'}
|
||||
alt=""
|
||||
class="h-44 w-full object-cover"
|
||||
/>
|
||||
<div class="overflow-hidden py-4">
|
||||
<p class="line-clamp-1 overflow-ellipsis text-lg font-semibold">
|
||||
{title}
|
||||
</p>
|
||||
</div>
|
||||
<p class=" line-clamp-3 overflow-ellipsis text-justify text-[#ABABAB]">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<PrimaryButton
|
||||
href={getCourseUrl()}
|
||||
class="w-full rounded border border-white bg-transparent text-white hover:bg-white hover:text-black"
|
||||
label="Go to Course"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { getPageSection } from '$lib/utils/helpers/page';
|
||||
import { sharedPage } from '$lib/utils/stores/pages';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
const content = $derived(getPageSection($sharedPage, SECTION.FOOTER));
|
||||
const seo = $derived(getPageSection($sharedPage, SECTION.SEO));
|
||||
</script>
|
||||
|
||||
{#if content?.show}
|
||||
<nav
|
||||
class="flex w-full flex-col items-start gap-4 bg-black px-6 py-4 text-white md:flex-row-reverse md:items-center md:justify-between"
|
||||
>
|
||||
<div class="w-fit rounded-md bg-white p-2">
|
||||
<Logo src={seo?.settings.logo} alt={seo?.settings.title} />
|
||||
</div>
|
||||
<ul
|
||||
class="-md:gap-8 flex flex-col items-start gap-4 font-semibold capitalize underline md:flex-row md:items-center"
|
||||
>
|
||||
{#if content.settings.twitter}
|
||||
<a href={content.settings.twitter} target="_blank" title="twitter">Twitter</a>
|
||||
{/if}
|
||||
{#if content.settings?.youtube}
|
||||
<a href={content.settings?.youtube} target="_blank">Youtube</a>{/if}
|
||||
|
||||
{#if content.settings.linkedin}
|
||||
<a href={content.settings.linkedin} target="_blank" title="linkedin">LinkedIn</a>
|
||||
{/if}
|
||||
{#if content.settings.facebook}
|
||||
<a href={content.settings.facebook} target="_blank" title="facebook">facebook</a>
|
||||
{/if}
|
||||
</ul>
|
||||
<a
|
||||
href="https://classroomio.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-1 text-blue-600 hover:underline dark:text-white"
|
||||
>
|
||||
<p class="text-base font-semibold">Built on ClassroomIO</p>
|
||||
</a>
|
||||
</nav>
|
||||
{/if}
|
||||
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import Close from 'carbon-icons-svelte/lib/Close.svelte';
|
||||
import Menu from 'carbon-icons-svelte/lib/Menu.svelte';
|
||||
import Logo from '$lib/components/ui/_custom/Logo.svelte';
|
||||
|
||||
import DirectionStraightRight from 'carbon-icons-svelte/lib/DirectionStraightRight.svelte';
|
||||
|
||||
import { ArrowUpRight, Demo } from 'carbon-icons-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { getPageSection } from '@/utils/helpers/page';
|
||||
import { sharedPage } from '@/utils/stores/pages';
|
||||
import PrimaryButton from './PrimaryButton.svelte';
|
||||
import { SECTION } from '@/utils/constants/page';
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
function toggleMenu() {
|
||||
open = !open;
|
||||
}
|
||||
|
||||
const seo = $derived(getPageSection($sharedPage, SECTION.SEO));
|
||||
const content = $derived(getPageSection($sharedPage, SECTION.NAVIGATION));
|
||||
</script>
|
||||
|
||||
<!-- testing this out for now -->
|
||||
<section class="fixed left-[5%] top-2 z-50 mx-auto w-[90%] lg:left-[2%] lg:w-fit">
|
||||
<div class="flex flex-row-reverse items-start justify-between">
|
||||
<div class="z-50 flex items-center p-1 lg:hidden">
|
||||
<button
|
||||
onclick={toggleMenu}
|
||||
class="flex w-fit items-center gap-2 bg-gray-900 p-2 text-sm text-white transition"
|
||||
>
|
||||
{#if open}
|
||||
<Close size={24} />
|
||||
<p class="font-semibold uppercase">close</p>
|
||||
{:else}
|
||||
<Menu size={24} />
|
||||
<p class="font-semibold uppercase">Menu</p>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<nav
|
||||
class="absolute w-full py-2 transition lg:relative {open
|
||||
? 'translate-x-0 lg:translate-x-0 '
|
||||
: '-translate-x-full lg:translate-x-0 '}"
|
||||
>
|
||||
<!-- Desktop nav -->
|
||||
<div class="relative h-80 w-52 rounded bg-neutral-900 py-2 text-sm text-white transition">
|
||||
<div class="ml-2 w-fit rounded-md bg-white p-2">
|
||||
<Logo
|
||||
src={seo?.settings.logo}
|
||||
alt={`${seo?.settings.title} logo`}
|
||||
className=" flex-col bg-white"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 space-y-2 border-b px-4 py-4">
|
||||
{#each content?.settings.navItems.slice(0, 2) as navItem}
|
||||
<a href={navItem.link} onclick={toggleMenu} class="flex items-center gap-2">
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight />
|
||||
{:else}
|
||||
<Demo />
|
||||
{/if}
|
||||
<p>{navItem.title}</p>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 space-y-2 border-b px-4 py-4">
|
||||
{#each content?.settings.navItems.slice(2, content?.settings.navItems.length) as navItem}
|
||||
<a href={navItem.link} onclick={toggleMenu} class="flex items-center gap-2">
|
||||
{#if navItem.redirect}
|
||||
<ArrowUpRight />
|
||||
{:else}
|
||||
<Demo />
|
||||
{/if}
|
||||
<p>{navItem.title}</p>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="px-2 py-6">
|
||||
<PrimaryButton
|
||||
class="bg-webflow hover:bg-webflow-secondary group flex h-fit w-full items-center gap-6 rounded px-2 py-2 text-xs text-white"
|
||||
onClick={() => {
|
||||
goto('/courses');
|
||||
toggleMenu();
|
||||
}}
|
||||
label="Learn with us"
|
||||
>
|
||||
<DirectionStraightRight
|
||||
size={16}
|
||||
class="transition-transform duration-300 group-hover:translate-x-1"
|
||||
/>
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
label?: string;
|
||||
href?: string;
|
||||
class?: string;
|
||||
children?: any;
|
||||
}
|
||||
const { onClick, children, label, href, class: className, ...restProps }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Button
|
||||
{href}
|
||||
onclick={onClick}
|
||||
class={cn('group bg-[#0737BE] hover:bg-[#0737BE] rounded-none text-white gap-6', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{label}
|
||||
{#if children}
|
||||
{@render children?.()}
|
||||
{/if}
|
||||
</Button>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import MagicCard from '$lib/components/magiccard/MagicCard.svelte';
|
||||
import MagicCard from '$lib/components/ui/magiccard/MagicCard.svelte';
|
||||
|
||||
interface Props {
|
||||
index?: number;
|
||||
@@ -8,27 +8,22 @@
|
||||
banner?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
index = 1,
|
||||
description = '',
|
||||
name = '',
|
||||
banner = ''
|
||||
}: Props = $props();
|
||||
let { index = 1, description = '', name = '', banner = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<MagicCard {index}>
|
||||
<section
|
||||
class="cursor-pointer flex flex-col justify-between relative border-[#EAEAEA] px-6 pt-6 space-y-4 bg-white dark:bg-inherit border dark:border-[#282828] rounded w-full h-full max-w-full lg:h-[280px]"
|
||||
class="cursor-pointer flex flex-col justify-between relative px-6 pt-6 space-y-4 hover:bg-[#282828] bg-inherit border border-[#282828] rounded w-full h-full max-w-full lg:h-[280px]"
|
||||
>
|
||||
<img src="/quote.svg" alt="" class="w-6 h-6 absolute top-6 left-8" />
|
||||
<div>
|
||||
<p class="my-6 text-[#282828] dark:text-[#ABABAB] line-clamp-5">
|
||||
<p class="my-6 text-[#ABABAB] line-clamp-5">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center p-4 gap-4 border-[#EAEAEA]">
|
||||
<img
|
||||
src={banner ? banner : '/classroomio-course-img-template.jpg'}
|
||||
src={banner ? banner : '/course-banner.jpg'}
|
||||
alt=""
|
||||
class="w-6 h-6 rounded-full"
|
||||
/>
|
||||
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
@@ -0,0 +1,81 @@
|
||||
<svg
|
||||
width="168"
|
||||
height="191"
|
||||
viewBox="0 0 168 191"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M146.744 71.4805H83.6106L78.5507 59.1652C77.772 57.396 76.4872 55.8967 74.858 54.8562C73.2289 53.8157 71.3283 53.2806 69.3956 53.3183H33.7102C31.7775 53.2806 29.8769 53.8157 28.2478 54.8562C26.6186 55.8967 25.3338 57.396 24.5551 59.1652L21.1108 70.3265C20.4728 72.4035 20.1438 74.563 20.1344 76.7357L19.5071 158.404C19.4897 160.913 20.4691 163.327 22.2301 165.114C23.9911 166.902 26.3897 167.917 28.8989 167.938H156.135V81.0203C156.154 78.51 155.176 76.095 153.414 74.3061C151.653 72.5172 149.254 71.5009 146.744 71.4805Z"
|
||||
fill="#032587"
|
||||
/>
|
||||
<path
|
||||
d="M146.744 71.4805H83.6108L78.5509 59.1652C77.7723 57.396 76.4874 55.8967 74.8583 54.8562C73.2291 53.8157 71.3285 53.2806 69.3958 53.3183H33.7105C32.4474 53.3178 31.1946 53.5443 30.0117 53.987H64.7561C68.8218 53.987 74.3847 56.3187 75.8287 59.834L80.8886 72.1552H142.104C147.288 72.1552 153.413 76.422 153.413 81.689V167.92H156.136V81.0203C156.155 78.51 155.176 76.095 153.415 74.3061C151.654 72.5172 149.254 71.5009 146.744 71.4805Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M146.744 71.4805H83.6106L78.5507 59.1652C77.772 57.396 76.4872 55.8967 74.858 54.8562C73.2289 53.8157 71.3283 53.2806 69.3956 53.3183H33.7102C31.7775 53.2806 29.8769 53.8157 28.2478 54.8562C26.6186 55.8967 25.3338 57.396 24.5551 59.1652L21.1108 70.3265C20.4728 72.4035 20.1438 74.563 20.1344 76.7357L19.5071 158.404C19.4897 160.913 20.4691 163.327 22.2301 165.114C23.9911 166.902 26.3897 167.917 28.8989 167.938H156.135V81.0203C156.154 78.51 155.176 76.095 153.414 74.3061C151.653 72.5172 149.254 71.5009 146.744 71.4805Z"
|
||||
stroke="#263238"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M29.6983 167.917H156.136L134.464 96.9016C133.911 95.0497 132.777 93.4248 131.23 92.2665C129.683 91.1082 127.804 90.478 125.871 90.4688H10.0092C8.58813 90.485 7.19062 90.8334 5.92823 91.4861C4.66584 92.1387 3.57356 93.0776 2.73865 94.2276C1.90375 95.3776 1.34935 96.7069 1.11974 98.1093C0.890134 99.5118 0.991673 100.948 1.41626 102.305L18.9927 159.898C19.6833 162.205 21.0962 164.229 23.0234 165.673C24.9507 167.116 27.2904 167.903 29.6983 167.917Z"
|
||||
fill="#0233BD"
|
||||
/>
|
||||
<path
|
||||
d="M7.42288 93.6171H123.291C125.223 93.6273 127.102 94.2579 128.649 95.416C130.196 96.5742 131.33 98.1986 131.884 100.05L152.597 167.917H156.148L134.476 96.9016C133.923 95.0497 132.789 93.4248 131.242 92.2665C129.694 91.1082 127.816 90.478 125.883 90.4688H10.009C8.33774 90.4722 6.70093 90.9443 5.2845 91.8314C3.86807 92.7185 2.7288 93.9851 1.99609 95.4872C3.5445 94.2736 5.45555 93.6151 7.42288 93.6171Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M29.6983 167.917H156.136L134.464 96.9016C133.911 95.0497 132.777 93.4248 131.23 92.2665C129.683 91.1082 127.804 90.478 125.871 90.4688H10.0092C8.58813 90.485 7.19062 90.8334 5.92823 91.4861C4.66584 92.1387 3.57356 93.0776 2.73865 94.2276C1.90375 95.3776 1.34935 96.7069 1.11974 98.1093C0.890134 99.5118 0.991673 100.948 1.41626 102.305L18.9927 159.898C19.6833 162.205 21.0962 164.229 23.0234 165.673C24.9507 167.116 27.2904 167.903 29.6983 167.917Z"
|
||||
stroke="#263238"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M71.3727 145.165C79.0338 145.165 85.2444 138.955 85.2444 131.294C85.2444 123.632 79.0338 117.422 71.3727 117.422C63.7115 117.422 57.501 123.632 57.501 131.294C57.501 138.955 63.7115 145.165 71.3727 145.165Z"
|
||||
fill="#263238"
|
||||
stroke="#263238"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M64.6855 137.982L78.0661 124.578"
|
||||
stroke="#F2F2F2"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M64.6855 124.578L78.0661 137.959"
|
||||
stroke="#F2F2F2"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M163.982 9.00001H126.415C125.774 9.00001 125.16 9.2544 124.707 9.70721C124.254 10.16 124 10.7742 124 11.4145V40.1286C124 40.7689 124.254 41.3831 124.707 41.8359C125.16 42.2887 125.774 42.5431 126.415 42.5431H135.735V51.7396C135.724 51.9767 135.785 52.2116 135.909 52.4139C136.033 52.6162 136.214 52.7765 136.431 52.8741C136.647 52.9718 136.888 53.0021 137.121 52.9613C137.355 52.9204 137.571 52.8103 137.742 52.6451L149.128 42.5431H163.982C164.622 42.5431 165.236 42.2887 165.689 41.8359C166.142 41.3831 166.396 40.7689 166.396 40.1286V11.4145C166.397 11.0972 166.335 10.7829 166.214 10.4896C166.093 10.1963 165.915 9.92983 165.691 9.70547C165.467 9.4811 165.2 9.30328 164.907 9.18221C164.613 9.06115 164.299 8.99923 163.982 9.00001Z"
|
||||
fill="white"
|
||||
stroke="#263238"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M144.719 29.8483C144.477 29.8525 144.239 29.7971 144.024 29.687C143.809 29.5768 143.624 29.4154 143.487 29.217C143.349 29.0186 143.263 28.7893 143.235 28.5495C143.207 28.3096 143.239 28.0667 143.328 27.8421C143.577 27.2749 143.911 26.7482 144.316 26.2798C144.706 25.8248 145.127 25.3977 145.577 25.0015C146.021 24.6109 146.441 24.2203 146.837 23.8179C147.212 23.4683 147.538 23.0702 147.808 22.6343C148.118 22.0995 148.237 21.4754 148.147 20.8638C148.057 20.2523 147.762 19.6894 147.311 19.267C146.44 18.5913 145.351 18.2586 144.251 18.3319C143.243 18.3061 142.241 18.5084 141.322 18.9237C140.861 19.1388 140.432 19.4177 140.049 19.7522C139.763 19.9972 139.404 20.1414 139.028 20.1628C138.651 20.1842 138.279 20.0815 137.966 19.8706C137.738 19.7172 137.549 19.5131 137.414 19.2743C137.278 19.0356 137.2 18.7686 137.185 18.4945C137.17 18.2203 137.219 17.9465 137.328 17.6944C137.437 17.4424 137.603 17.219 137.812 17.0418C138.359 16.6036 138.955 16.2302 139.588 15.9292C141.154 15.2226 142.859 14.8768 144.577 14.9172C145.948 14.8869 147.313 15.1137 148.601 15.586C149.677 15.9803 150.619 16.6719 151.317 17.5803C151.97 18.4556 152.31 19.5248 152.282 20.6163C152.302 21.377 152.176 22.1345 151.909 22.8473C151.673 23.4382 151.341 23.986 150.927 24.4689C150.522 24.9353 150.085 25.3723 149.619 25.7767C149.145 26.1851 148.713 26.5816 148.305 26.9603C147.912 27.3411 147.577 27.7778 147.311 28.2564C147.209 28.4443 147.127 28.6427 147.068 28.8482C146.975 29.1491 146.788 29.4125 146.535 29.5998C146.281 29.7871 145.975 29.8887 145.66 29.8897L144.719 29.8483ZM144.943 37.299C144.621 37.3054 144.3 37.247 144.001 37.1274C143.701 37.0078 143.429 36.8293 143.199 36.6026C142.97 36.3759 142.788 36.1054 142.665 35.8073C142.541 35.5091 142.479 35.1893 142.482 34.8667C142.476 34.551 142.535 34.2374 142.655 33.9453C142.775 33.6532 142.954 33.3887 143.18 33.1683C143.665 32.7241 144.298 32.4777 144.955 32.4777C145.613 32.4777 146.246 32.7241 146.731 33.1683C146.954 33.3902 147.13 33.6551 147.248 33.9471C147.366 34.239 147.424 34.5519 147.417 34.8667C147.422 35.1926 147.361 35.5161 147.237 35.8174C147.113 36.1187 146.928 36.3915 146.695 36.6192C146.462 36.8468 146.185 37.0245 145.88 37.1414C145.576 37.2583 145.251 37.3119 144.926 37.299H144.943Z"
|
||||
fill="#EC644E"
|
||||
stroke="#263238"
|
||||
stroke-width="1.47949"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M89.0291 190.495C131.502 190.495 165.933 188.569 165.933 186.193C165.933 183.817 131.502 181.891 89.0291 181.891C46.5561 181.891 12.125 183.817 12.125 186.193C12.125 188.569 46.5561 190.495 89.0291 190.495Z"
|
||||
fill="#8192C3"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 495 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,15 @@
|
||||
import type { Component } from 'svelte';
|
||||
import CoursesPage from './pages/Courses.svelte';
|
||||
import HomePage from './pages/Home.svelte';
|
||||
import Navigation from './Navigation.svelte';
|
||||
import Footer from './Footer.svelte';
|
||||
import Button from './PrimaryButton.svelte';
|
||||
import type { TemplateComponents } from '$lib/utils/types/template';
|
||||
|
||||
export const components: TemplateComponents = {
|
||||
navigation: Navigation as unknown as Component,
|
||||
footer: Footer as unknown as Component,
|
||||
button: Button as unknown as Component,
|
||||
coursespage: CoursesPage as unknown as Component,
|
||||
homepage: HomePage as unknown as Component
|
||||
};
|
||||