feat(dashboard): improve student course sidebar navigation (#814)

* docs(prototypes): add course sidebar navigation HTML prototypes

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

* docs(prototypes): add downloadable zip for course sidebar mockups

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

* feat(dashboard): improve student course sidebar navigation

Collapse all sections except the active one for students, show section
completion as counts (e.g. 3/4), and replace the footer progress card with
prev/next controls plus a thin progress bar.

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

* fix(dashboard): address PR review and remove sidebar prototypes

Remove HTML prototype files from the PR. Match content IDs as exact path
segments for active-item resolution, and use content-neutral prev/next labels.

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

* fix(dashboard): keep ClassroomIO brand untranslated in powered-by footer

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

* fix(dashboard): restore progress summary in course sidebar footer

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

* fix(dashboard): move progress bar below summary in sidebar footer

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

* feat(course): add mobile student bottom nav with outline sheet

Implement prototype 3 mobile navigation for course learners: a fixed
bottom bar with progress ring, prev/next controls, and a sheet showing
the progress card plus collapsible course outline with scroll-to-active.

Extends public-course UI primitives (ring bottom nav, collapsible
sidebar sections, mobile sheet progress slot) and wires them into the
authenticated course layout for isCourseLearnerView on lesson/exercise
routes. Hides duplicate header prev/next and progress popover on mobile.

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

* fix(course): address review feedback and exclude prototypes

- Add .coderabbit.yaml and .greptile/config.json to skip prototypes/**
- Document prototypes/ as out-of-scope in AGENTS.md and Greptile rules
- Render locked mobile outline rows without href navigation targets
- Replace nested IconButton in collapsible section trigger with chevron icon
- Reset public-course sidebar section collapse on every mobile sheet reopen

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

---------
This commit is contained in:
Rotimi Best
2026-08-19 11:52:58 +01:00
committed by GitHub
co-authored by Rotimi Best
parent bf3a7e7057
commit 55ae6dd4fe
38 changed files with 1157 additions and 193 deletions
@@ -93,6 +93,27 @@ export const SIDEBAR_FIXTURE: PublicCourseSidebarSection[] = [
}
];
export const LONG_SIDEBAR_FIXTURE: PublicCourseSidebarSection[] = Array.from({ length: 10 }, (_, sectionIndex) => {
const sectionNumber = sectionIndex + 1;
return {
id: `section-${sectionNumber}`,
title: `Module ${sectionNumber}`,
items: Array.from({ length: 4 }, (_, itemIndex) => {
const itemNumber = itemIndex + 1;
const slug = `module-${sectionNumber}-lesson-${itemNumber}`;
return {
kind: 'lesson' as const,
id: `lesson-${sectionNumber}-${itemNumber}`,
slug,
title: `Lesson ${itemNumber}: Topic ${sectionNumber}.${itemNumber}`,
isUnlocked: true
};
})
};
});
export const LESSON_FIXTURE: PublicLessonViewData = {
kind: 'lesson',
title: 'Hallucination & limitations',
@@ -9,6 +9,7 @@
EXERCISE_FIXTURE,
LESSON_FIXTURE,
LESSON_LOCKED_FIXTURE,
LONG_SIDEBAR_FIXTURE,
ORG_FIXTURE,
SIDEBAR_FIXTURE
} from './fixtures';
@@ -105,6 +106,40 @@
{/snippet}
</Story>
<Story name="Bottom nav · ring variant">
{#snippet template()}
<div class="ui:relative ui:min-h-[200px] ui:bg-background">
<PublicCourse.PublicCourseBottomNav
positionLabel="3 / 12"
sublineLabel="Hallucination & limitations"
hasPrev
hasNext
centerVariant="ring"
progressPercent={42}
onPrev={() => console.log('prev')}
onNext={() => console.log('next')}
onOpenSheet={() => console.log('open sheet')}
/>
</div>
{/snippet}
</Story>
<Story name="Mobile sheet · collapse + long course">
{#snippet template()}
{@const activeSlug = 'module-10-lesson-4'}
<div class="ui:mx-auto ui:max-w-sm ui:border ui:border-border">
<PublicCourse.PublicCourseMobileSheet
open
sections={LONG_SIDEBAR_FIXTURE}
{activeSlug}
collapseToSectionId="section-10"
title="Course outline"
onItemClick={(item: PublicCourseSidebarItem) => console.log('navigate', item.slug)}
/>
</div>
{/snippet}
</Story>
<Story name="Callout · inline + full variants">
{#snippet template()}
<div class="ui:mx-auto ui:max-w-2xl ui:space-y-8 ui:p-8">