# Page and Route Creation Guide This guide explains how to create pages and routes in the dashboard application, following the established patterns and architecture. ## Table of Contents 1. [Route Structure Overview](#route-structure-overview) 2. [Creating Basic Pages](#creating-basic-pages) 3. [Creating Settings Pages](#creating-settings-pages) 4. [Creating Feature Pages](#creating-feature-pages) 5. [Layout Patterns](#layout-patterns) 6. [Form Patterns (Field.Group)](#form-patterns-fieldgroup) 7. [Navigation Setup](#navigation-setup) 8. [Breadcrumb Setup](#breadcrumb-setup) --- ## Route Structure Overview The dashboard has two main route contexts: ### 1. Organization Routes (`/org/[slug]/...`) - Uses `OrgSidebar` and `AppHeader` - Path pattern: `/org/[slug]/[feature]` - Example: `/org/acme/courses`, `/org/acme/settings` ### 2. LMS Routes (`/lms/...`) - Uses `LMSSidebar` and `LmsHeader` - Path pattern: `/lms/[feature]` - Example: `/lms/community`, `/lms/settings` --- ## Creating Basic Pages ### Step 1: Create the Route File **Location**: `apps/dashboard/src/routes/[context]/[feature]/+page.svelte` **Pattern**: ```svelte Feature Name - ClassroomIO {$t('feature.title')} {#snippet child()} {/snippet} ``` ### Step 2: Create the Feature Page Component **Location**: `apps/dashboard/src/lib/features/[feature]/pages/[feature].svelte` **Pattern**: ```svelte
{#if isLoading} {:else if !data.length} {:else} {#each data as item} {/each} {/if}
``` ### Step 3: Export the Page **Location**: `apps/dashboard/src/lib/features/[feature]/pages/index.ts` ```typescript export { default as FeaturePage } from './feature.svelte'; ``` --- ## Creating Settings Pages Settings pages follow a specific pattern with layouts and subroutes. ### Step 1: Create Settings Layout **Location**: `apps/dashboard/src/routes/[context]/settings/+layout.svelte` ```svelte {@render children?.()} ``` ### Step 2: Create Default Settings Page **Location**: `apps/dashboard/src/routes/[context]/settings/+page.svelte` ```svelte Settings - ClassroomIO {$t('settings.heading')} {#snippet child()} {/snippet} ``` ### Step 3: Create Settings Subroute **Location**: `apps/dashboard/src/routes/[context]/settings/[subroute]/+page.svelte` ```svelte Subroute Name - ClassroomIO {$t('settings.subroute.heading')} {#snippet child()} {/snippet} ``` **Note**: Settings pages use subroutes instead of tabs. Each subroute has its own `+page.svelte` file. --- ## Creating Feature Pages ### Feature Page Structure Feature pages contain all business logic and should be placed in `lib/features/[feature]/pages/`. **Key Principles**: - ✅ All business logic goes in the feature page - ✅ Use `Page.BodyHeader` for filters, search, and view toggles - ✅ Use `Empty` component for empty states - ✅ Use `Search` component for search inputs - ✅ Handle loading states with skeleton/loader components - ❌ Don't use `Page.Header` or `Page.Action` in feature pages (these are in route files) ### Example Feature Page ```svelte All
{#if isLoading} {:else if !discussions.length} {:else} {#each discussions as discussion} {/each} {/if}
``` --- ## Layout Patterns ### Organization Layout **Location**: `apps/dashboard/src/routes/org/[slug]/+layout.svelte` ```svelte
{@render children?.()}
``` ### LMS Layout **Location**: `apps/dashboard/src/routes/lms/+layout.svelte` ```svelte
{@render children?.()}
``` **Key Differences**: - Org routes use `AppHeader` (includes AppSetup) - LMS routes use `LmsHeader` (no AppSetup) - Org routes use `OrgSidebar` - LMS routes use `LMSSidebar` --- ## Form Patterns (Field.Group) **IMPORTANT**: Always use `Field.Group` pattern for forms. The old `Row/Grid/Column` pattern is deprecated and removed. ### Field.Group Pattern **Location**: `apps/dashboard/src/lib/features/[feature]/pages/[feature].svelte` ```svelte {$t('form.section_title')} {$t('form.name')} (hasUnsavedChanges = true)} /> {$t('form.email')} (hasUnsavedChanges = true)} /> (hasUnsavedChanges = true)} /> {$t('form.enabled')} {$t('form.another_section')} (hasUnsavedChanges = true)} /> ``` ### Field Components - `` - Main container (use `class="max-w-md! w-full px-2"`) - `` - Section container - `` - Section title - `` - Individual field wrapper - `` - Field label - `` - Field description/help text - `` - Field error message - `` - Visual separator between sections - `orientation="horizontal"` - For switches/checkboxes with labels side-by-side ### Pattern Rules - ✅ DO: Use `Field.Group` as the main container - ✅ DO: Use `Field.Set` for logical sections - ✅ DO: Use `Field.Separator` between sections - ✅ DO: Use `Field.Legend` for section titles - ✅ DO: Wrap form inputs in `Field.Field` - ✅ DO: Use `Field.Label` for input labels - ✅ DO: Use `orientation="horizontal"` for switches/checkboxes - ❌ DON'T: Use `Row`, `Grid`, or `Column` components (deprecated) - ❌ DON'T: Mix Field.Group with old layout components --- ## Navigation Setup ### Adding Items to Organization Navigation **Location**: `apps/dashboard/src/lib/features/ui/navigation/org-navigation.ts` ```typescript const baseNavConfig: NavItemConfig[] = [ // ... existing items { titleKey: 'org_navigation.new_feature', path: '/new-feature', icon: NewFeatureIcon, matchPattern: '^/org/[^/]+/new-feature(/.*)?$', // Optional: requires admin access requiresAdmin: true, // Optional: nested routes nestedRoutes: [ { path: 'subroute', titleKey: 'Subroute Name' } ], // Optional: supports dynamic segments supportsDynamicSegment: true } ]; ``` ### Adding Items to LMS Navigation **Location**: `apps/dashboard/src/lib/features/ui/navigation/lms-navigation.ts` ```typescript const baseNavConfig: NavItemConfig[] = [ // ... existing items { titleKey: 'lms_navigation.new_feature', path: '/new-feature', icon: NewFeatureIcon, matchPattern: '^/lms/new-feature(/.*)?$', // Optional: conditional display show: (currentOrg) => currentOrg?.customization?.feature?.enabled === true, // Optional: nested routes nestedRoutes: [ { path: 'subroute', titleKey: 'Subroute Name' } ], // Optional: supports dynamic segments supportsDynamicSegment: true } ]; ``` ### Navigation Item Properties - `titleKey` - Translation key for the navigation label - `path` - Route path (relative to context) - `icon` - Lucide icon component - `matchPattern` - Regex pattern for active state matching - `requiresAdmin` - Only show for admin users (org navigation only) - `show` - Function to conditionally show/hide item - `nestedRoutes` - Static nested routes (like `community/ask`) - `supportsDynamicSegment` - Supports dynamic routes (like `community/[slug]`) - `useHashUrl` - Use '#' for collapsible parent (settings menu) - `items` - Nested items for collapsible menus (settings sub-items) --- ## Breadcrumb Setup Breadcrumbs are automatically generated from navigation configuration. ### For Organization Routes Breadcrumbs use `generateBreadcrumbs()` from `breadcrumb.ts` which reads from `org-navigation.ts`. ### For LMS Routes Breadcrumbs use `generateLmsBreadcrumbs()` from `lms-breadcrumb.ts` which reads from `lms-navigation.ts`. ### Adding Dynamic Breadcrumbs For dynamic routes (like `community/[slug]`), add breadcrumb data in the route's `+page.ts`: ```typescript // routes/org/[slug]/community/[slug]/+page.ts export async function load({ params }) { // Fetch question data const question = await fetchQuestion(params.slug); return { breadcrumb: question.title // This will appear in breadcrumbs }; } ``` --- ## Complete Examples ### Example 1: Simple Feature Page **Route**: `routes/org/[slug]/courses/+page.svelte` ```svelte Courses - ClassroomIO {$t('courses.title')} {#snippet child()} {/snippet} ``` ### Example 2: Settings Page with Save Action **Route**: `routes/org/[slug]/settings/customize-lms/+page.svelte` ```svelte Customize LMS - ClassroomIO {$t('components.settings.customize_lms.title')} {#snippet child()} {/snippet} ``` ### Example 3: Form Page Component **Feature Page**: `lib/features/settings/pages/profile.svelte` ```svelte {$t('settings.profile.profile_picture.heading')} (hasUnsavedChanges = true)} /> {$t('settings.profile.personal_information.heading')} {$t('settings.profile.personal_information.full_name')} (hasUnsavedChanges = true)} /> {#if profileApi.errors.fullname} {$t(profileApi.errors.fullname)} {/if} ``` --- ## Checklist for New Pages When creating a new page, follow this checklist: ### Route Setup - [ ] Create route file at `routes/[context]/[feature]/+page.svelte` - [ ] Add `Page.Root`, `Page.Header`, `Page.Body` structure - [ ] Add `` with page title - [ ] Import and render feature page component ### Feature Page - [ ] Create feature page at `lib/features/[feature]/pages/[feature].svelte` - [ ] Export from `lib/features/[feature]/pages/index.ts` - [ ] Use `Page.BodyHeader` for filters/search - [ ] Use `Empty` component for empty states - [ ] Handle loading states ### Forms (if applicable) - [ ] Use `Field.Group` pattern (NOT Row/Grid/Column) - [ ] Use `Field.Set` for sections - [ ] Use `Field.Separator` between sections - [ ] Use `Field.Legend` for section titles - [ ] Wrap inputs in `Field.Field` - [ ] Add `UnsavedChanges` component if needed ### Navigation (if needed) - [ ] Add item to `org-navigation.ts` or `lms-navigation.ts` - [ ] Set appropriate `matchPattern` for active state - [ ] Add icon component - [ ] Configure nested routes if needed - [ ] Configure dynamic segments if needed ### Settings Pages (if applicable) - [ ] Create `+layout.svelte` with `Page.Root` wrapper - [ ] Create default `+page.svelte` for main settings - [ ] Create subroute `+page.svelte` files for each subroute - [ ] Export page components from `$features/settings/pages` --- ## Common Patterns ### Page Width Classes - `class="w-full"` - Full width - `class="w-full md:max-w-3xl lg:mx-auto"` - Centered, max width on larger screens (settings pages) - `class="w-full md:max-w-4xl lg:mx-auto"` - Slightly wider (customize-lms) - `class="mx-auto w-full max-w-6xl"` - Wide content area (main layouts) ### Page Actions Actions in `Page.Action` typically include: - Save/Update buttons - Create/Add buttons - Export buttons - Feature-specific action buttons ### Loading States Use skeleton loaders or spinner components: ```svelte {#if isLoading} {:else} {/if} ``` ### Empty States Always use the `Empty` component: ```svelte ``` --- ## Best Practices 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 established components (`Page.*`, `Field.*`, `Empty`, `Search`) 5. **State Management**: Keep state local to feature pages or use feature-specific stores 6. **Translation Keys**: Always use translation keys from `$t()` function 7. **Type Safety**: Use TypeScript types for props and state 8. **Accessibility**: Use semantic HTML and proper ARIA labels --- ## Migration from Old Patterns If you encounter old patterns, migrate them: ### Old: Row/Grid/Column Pattern ```svelte Title ``` ### New: Field.Group Pattern ```svelte Title Label ``` --- ## Additional Resources - See `ROUTE_PAGE_SETUP.md` for more details on feature page structure - See existing pages in `routes/org/[slug]/` and `routes/lms/` for examples - See `lib/features/settings/pages/` for form examples - See `lib/features/community/pages/` for list page examples