feat: preview templates, thumbnail features, 2 new org templates
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { resolve } from '$app/paths';
|
||||
import { onMount } from 'svelte';
|
||||
import type { Component } from 'svelte';
|
||||
import { SvelteSet, SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
@@ -10,20 +12,18 @@
|
||||
|
||||
import { PoweredBy } from '$features/ui';
|
||||
import LibraryBigIcon from '@lucide/svelte/icons/library-big';
|
||||
import { CourseCardList } from '$features/course/components';
|
||||
import { normalizeLandingPageSettings } from '$features/org/utils/landing-page';
|
||||
|
||||
import {
|
||||
BoldLandingHero,
|
||||
BoldLandingNav,
|
||||
ClassicLandingHero,
|
||||
ClassicLandingNav,
|
||||
MinimalLandingHero,
|
||||
MinimalLandingNav,
|
||||
OrgLandingPageFooter
|
||||
} from '@cio/ui/custom/org-landing-page';
|
||||
importThemeCourseCard,
|
||||
importThemeNavHero,
|
||||
normalizeLandingPageSettings,
|
||||
themeCourseGridClass,
|
||||
themeHeaderShellClass,
|
||||
themeStyle,
|
||||
themeRendersNavInsideHero
|
||||
} from '$features/org/utils/landing-page';
|
||||
|
||||
import { LandingButton, OrgLandingPageFooter } from '@cio/ui/custom/org-landing-page';
|
||||
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { Checkbox } from '@cio/ui/base/checkbox';
|
||||
import { Empty } from '@cio/ui/custom/empty';
|
||||
import { Separator } from '@cio/ui/base/separator';
|
||||
@@ -46,11 +46,33 @@
|
||||
}
|
||||
);
|
||||
|
||||
const shellClass = $derived(
|
||||
landingSettings.theme === 'classic'
|
||||
? 'ui:min-h-screen ui:bg-muted/10 ui:text-foreground ui:font-sans'
|
||||
: 'ui:min-h-screen ui:bg-background ui:text-foreground ui:font-sans'
|
||||
);
|
||||
const shellClass = $derived(`ui:min-h-screen ${themeHeaderShellClass(landingSettings.theme)}`);
|
||||
const shellStyle = $derived(themeStyle(landingSettings.theme));
|
||||
const navInsideHero = $derived(themeRendersNavInsideHero(landingSettings.theme));
|
||||
const courseGridClass = $derived(themeCourseGridClass(landingSettings.theme));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let NavComponent = $state<Component<any> | null>(null);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let HeroComponent = $state<Component<any> | null>(null);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let CourseCardComponent = $state<Component<any> | null>(null);
|
||||
|
||||
onMount(async () => {
|
||||
const [navHero, cardComponent] = await Promise.all([
|
||||
importThemeNavHero(landingSettings.theme),
|
||||
importThemeCourseCard(landingSettings.theme)
|
||||
]);
|
||||
NavComponent = navHero.NavComponent;
|
||||
HeroComponent = navHero.HeroComponent;
|
||||
CourseCardComponent = cardComponent;
|
||||
});
|
||||
|
||||
const heroProps = $derived({
|
||||
...landingSettings.hero,
|
||||
heading: t.get('public_courses.heading'),
|
||||
image: ''
|
||||
});
|
||||
|
||||
async function applyTagFilters(nextTags: string[]) {
|
||||
const params = new SvelteURLSearchParams(page.url.searchParams);
|
||||
@@ -119,54 +141,50 @@
|
||||
|
||||
<PoweredBy />
|
||||
|
||||
<main class={shellClass}>
|
||||
{#if landingSettings.theme === 'minimal'}
|
||||
<MinimalLandingHero hero={{ ...landingSettings.hero, heading: $t('public_courses.heading') }}>
|
||||
{#snippet navigation()}
|
||||
<MinimalLandingNav
|
||||
orgName={data.org.name}
|
||||
logoUrl={data.org.avatarUrl ?? undefined}
|
||||
navItems={landingSettings.navItems}
|
||||
{authAction}
|
||||
/>
|
||||
{/snippet}
|
||||
</MinimalLandingHero>
|
||||
{:else if landingSettings.theme === 'bold'}
|
||||
<BoldLandingNav
|
||||
orgName={data.org.name}
|
||||
logoUrl={data.org.avatarUrl ?? undefined}
|
||||
navItems={landingSettings.navItems}
|
||||
{authAction}
|
||||
/>
|
||||
<BoldLandingHero hero={{ ...landingSettings.hero, heading: $t('public_courses.heading') }} />
|
||||
{:else}
|
||||
<ClassicLandingNav
|
||||
orgName={data.org.name}
|
||||
logoUrl={data.org.avatarUrl ?? undefined}
|
||||
navItems={landingSettings.navItems}
|
||||
{authAction}
|
||||
/>
|
||||
<ClassicLandingHero hero={{ ...landingSettings.hero, heading: $t('public_courses.heading') }} />
|
||||
<main class={shellClass} style={shellStyle}>
|
||||
{#if NavComponent && HeroComponent}
|
||||
{#if navInsideHero}
|
||||
<HeroComponent hero={heroProps} orgName={data.org.name}>
|
||||
{#snippet navigation()}
|
||||
<NavComponent
|
||||
orgName={data.org.name}
|
||||
logoUrl={data.org.avatarUrl ?? undefined}
|
||||
navItems={landingSettings.navItems}
|
||||
{authAction}
|
||||
/>
|
||||
{/snippet}
|
||||
</HeroComponent>
|
||||
{:else}
|
||||
<NavComponent
|
||||
orgName={data.org.name}
|
||||
logoUrl={data.org.avatarUrl ?? undefined}
|
||||
navItems={landingSettings.navItems}
|
||||
{authAction}
|
||||
/>
|
||||
<HeroComponent hero={heroProps} orgName={data.org.name} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<section class="mx-auto w-full max-w-7xl px-4 py-8 md:px-6">
|
||||
<div class="mb-6 flex items-center justify-end gap-4">
|
||||
<Button variant="outline" onclick={clearFilters} disabled={selectedTags.length === 0}>
|
||||
<LandingButton variant="secondary" onclick={clearFilters} disabled={selectedTags.length === 0}>
|
||||
{$t('public_courses.clear_filters')}
|
||||
</Button>
|
||||
</LandingButton>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-8 lg:grid-cols-[280px_minmax(0,1fr)]">
|
||||
<aside class="ui:bg-background space-y-4 rounded-lg border p-4">
|
||||
<aside
|
||||
class="ui:bg-[var(--landing-card)] ui:text-[var(--landing-fg)] ui:border-[var(--landing-border)] space-y-4 rounded-lg border p-4"
|
||||
>
|
||||
<div>
|
||||
<h2 class="text-base font-semibold">{$t('public_courses.filters.title')}</h2>
|
||||
<p class="ui:text-muted-foreground text-xs">{$t('public_courses.filters.help')}</p>
|
||||
<p class="ui:text-[var(--landing-fg-muted)] text-xs">{$t('public_courses.filters.help')}</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
<Separator class="ui:bg-[var(--landing-border)]" />
|
||||
|
||||
{#if displayTagGroups.length === 0}
|
||||
<p class="ui:text-muted-foreground text-sm">{$t('public_courses.filters.empty')}</p>
|
||||
<p class="ui:text-[var(--landing-fg-muted)] text-sm">{$t('public_courses.filters.empty')}</p>
|
||||
{:else}
|
||||
<div class="space-y-5">
|
||||
{#each displayTagGroups as group (group.id)}
|
||||
@@ -174,14 +192,14 @@
|
||||
<div class="space-y-1">
|
||||
<h3 class="text-sm font-semibold">{group.name}</h3>
|
||||
{#if group.description}
|
||||
<p class="ui:text-muted-foreground text-xs">{group.description}</p>
|
||||
<p class="ui:text-[var(--landing-fg-muted)] text-xs">{group.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
{#each group.tags as tag (tag.id)}
|
||||
<label
|
||||
class="hover:ui:bg-muted/30 flex cursor-pointer items-center justify-between rounded-md border px-3 py-2"
|
||||
class="ui:border-[var(--landing-border)] ui:hover:bg-[var(--landing-card-soft)] flex cursor-pointer items-center justify-between rounded-md border px-3 py-2"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
@@ -195,7 +213,7 @@
|
||||
></span>
|
||||
<span class="text-sm">{tag.name}</span>
|
||||
</div>
|
||||
<span class="ui:text-muted-foreground text-xs">{tag.courseCount}</span>
|
||||
<span class="ui:text-[var(--landing-fg-muted)] text-xs">{tag.courseCount}</span>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -213,8 +231,12 @@
|
||||
description={$t('public_courses.empty.description')}
|
||||
variant="page"
|
||||
/>
|
||||
{:else}
|
||||
<CourseCardList courses={data.courses} isOnLandingPage={true} />
|
||||
{:else if CourseCardComponent}
|
||||
<div class={courseGridClass}>
|
||||
{#each data.courses as course, index (course.id)}
|
||||
<CourseCardComponent {course} {index} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user