Files
classroomio/apps/dashboard/ai/ROUTE_PAGE_SETUP.md
T
Rotimi BestandAbdulmuizz Hamzat 9fb6c7cbaa feat: migrate course UI from supabase (#615)
* chore: fix storybook build

* chore: implement theming

* community list not showing properly

* refactor: migrate Chip component to @cio/ui/custom/chip

- Updated imports for the Chip component across multiple files to use the new location in the @cio/ui package.
- Replaced the previous Chip implementation with a new version that utilizes a Badge for better styling and functionality.
- Adjusted related components to ensure consistent usage of the updated Chip component.

* fix: align course sidebar design to match organization design (#611)

* fix: align course sidebar design to match organization design

* feat: add hideSearch prop to AppHeader and update sidebar layout

---------

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

* refactor: replace PrimaryButton with Button component across multiple files

- Updated various components to use the new Button component from @cio/ui/base/button instead of the deprecated PrimaryButton.
- Ensured consistent button usage throughout the application for improved styling and functionality.
- Removed the PrimaryButton component and its associated constants from the codebase.

* refactor: standardize button and input usage across components

- Replaced instances of deprecated components such as TextField, TextArea, and IconButton with their updated counterparts (InputField, TextareaField, and updated IconButton).
- Ensured consistent event handling by changing `onClick` to `onclick` for better alignment with Svelte's event binding conventions.
- Removed unused components and streamlined imports to enhance code maintainability and readability.

* chore: themeing, course card, use $features

* refactor: centralize UI components and update modal implementations

- Refactored various components to centralize UI elements, improving discoverability and maintainability.
- Replaced deprecated modal components with the new Dialog component for consistent modal behavior across the application.
- Removed unused components and streamlined imports to enhance code clarity and reduce complexity.

* feat: add new documentation and refactor org and lms UI components

- Introduced new documentation files for Auth Flow, Breadcrumbs Refactoring, Page and Route Creation, and Route Page Setup to enhance developer onboarding and reference.
- Refactored various UI components, including the Course Sidebar and Lesson Modal, to improve layout and functionality.
- Removed deprecated components and streamlined imports for better maintainability.
- Updated translations across multiple languages to ensure consistency in user experience.

* refactor: update UI components and improve styling consistency

- Refactored various components to enhance styling consistency, including updates to button and text color classes.
- Removed deprecated components and streamlined imports for better maintainability.
- Replaced instances of PageBody with a more flexible Page component structure across multiple routes.
- Updated translations and UI elements to align with the new design system.
- Deleted unused components to reduce code complexity and improve clarity.

* update output.css

---------

Co-authored-by: Abdulmuizz Hamzat <[email protected]>
2025-12-22 02:36:48 +01:00

8.6 KiB

Route Page Setup Documentation

This document outlines the pattern for setting up page routes in the dashboard application. Follow this structure to maintain consistency across the codebase.

Overview

The application follows a feature-based architecture where:

  • Route files (src/routes/org/[slug]/[feature]/+page.svelte) are minimal wrappers that set up the Page structure
  • Feature pages (src/lib/features/[feature]/pages/[feature].svelte) contain all business logic, state management, and UI components
  • Components are organized in src/lib/features/[feature]/components/ and exported via index.ts
  • Pages are exported from src/lib/features/[feature]/pages/index.ts

Directory Structure

apps/dashboard/src/
├── routes/
│   └── org/
│       └── [slug]/
│           └── [feature]/
│               └── +page.svelte          # Minimal route wrapper
└── lib/
    └── features/
        └── [feature]/
            ├── components/
            │   ├── [component].svelte
            │   └── index.ts               # Export all components
            ├── pages/
            │   ├── [feature].svelte      # Main feature page with all logic
            │   └── index.ts               # Export page component
            ├── utils/                     # Feature-specific utilities
            └── api/                       # Feature-specific API calls

Route File Pattern (+page.svelte)

The route file should be minimal and only contain:

  1. Page structure using @cio/ui/base/page components
  2. Import and render the feature page component
  3. Page metadata (title, etc.)

Example Route File

<script>
  import { FeaturePage } from '$features/[feature]/pages';
  import { FeatureActionButton } from '$features/[feature]/components';
  import { t } from '$lib/utils/functions/translations';
  import * as Page from '@cio/ui/base/page';
</script>

<svelte:head>
  <title>Feature Name - ClassroomIO</title>
</svelte:head>

<Page.Root class="w-full">
  <Page.Header>
    <Page.HeaderContent>
      <Page.Title>{$t('feature.title')}</Page.Title>
    </Page.HeaderContent>
    <Page.Action>
      <FeatureActionButton />
    </Page.Action>
  </Page.Header>
  <Page.Body>
    {#snippet child()}
      <FeaturePage />
    {/snippet}
  </Page.Body>
</Page.Root>

Route File Rules

  • ✅ DO: Keep it minimal - only Page structure
  • ✅ DO: Import feature page from $features/[feature]/pages
  • ✅ DO: Import action buttons/components from $features/[feature]/components
  • ✅ DO: Use Page.Root, Page.Header, Page.HeaderContent, Page.Title, Page.Action, Page.Body
  • ❌ DON'T: Put business logic in route files
  • ❌ DON'T: Put state management in route files
  • ❌ DON'T: Put data fetching in route files
  • ❌ DON'T: Put filters/search in route files (use Page.BodyHeader in feature page)

Feature Page Pattern (pages/[feature].svelte)

The feature page contains all the business logic, state management, and UI components.

Feature Page Structure

<script lang="ts">
  // 1. UI Component Imports
  import { Empty } from '@cio/ui/custom/empty';
  import { Search } from '@cio/ui/custom/search';
  import * as Page from '@cio/ui/base/page';
  import * as Select from '@cio/ui/base/select';
  import { SomeIcon } from '@lucide/svelte/icons/...';

  // 2. Feature Component Imports
  import { FeatureCard, FeatureLoader, FeatureModal } from '../components';

  // 3. Utility Imports
  import { t } from '$lib/utils/functions/translations';
  import { someStore } from '../utils/store';
  import { fetchFeatureData } from '../api';

  // 4. State Management
  let isLoading = $state(false);
  let searchValue = $state('');
  let selectedId = $state('');
</script>

<FeatureModal />

<Page.BodyHeader align="right">
  <Search placeholder="Search..." bind:value={searchValue} />
  <Select.Root bind:value={selectedId}>
    <!-- Select options -->
  </Select.Root>
</Page.BodyHeader>

<!-- Main Content with States -->
<div class="w-full">
  {#if isLoading}
    <!-- Loading State -->
    <FeatureLoader />
    <FeatureLoader />
    <FeatureLoader />
  {:else if !data.length}
    <!-- Empty State -->
    <Empty
      title={$t('feature.empty_title')}
      description={$t('feature.empty_description')}
      icon={SomeIcon}
      variant="page"
    >
      <FeatureActionButton />
    </Empty>
  {:else}
    <!-- Content -->
    {#each data as item}
      <FeatureCard {item} />
    {/each}
  {/if}
</div>

Feature Page Rules

  • ✅ DO: Use Page.BodyHeader for filters, search, and view toggles
  • ✅ DO: Use Empty component from @cio/ui/custom/empty for empty states
  • ✅ DO: Use Search component from @cio/ui/custom/search for search inputs
  • ✅ DO: Handle loading states with skeleton/loader components
  • ✅ DO: Export the page component from pages/index.ts as [Feature]Page
  • ✅ DO: Keep all business logic in the feature page
  • ❌ DON'T: Use Page.Header or Page.Action in feature pages (these are in route files)
  • ❌ DON'T: Put route-specific logic in feature pages

Component Organization

Components Directory

Create reusable components specific to the feature:

components/
├── feature-card.svelte
├── feature-loader.svelte
├── feature-modal.svelte
└── index.ts

Components Index (components/index.ts)

export { default as FeatureCard } from './feature-card.svelte';
export { default as FeatureLoader } from './feature-loader.svelte';
export { default as FeatureModal } from './feature-modal.svelte';
export { default as FeatureActionButton } from './feature-action-button.svelte';

Pages Index (pages/index.ts)

export { default as FeaturePage } from './feature.svelte';

Complete Example

Route File: routes/org/[slug]/community/+page.svelte

<script>
  import { CommunityPage } from '$features/community/pages';
  import { AskCommunityButton } from '$features/community/components';
  import { t } from '$lib/utils/functions/translations';
  import * as Page from '@cio/ui/base/page';
</script>

<svelte:head>
  <title>Community - ClassroomIO</title>
</svelte:head>

<Page.Root class="w-full">
  <Page.Header>
    <Page.HeaderContent>
      <Page.Title>{$t('community.title')}</Page.Title>
    </Page.HeaderContent>
    <Page.Action>
      <AskCommunityButton />
    </Page.Action>
  </Page.Header>
  <Page.Body>
    {#snippet child()}
      <CommunityPage />
    {/snippet}
  </Page.Body>
</Page.Root>

Feature Page: lib/features/community/pages/community.svelte

<script lang="ts">
  import { Search } from '@cio/ui/custom/search';
  import { Empty } from '@cio/ui/custom/empty';
  import * as Page from '@cio/ui/base/page';
  import * as Select from '@cio/ui/base/select';
  import MessageSquareMoreIcon from '@lucide/svelte/icons/message-square-more';
  import { CommunityListLoader, AskCommunityButton } from '../components';
  // ... other imports

  let isLoading = $state(false);
  let searchValue = $state('');
  let discussions = $state([]);

  // ... logic
</script>

<Page.BodyHeader>
  <Search placeholder="Search..." bind:value={searchValue} />
  <Select.Root>
    <!-- Select options -->
  </Select.Root>
</Page.BodyHeader>

{#if isLoading}
  <CommunityListLoader />
{:else}
  {#each discussions as discussion}
    <!-- Discussion content -->
  {:else}
    <Empty
      title={$t('community.no_question')}
      description={$t('community.ask_a_question')}
      icon={MessageSquareMoreIcon}
    >
      <AskCommunityButton />
    </Empty>
  {/each}
{/if}

Key Patterns

  1. Separation of Concerns: Route files handle routing structure, feature pages handle business logic
  2. Reusability: Feature pages can be imported and used in different contexts
  3. Consistency: All pages follow the same structure for maintainability
  4. Design System: Use Page.BodyHeader for filters, Empty for empty states, Search for search inputs
  5. State Management: Keep state local to feature pages or use feature-specific stores

Migration Checklist

When refactoring an existing route:

  • Create feature directory structure (lib/features/[feature]/)
  • Move business logic from route file to feature page
  • Extract components to components/ directory
  • Create components/index.ts with exports
  • Create pages/index.ts with page export
  • Update route file to be minimal wrapper
  • Add Page.BodyHeader for filters/search if needed
  • Replace custom empty states with Empty component
  • Replace custom search inputs with Search component
  • Test all functionality works correctly