diff --git a/apps/website/FOLDER_STRUCTURE.md b/apps/website/FOLDER_STRUCTURE.md index 2997bd19..68ecdff5 100644 --- a/apps/website/FOLDER_STRUCTURE.md +++ b/apps/website/FOLDER_STRUCTURE.md @@ -5,6 +5,7 @@ This document describes the folder structure for the `apps/website` project, spe ## Overview The codebase follows a clear separation of concerns with three main categories: + - **Components**: Presentational UI components (dummy components) - **Features**: Business logic and functional components - **Utils**: Utility functions, constants, and shared types @@ -87,10 +88,12 @@ src/lib/ ## Naming Conventions ### Files and Folders + - **Kebab-case**: All file and folder names use kebab-case (e.g., `blog-list-item.svelte`, `feedback-form.svelte`) - **Single-file folders**: If a folder contains only one file, it should be flattened to a single file (e.g., `Footer/Footer.svelte` → `components/footer.svelte`) ### Components + - Component files use `.svelte` extension - Component names match their file names in kebab-case - Example: `blog-list-item.svelte` exports a component that can be imported as `BlogListItem` @@ -102,18 +105,21 @@ src/lib/ **Purpose**: Presentational components with minimal or no business logic. These are "dummy" components that receive props and render UI. **Characteristics**: + - No API calls or complex state management - Primarily focused on rendering and styling - May have simple local state for UI interactions (e.g., open/close modals) - Reusable across different pages/features **Examples**: + - `footer.svelte` - Site footer component - `navigation.svelte` - Site navigation bar - `hero.svelte` - Hero section component - `modal.svelte` - Generic modal wrapper **Import Pattern**: + ```svelte // ✅ CORRECT - Use named imports from the index import { Footer, Navigation, Hero } from '$lib/components'; @@ -129,17 +135,20 @@ import { Footer, Navigation, Hero } from '$lib/components'; **Purpose**: Components and modules that contain business logic, API calls, form handling, state management, or domain-specific functionality. **Characteristics**: + - Contains business logic and data handling - May include API calls, form submissions, or data transformations - Often includes stores, utilities, or helper functions specific to the feature - Organized by feature domain (e.g., `contact/`, `tools/`) **Examples**: + - `features/contact/feedback-form.svelte` - Form with submission logic - `features/tools/progress/store.ts` - State management for progress tool - `features/tools/tic-tac-toe/board.ts` - Game logic **Import Pattern**: + ```svelte import FeedbackForm from '$lib/features/contact/feedback-form.svelte'; import { htmlBody, openModal } from '$lib/features/tools/progress/store'; @@ -150,12 +159,14 @@ import { htmlBody, openModal } from '$lib/features/tools/progress/store'; **Purpose**: Shared utility functions, constants, types, and helper modules used across the application. **Characteristics**: + - Pure functions or utility modules - No component-specific logic - Reusable across different features/components - May include TypeScript types and interfaces **Examples**: + - `utils/format-date.ts` - Date formatting utility - `utils/is-form-valid.ts` - Form validation helper - `utils/submit-form.ts` - Generic form submission utility @@ -163,6 +174,7 @@ import { htmlBody, openModal } from '$lib/features/tools/progress/store'; - `utils/constants/tools.ts` - Application constants **Import Pattern**: + ```typescript import { formatDate } from '$lib/utils/format-date'; import { isFormValid } from '$lib/utils/is-form-valid'; @@ -219,7 +231,7 @@ src/lib/ @@ -307,6 +319,7 @@ Routes now import from `$lib/features/tools/*/` instead of `./components/`. ## Assets Static assets like images, SVGs, and other media files remain in their respective locations: + - `emojis/` - Emoji SVG files - `public/` - Public static assets (images, fonts, etc.) @@ -323,6 +336,7 @@ export { default as Navigation } from './navigation.svelte'; ``` This allows for clean, consistent imports: + ```svelte import { Footer, Navigation, Hero } from '$lib/components'; ``` @@ -332,6 +346,7 @@ import { Footer, Navigation, Hero } from '$lib/components'; ## Summary This structure provides: + - ✅ Clear separation between UI and business logic - ✅ Consistent naming conventions (kebab-case) - ✅ Flattened structure (no unnecessary single-file folders) @@ -341,4 +356,3 @@ This structure provides: - ✅ Scalable for future growth For questions or suggestions about this structure, please refer to the project maintainers. - diff --git a/apps/website/jsconfig.json b/apps/website/jsconfig.json index fe45e13f..f1da068b 100644 --- a/apps/website/jsconfig.json +++ b/apps/website/jsconfig.json @@ -1,17 +1,17 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true - } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in } diff --git a/apps/website/src/app.d.ts b/apps/website/src/app.d.ts index f59b884c..899c7e8f 100644 --- a/apps/website/src/app.d.ts +++ b/apps/website/src/app.d.ts @@ -1,12 +1,12 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } } export {}; diff --git a/apps/website/src/app.html b/apps/website/src/app.html index 5171f400..20a536e0 100644 --- a/apps/website/src/app.html +++ b/apps/website/src/app.html @@ -1,20 +1,19 @@ + + + + - - - - - - - - + - - - - + + + + + + + + - - - - + - + - - - - %sveltekit.head% - + Cal('ui', { + theme: 'light', + styles: { branding: { brandColor: '#1D4EE2' } }, + hideEventTypeDetails: false, + layout: 'month_view' + }); + + %sveltekit.head% + - -
%sveltekit.body%
- - - \ No newline at end of file + +
%sveltekit.body%
+ + diff --git a/apps/website/src/blog/brand-customisations.md b/apps/website/src/blog/brand-customisations.md index f0dbfcb1..0d296279 100644 --- a/apps/website/src/blog/brand-customisations.md +++ b/apps/website/src/blog/brand-customisations.md @@ -13,7 +13,7 @@ published: true ![classroomio-brand-customisations](https://assets.cdn.clsrio.com/blog/brand-customisations.png) _ClassroomIO Brand Customisations_ -We are excited to announce more customisations coming into our learning management system (LMS). In this blog post, we will like to share with you how we've made *YOUR* *STUDENT DASHBOARD* much more customisable than before. +We are excited to announce more customisations coming into our learning management system (LMS). In this blog post, we will like to share with you how we've made _YOUR_ _STUDENT DASHBOARD_ much more customisable than before. ## Demo @@ -40,7 +40,7 @@ However that is not the UI that your audience will see when they consume your co ![classroomio-lms-student-dashboard](https://brand.cdn.clsrio.com/classroomio-lms-courses.png) _The LMS aka Student dashboard - yourorg.classroomio.com_ -Typically this page can be found at *yourorg.classroomio.com*, where *yourorg* is a slug you set when you first set up your account. For example, we've built an internal bootcamp called ClassroomIO Developers, in this bootcamp we train other developers on some of the key tools we use internally. You can checkout our page here: [ciodevs.classroomio.com](https://ciodevs.classroomio.com), in this case you can see that our slug is *ciodevs* and you should be able to easily see yours when you log into the dashboard `/org/{yourorg}`. +Typically this page can be found at _yourorg.classroomio.com_, where _yourorg_ is a slug you set when you first set up your account. For example, we've built an internal bootcamp called ClassroomIO Developers, in this bootcamp we train other developers on some of the key tools we use internally. You can checkout our page here: [ciodevs.classroomio.com](https://ciodevs.classroomio.com), in this case you can see that our slug is _ciodevs_ and you should be able to easily see yours when you log into the dashboard `/org/{yourorg}`. In your student dashboard, your audience can: diff --git a/apps/website/src/blog/classroomio-at-eduai-conference-at-unilag.md b/apps/website/src/blog/classroomio-at-eduai-conference-at-unilag.md index f6efc638..c92aec58 100644 --- a/apps/website/src/blog/classroomio-at-eduai-conference-at-unilag.md +++ b/apps/website/src/blog/classroomio-at-eduai-conference-at-unilag.md @@ -25,7 +25,7 @@ A bit about EduAI Hub before I jump into my experience at the summit: ### My experience -Now to my experience - __it was nothing less than amazing__. +Now to my experience - **it was nothing less than amazing**. I could see how the eyes of some of the educators lite up when I said we can allow them build a customisable alternative to Google Classroom without writing a single line of code. I spoke about the importance of a customisable no-code elearning platform that anyone could use to deploy their own learning management system that suits their brand. I also gave an analogy using Shopify and Wordpress, how Shopify is allowing businesses create ecommerce stores in minutes or Wordpress allowing writters and businesses create robust websites in minutes - that's exactly what we want to do in the educational space starting with bootcamps. @@ -33,7 +33,7 @@ You can find my slides [here](https://docs.google.com/presentation/d/1yU9EFmYA5O This was my first actual event speaking about ClassroomIO and I was amazed by the amount of people that wanted to chat about how they could use the product for their online courses. -Apart from the talk, I met amazing people working on other edtech related products and how they also use AI to empower their users. +Apart from the talk, I met amazing people working on other edtech related products and how they also use AI to empower their users. ### Unexpected learning diff --git a/apps/website/src/blog/multi-language.md b/apps/website/src/blog/multi-language.md index 60745614..5f0f2454 100644 --- a/apps/website/src/blog/multi-language.md +++ b/apps/website/src/blog/multi-language.md @@ -48,6 +48,6 @@ Oh we even hooked up our AI content generator with it as well, you can switch to ## Next steps -All translations in our app are stored in a JSON file which we generate and keep up to date using Google translate. This means that it might not be 100% accurate and there could be some mistakes when use our dashboard in other languages. If you do find any issues with our translations, please do open an [issue on Github](https://github.com/classroomio/classroomio/issues/new/choose) with the correct translation and we'd be so happy to fix it. +All translations in our app are stored in a JSON file which we generate and keep up to date using Google translate. This means that it might not be 100% accurate and there could be some mistakes when use our dashboard in other languages. If you do find any issues with our translations, please do open an [issue on Github](https://github.com/classroomio/classroomio/issues/new/choose) with the correct translation and we'd be so happy to fix it. -If you are a native speaker of any of the 7 languages and want to contribute, please feel free to nitpick into our [translations](https://github.com/classroomio/classroomio/tree/main/apps/dashboard/src/lib/utils/translations) and open a PR with the fixes. Every contribution is valuable and will be gladly welcomed 😊 \ No newline at end of file +If you are a native speaker of any of the 7 languages and want to contribute, please feel free to nitpick into our [translations](https://github.com/classroomio/classroomio/tree/main/apps/dashboard/src/lib/utils/translations) and open a PR with the fixes. Every contribution is valuable and will be gladly welcomed 😊 diff --git a/apps/website/src/blog/self-host.md b/apps/website/src/blog/self-host.md index 7ebd3ff3..a6eb2bf2 100644 --- a/apps/website/src/blog/self-host.md +++ b/apps/website/src/blog/self-host.md @@ -17,7 +17,7 @@ Today we are happy to share that you can now easily self-host classroomio and us If you are interested in just trying out the product, it is advisable to use the [cloud version](https://app.classroomio.com). You can use almost all the features for free on the cloud version unless you need more students. Right now the main reason you'd have to upgrade is because your students are growing. If you don't want to upgrade and/or face this, then you'd be fine self hosting the whole application. -For self-hosting, we have written up a [guide that uses Railway](https://classroomio.com/docs/quickstart/self-hosting) to help deploy our stack and you get to manage the servers on Railway as you wish. We hope this is a good step in helping you educate your audience at ease. +For self-hosting, we have written up a [guide that uses Railway](https://classroomio.com/docs/quickstart/self-hosting) to help deploy our stack and you get to manage the servers on Railway as you wish. We hope this is a good step in helping you educate your audience at ease. Below is a demo showing you step by step how to self host the project diff --git a/apps/website/src/blog/students-analytics.md b/apps/website/src/blog/students-analytics.md index 31ccd24b..e51d7fdc 100644 --- a/apps/website/src/blog/students-analytics.md +++ b/apps/website/src/blog/students-analytics.md @@ -22,14 +22,14 @@ The audience page as you already know allows you to view the students in your **
+loop +muted +autoplay +preload="auto" +autoPictureInPicture +class="rounded-md" src="https://assets.cdn.clsrio.com/blog/organisation-student-analytics.mp4" controls + +> ## Course Student Analytics @@ -43,14 +43,14 @@ Further more, when you go into any course and go to the people tab, you can view
+muted +autoplay +preload="auto" +autoPictureInPicture +class="rounded-md" +src="https://assets.cdn.clsrio.com/blog/course-students-analytics.mp4" controls + +> ## What's Next? diff --git a/apps/website/src/lib/components/blog-list-item.svelte b/apps/website/src/lib/components/blog-list-item.svelte index e02c31c2..bfe8e799 100644 --- a/apps/website/src/lib/components/blog-list-item.svelte +++ b/apps/website/src/lib/components/blog-list-item.svelte @@ -2,8 +2,14 @@ import { Badge } from '@cio/ui/base/badge'; import { formatDate } from '$lib/utils/format-date'; - export let post; - export let isRecommended = false; + /** + * @typedef {Object} Props + * @property {any} post + * @property {boolean} [isRecommended] + */ + + /** @type {Props} */ + let { post, isRecommended = false } = $props(); {#if !isRecommended} diff --git a/apps/website/src/lib/components/core-feature-section.svelte b/apps/website/src/lib/components/core-feature-section.svelte index 8d097175..2c9413f0 100644 --- a/apps/website/src/lib/components/core-feature-section.svelte +++ b/apps/website/src/lib/components/core-feature-section.svelte @@ -1,13 +1,29 @@
@@ -32,15 +48,15 @@ {/if}

{tagline}

-

{title}

+

{title}

{description}

- {#if $$slots.more} + {#if more}

- + {@render more?.()}

{/if} diff --git a/apps/website/src/lib/components/custom-img.svelte b/apps/website/src/lib/components/custom-img.svelte index 0494887c..f25f6372 100644 --- a/apps/website/src/lib/components/custom-img.svelte +++ b/apps/website/src/lib/components/custom-img.svelte @@ -1,6 +1,10 @@
diff --git a/apps/website/src/lib/components/footer.svelte b/apps/website/src/lib/components/footer.svelte index eaeaa350..06671ea3 100644 --- a/apps/website/src/lib/components/footer.svelte +++ b/apps/website/src/lib/components/footer.svelte @@ -4,7 +4,7 @@ import Youtube from '@lucide/svelte/icons/youtube'; -
+
classroomio logo diff --git a/apps/website/src/lib/components/hero.svelte b/apps/website/src/lib/components/hero.svelte index 9fb5b29c..f62b9bcf 100644 --- a/apps/website/src/lib/components/hero.svelte +++ b/apps/website/src/lib/components/hero.svelte @@ -11,8 +11,8 @@ { label: 'Customers', color: '#0233BD' }, { label: 'Students', color: '#9747FF' } ]; - let animate = false; - let titleIndex = 0; + let animate = $state(false); + let titleIndex = $state(0); let timeout: NodeJS.Timeout; onMount(() => { diff --git a/apps/website/src/lib/components/index.ts b/apps/website/src/lib/components/index.ts index 560cf0ce..1b85adb6 100644 --- a/apps/website/src/lib/components/index.ts +++ b/apps/website/src/lib/components/index.ts @@ -19,4 +19,3 @@ export { default as Testimonial } from './testimonial.svelte'; export { default as Timeline } from './timeline.svelte'; export { default as ToolsHeader } from './tools-header.svelte'; export { default as UploadWidget } from './upload-widget.svelte'; - diff --git a/apps/website/src/lib/components/modal.svelte b/apps/website/src/lib/components/modal.svelte index 010e751d..d3f2904f 100644 --- a/apps/website/src/lib/components/modal.svelte +++ b/apps/website/src/lib/components/modal.svelte @@ -1,6 +1,13 @@ {#if open} @@ -10,7 +17,7 @@
- + {@render children?.()}
{/if} diff --git a/apps/website/src/lib/components/more-features.svelte b/apps/website/src/lib/components/more-features.svelte index 5d3c9651..f2eb93ad 100644 --- a/apps/website/src/lib/components/more-features.svelte +++ b/apps/website/src/lib/components/more-features.svelte @@ -4,8 +4,8 @@ import { folder, globe, team, transcription } from '$lib/emojis'; import { tools } from '$lib/utils/constants/tools'; - let selected = 0; - let interval: NodeJS.Timer; + let selected = $state(0); + let interval: NodeJS.Timer = $state(); const moreFeatures = [ { @@ -22,13 +22,13 @@ 'Extend invitations and manage your teaching institution seamlessly through ClassroomIO.', image: '/team-management.png' }, - { - icon: transcription, - title: 'Auto Video Transcription', - description: - 'AI automatically transcribes videos, making content searchable for seamless student access.', - image: '/video-transcription.png' - }, + // { + // icon: transcription, + // title: 'Auto Video Transcription', + // description: + // 'AI automatically transcribes videos, making content searchable for seamless student access.', + // image: '/video-transcription.png' + // }, { icon: folder, title: 'Downloadable Lesson PDF', @@ -52,7 +52,7 @@
-

+

There is More...

@@ -67,8 +67,8 @@ {#each moreFeatures as moreFeature, i}

-

{moreFeature.description}

+

{moreFeature.description}

{/each}
@@ -99,7 +99,7 @@ height="100%" loading="lazy" id="image" - in:fly={{ y: 100, duration: 1000 }} + in:fly={{ x: 100, duration: 800 }} class="w-full flex flex-col items-center" src={moreFeatures[selected].image} alt={moreFeatures[selected].title} diff --git a/apps/website/src/lib/components/navigation.svelte b/apps/website/src/lib/components/navigation.svelte index 54874d93..7697508d 100644 --- a/apps/website/src/lib/components/navigation.svelte +++ b/apps/website/src/lib/components/navigation.svelte @@ -14,13 +14,28 @@ import type { HTMLAttributes } from 'svelte/elements'; import * as NavigationMenu from '@cio/ui/base/navigation-menu'; import LibraryBigIcon from '@lucide/svelte/icons/library-big'; + import { cn } from '@cio/ui/tools'; - export let stars: number; + let { stars }: { stars: number } = $props(); + + type ListItemProps = HTMLAttributes & { + title: string; + href: string; + subtitle: string; + key: string; + }; let showDrawer = false; let showSolutions = false; - let activeLink = ''; + let activeLink = $derived(page.url.pathname); + let activeHash = $derived(page.url.hash); + + $effect(() => { + console.log('activeLink', activeLink); + console.log('activeHash', activeHash); + console.log('isSolutionsActive', isSolutionsActive); + }); function handleShowDrawer() { showDrawer = !showDrawer; @@ -76,16 +91,7 @@ } ]; - type ListItemProps = HTMLAttributes & { - title: string; - href: string; - subtitle: string; - key: string; - }; - - $: activeLink = page.url.pathname; - $: activeHash = page.url.hash; - $: isSolutionsActive = solutions.some((s) => activeHash.includes(s.key)); + let isSolutionsActive = $derived(!!solutions.some((s) => activeLink.includes(s.key))); {#snippet list_item({ title, subtitle, href, key, class: className, ...restProps }: ListItemProps)} @@ -140,11 +146,14 @@
- @@ -262,7 +269,7 @@ >
classroomio logo -
@@ -270,9 +277,11 @@