feat: certificates, landing pages, ai tutor, website update, compliance

This commit is contained in:
Rotimi Best
2026-05-18 03:47:22 +01:00
parent c31ad63955
commit ac3a2c14a7
644 changed files with 56050 additions and 5439 deletions
@@ -0,0 +1,141 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { Certificate } from '@cio/ui';
import {
CERTIFICATE_TEMPLATE_LIST,
LONG_RENDER_DATA,
MINIMAL_RENDER_DATA,
SAMPLE_RENDER_DATA,
STORY_ACCENT_VARIATIONS,
buildDesign
} from './fixtures';
const { Story } = defineMeta({
title: 'Templates/Certificate',
component: Certificate.Preview,
parameters: {
layout: 'fullscreen'
},
tags: ['autodocs']
});
</script>
<Story name="Gallery · all templates">
{#snippet template()}
<div class="grid grid-cols-1 gap-6 bg-zinc-100 p-6 md:grid-cols-2 xl:grid-cols-3">
{#each CERTIFICATE_TEMPLATE_LIST as template (template.id)}
<figure class="overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm">
<div class="aspect-[1.4/1] w-full">
<Certificate.Preview design={buildDesign(template.id)} data={SAMPLE_RENDER_DATA} />
</div>
<figcaption class="border-t border-zinc-200 px-4 py-3">
<h3 class="text-sm font-medium text-zinc-900">{template.label}</h3>
<p class="mt-0.5 text-xs text-zinc-500">{template.description}</p>
</figcaption>
</figure>
{/each}
</div>
{/snippet}
</Story>
<Story name="Classique · full-size">
{#snippet template()}
<div class="flex h-screen items-center justify-center bg-zinc-100 p-8">
<div class="aspect-[1.4/1] w-full max-w-5xl">
<Certificate.Preview design={buildDesign('classique')} data={SAMPLE_RENDER_DATA} showControls />
</div>
</div>
{/snippet}
</Story>
<Story name="Brutalist · full-size">
{#snippet template()}
<div class="flex h-screen items-center justify-center bg-zinc-100 p-8">
<div class="aspect-[1.4/1] w-full max-w-5xl">
<Certificate.Preview
design={buildDesign('brutalist', { accentColor: '#ff4500' })}
data={SAMPLE_RENDER_DATA}
showControls
/>
</div>
</div>
{/snippet}
</Story>
<Story name="Noir · full-size">
{#snippet template()}
<div class="flex h-screen items-center justify-center bg-zinc-950 p-8">
<div class="aspect-[1.4/1] w-full max-w-5xl">
<Certificate.Preview
design={buildDesign('noir', { accentColor: '#d4af37' })}
data={SAMPLE_RENDER_DATA}
showControls
/>
</div>
</div>
{/snippet}
</Story>
<Story name="Poster · full-size">
{#snippet template()}
<div class="flex h-screen items-center justify-center bg-zinc-100 p-8">
<div class="aspect-[1.4/1] w-full max-w-5xl">
<Certificate.Preview
design={buildDesign('poster', { accentColor: '#ff5722' })}
data={SAMPLE_RENDER_DATA}
showControls
/>
</div>
</div>
{/snippet}
</Story>
<Story name="Minimal · full-size">
{#snippet template()}
<div class="flex h-screen items-center justify-center bg-zinc-100 p-8">
<div class="aspect-[1.4/1] w-full max-w-5xl">
<Certificate.Preview design={buildDesign('minimal')} data={SAMPLE_RENDER_DATA} showControls />
</div>
</div>
{/snippet}
</Story>
<Story name="Classique · accent variations">
{#snippet template()}
<div class="grid grid-cols-1 gap-5 bg-zinc-100 p-6 md:grid-cols-2 xl:grid-cols-3">
{#each STORY_ACCENT_VARIATIONS as accent (accent)}
<figure class="overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm">
<div class="aspect-[1.4/1] w-full">
<Certificate.Preview design={buildDesign('classique', { accentColor: accent })} data={SAMPLE_RENDER_DATA} />
</div>
<figcaption class="border-t border-zinc-200 px-4 py-2 text-xs text-zinc-600">
<code>{accent}</code>
</figcaption>
</figure>
{/each}
</div>
{/snippet}
</Story>
<Story name="Edge cases · long names + short data">
{#snippet template()}
<div class="grid grid-cols-1 gap-5 bg-zinc-100 p-6 xl:grid-cols-2">
<figure class="overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm">
<div class="aspect-[1.4/1] w-full">
<Certificate.Preview design={buildDesign('poster')} data={LONG_RENDER_DATA} />
</div>
<figcaption class="border-t border-zinc-200 px-4 py-3 text-xs text-zinc-600">
Long names · poster template
</figcaption>
</figure>
<figure class="overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm">
<div class="aspect-[1.4/1] w-full">
<Certificate.Preview design={buildDesign('minimal')} data={MINIMAL_RENDER_DATA} />
</div>
<figcaption class="border-t border-zinc-200 px-4 py-3 text-xs text-zinc-600">
Minimal copy · minimal template
</figcaption>
</figure>
</div>
{/snippet}
</Story>
@@ -0,0 +1,58 @@
import {
ACCENT_COLORS,
CERTIFICATE_TEMPLATES,
DEFAULT_CERTIFICATE_DESIGN,
type CertificateDesign,
type CertificateRenderData,
type CertificateTemplateId
} from '@cio/certificates';
export const CERTIFICATE_TEMPLATE_LIST = CERTIFICATE_TEMPLATES;
export const SAMPLE_RENDER_DATA: CertificateRenderData = {
recipientName: 'Eleanor Vance',
courseName: 'Certificate of Excellence',
courseDescription:
'In recognition of outstanding achievement and exemplary dedication to the craft of design and storytelling.',
orgName: 'The Royal Academy of Arts',
date: 'October 12, 2026',
certificateId: 'N° 0247'
};
export const LONG_RENDER_DATA: CertificateRenderData = {
recipientName: 'Maximilian Alexandros Constantinopoulos',
courseName: 'Advanced Editorial Layout & Typography for the Digital Era',
courseDescription:
'Awarded after sustained mastery of advanced editorial layout, typography, color theory, motion craft, and the careful coordination of a multi-stage production.',
orgName: 'The International Atelier of Design & Storytelling',
date: 'February 03, 2026',
certificateId: 'N° 9991'
};
export const MINIMAL_RENDER_DATA: CertificateRenderData = {
recipientName: 'Jo',
courseName: 'Intro',
courseDescription: 'Successfully completed the introductory programme.',
orgName: 'CIO',
date: 'May 11, 2026',
certificateId: 'N° 0001'
};
export function buildDesign(
templateId: CertificateTemplateId,
overrides: Partial<CertificateDesign> = {}
): CertificateDesign {
return {
templateId,
accentColor: overrides.accentColor ?? DEFAULT_CERTIFICATE_DESIGN.accentColor,
subtitle: overrides.subtitle ?? DEFAULT_CERTIFICATE_DESIGN.subtitle,
descriptionOverride: overrides.descriptionOverride,
signatories: overrides.signatories ?? [
{ name: 'Margaret W. Holloway', role: 'Director of Studies' },
{ name: 'James K. Reeves', role: 'Dean of Faculty' }
],
idFormat: overrides.idFormat ?? DEFAULT_CERTIFICATE_DESIGN.idFormat
};
}
export const STORY_ACCENT_VARIATIONS = ACCENT_COLORS;
@@ -118,16 +118,71 @@ export const mockProps: OrgLandingPageProps = {
href: '#contact'
}
},
links: {
heading: 'Resources',
description: 'Everything you need beyond the catalog.',
boldVisitLabel: 'Visit',
classicLearnMoreLabel: 'Learn more',
cards: [
{
icon: 'help-circle',
title: 'Help Center',
description: 'Guides, FAQs, and how-to articles for admins and learners.',
href: 'https://example.com/help'
},
{
icon: 'video',
title: 'Webinars',
description: 'Live sessions and recordings on compliance and product updates.',
href: 'https://example.com/webinars'
},
{
icon: 'users',
title: 'Community',
description: 'Connect with peers, share templates, and ask questions.',
href: 'https://example.com/community'
}
]
},
callout: {
heading: 'Ready to simplify compliance?',
description:
'Launch your first certification program in minutes. Automate tracking, reduce risk, and keep your team certified.',
action: { label: 'Start Free', href: '#courses' }
},
footerLinks: [
{ label: 'Terms of Service', href: '#terms' },
{ label: 'Privacy Policy', href: '#privacy' },
{ label: 'Support', href: '#support' }
],
footerText: '© 2026 Certifi Academy. All rights reserved.'
footer: {
brand: {
socials: [
{ platform: 'linkedin', href: 'https://linkedin.com/company/example' },
{ platform: 'x', href: 'https://x.com/example' }
]
},
columns: [
{
id: 'col-company',
heading: 'Company',
links: [
{ id: 'col-company-about', label: 'About', href: '#about' },
{ id: 'col-company-careers', label: 'Careers', href: '#careers' }
],
cta: { label: 'Explore more', href: '#more' }
},
{
id: 'col-resources',
heading: 'Resources',
links: [
{ id: 'col-resources-help', label: 'Help Center', href: '#help' },
{ id: 'col-resources-blog', label: 'Blog', href: '#blog' }
]
}
],
bottom: {
text: '© 2026 Certifi Academy. All rights reserved.',
links: [
{ id: 'bottom-terms', label: 'Terms of Service', href: '#terms' },
{ id: 'bottom-privacy', label: 'Privacy Policy', href: '#privacy' },
{ id: 'bottom-support', label: 'Support', href: '#support' }
]
}
}
};
@@ -1,6 +1,15 @@
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import { MinimalLandingPage, BoldLandingPage, ClassicLandingPage } from '@cio/ui/custom/org-landing-page';
import {
MinimalLandingPage,
BoldLandingPage,
ClassicLandingPage,
SaasLandingPage,
TechLandingPage,
StudioLandingPage,
CorporateLandingPage,
TerminalLandingPage
} from '@cio/ui/custom/org-landing-page';
import { mockProps } from './fixtures';
const { Story } = defineMeta({
@@ -23,3 +32,23 @@
<Story name="Classic">
<ClassicLandingPage {...mockProps} />
</Story>
<Story name="SaaS">
<SaasLandingPage {...mockProps} />
</Story>
<Story name="Tech">
<TechLandingPage {...mockProps} />
</Story>
<Story name="Studio">
<StudioLandingPage {...mockProps} />
</Story>
<Story name="Corporate">
<CorporateLandingPage {...mockProps} />
</Story>
<Story name="Terminal">
<TerminalLandingPage {...mockProps} />
</Story>