Skip to content

Conversation

@hermannakos
Copy link
Collaborator

Summary

  • Add new My Courses widget to Student app dashboard showing favorite courses and groups
  • Implement CourseCard and GroupCard composables matching Figma design specifications
  • Support grade display (percentage/letter), announcement counts, and synced indicators
  • Add collapsible sections for courses and groups with persistent expand/collapse state
  • Support responsive grid layout for tablet devices (1-3 columns based on screen width)
  • Include color overlay toggle support based on user preferences

Test plan

  1. Launch Student app and navigate to Dashboard
  2. Verify the "Favorite Courses and Groups" widget appears
  3. Verify courses display with correct name, color, grade (if enabled), and announcement count
  4. Verify groups display with parent course name and member count
  5. Tap "Courses" or "Groups" header to toggle section collapse/expand
  6. Tap "All Courses" button to navigate to course list
  7. Tap a course card to navigate to course details
  8. Tap a group card to navigate to group details
  9. Test on tablet to verify multi-column grid layout
  10. Toggle Settings > Course color overlay and verify course cards update

refs: MBL-19461
affects: Student
release note: Added "My Courses" widget to the dashboard showing your favorite courses and groups with quick access to grades and announcements

🤖 Generated with Claude Code

hermannakos and others added 8 commits December 1, 2025 13:27
Implement the data and domain layers for the new My Courses widget:

- Add domain models for course and group cards with grade display states
- Extend CourseRepository with favorite courses and dashboard cards
- Create GroupRepository for fetching user groups
- Add use cases for loading courses and groups
- Implement CoursesWidgetViewModel with state management
- Add DataStore for persisting section expanded state
- Create app-specific behaviors for grade visibility (Student only)
- Set up dependency injection modules for all apps

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Implements the Compose UI components for the My Courses dashboard widget:
- Generic shimmer loading component in pandautils for reusable loading states
- CourseCard with horizontal 72x72 layout, kebab menu with dropdown, grade badge
- GroupCard with member count badge and parent course name display
- CollapsibleSection for expandable courses/groups sections with animations
- CoursesWidget main composable with loading, courses, and groups sections
- Integrated widget into dashboard with WidgetMetadata and default widgets
- Updated behavior interface with menu action methods (manage offline content, customize course)

All components follow Material3 design system and match Figma specifications.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Implements color overlay preference for course cards, matching existing dashboard behavior:
- Created ObserveColorOverlayUseCase following same pattern as grade visibility
- Added observeColorOverlay() to CoursesWidgetBehavior interface
- Updated CourseCard to conditionally show overlay (0.4 alpha) or full image (1.0 alpha)
- Course color background always visible when no image present
- ViewModel observes preference changes and updates UI state reactively
- Integrated with existing StudentPrefs.hideCourseColorOverlay setting

The overlay switch in the navigation drawer now controls both legacy cards and the new widget.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Remove course code display, show only course name for cleaner layout
- Update NotAvailable grade badge to use noGradeText string resource
- Match grade badge font styling (14sp SemiBold) across all grade types including N/A

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add CoursesWidgetRouter interface and app-specific implementations
- Update use cases to return raw Course/Group objects
- Move card item mapping to ViewModel for navigation access
- Add NonLazyGrid composable for grid layout in non-lazy context
- Pass Course/Group objects to router for proper navigation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Fix CourseCard ripple effect to respect rounded corners
- Add preview composables for DashboardScreen states (loading, error, empty)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add LoadFavoriteCoursesUseCaseTest
- Add LoadGroupsUseCaseTest
- Add CoursesWidgetViewModelTest
- Extend CourseRepositoryTest with getFavoriteCourses and getDashboardCards tests
- Add GroupRepositoryTest
- Add StudentCoursesWidgetBehaviorTest
- Add TeacherCoursesWidgetBehaviorTest
- Add ObserveGradeVisibilityUseCaseTest
- Add ObserveColorOverlayUseCaseTest
- Fix TeacherCoursesWidgetModule DI scope (FragmentComponent -> ViewModelComponent)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Add comprehensive UI tests for CoursesWidgetContent composable covering:
- Loading shimmer state
- Empty state (no courses or groups)
- Single and multiple courses display
- Groups display
- Courses and groups together
- Grade display (percentage, letter, hidden)
- Collapsible section toggle callbacks
- Section collapse/expand interactions
- Synced indicator and announcement count
- Multiple columns on tablet

Also add getFragmentActivityOrNull extension for test-safe activity access
and test tags to CourseCard and GroupCard composables.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR adds a new Courses Widget to the dashboard, implementing the feature for both Student and Teacher apps with appropriate app-specific behavior. The implementation follows the project's established patterns well, including proper use of the Router pattern, dependency injection with Hilt, and comprehensive test coverage.

✅ Positive Aspects

  • Excellent test coverage: Comprehensive unit tests for use cases, behaviors, and UI components
  • Good architectural separation: Proper use of the Router pattern with app-specific implementations
  • Clean Compose UI: Well-structured composables with proper state management and animations
  • Consistent with project patterns: Follows MVVM architecture, uses Hilt DI, and matches existing code style
  • Responsive design: Properly handles both phone and tablet layouts with configurable columns
  • Accessibility: Good use of content descriptions and semantic elements

🔍 Issues Found

Below are specific issues that should be addressed:

  • Hardcoded SharedPreferences constants (ObserveColorOverlayUseCase.kt:51, ObserveGradeVisibilityUseCase.kt:51): Extract PREFS_NAME and preference keys to avoid duplication and potential sync issues
  • Inconsistent error handling (StudentCoursesWidgetRouter.kt:38 vs TeacherCoursesWidgetBehavior.kt:48): Student router uses TODO comments while Teacher throws NotImplementedError - should be consistent
  • Potential UI flash (DashboardFragment.kt:54): Status bar styling moved to onViewCreated may cause brief visual flash
  • Unused parameter (CourseCard.kt:177): onMenuClick callback is defined but never used in the implementation
  • Background color changes (DashboardScreen.kt:114, 129): Changed from backgroundLightest to backgroundLight - verify this is intentional and doesn't affect other UI elements

🎯 Additional Recommendations

  1. TODO Comments: Consider creating follow-up GitHub issues for unimplemented routing features (Manage Offline Content, Customize Course) to ensure they're tracked
  2. Constants Management: Consider creating a shared constants object for preference keys used across multiple files
  3. Error Handling: The use cases using SharedPreferences don't handle potential exceptions - consider adding error handling
  4. Memory Leaks: The SharedPreferences listeners in use cases are properly cleaned up with awaitClose - good job!

🔒 Security & Performance

  • ✅ No security vulnerabilities identified
  • ✅ No SQL injection or XSS risks
  • ✅ Proper use of Flow for reactive data
  • ✅ Efficient SharedPreferences listener management

📝 Code Quality

Overall code quality is very good. The implementation is clean, well-tested, and follows Kotlin best practices. Minor improvements suggested in inline comments would enhance maintainability.

Recommendation: Address the inline comments, particularly around constants duplication and error handling consistency, then this should be good to merge.

@github-actions
Copy link

github-actions bot commented Dec 4, 2025

Parent Install Page

@github-actions
Copy link

github-actions bot commented Dec 4, 2025

🧪 Unit Test Results

✅ 📱 Parent App

  • Tests: 310 total, 0 failed, 0 skipped
  • Duration: 35.083s
  • Success Rate: 100%

✅ 📱 Student App

  • Tests: 1241 total, 0 failed, 0 skipped
  • Duration: 0.000s
  • Success Rate: 100%

✅ 🌅 Horizon

  • Tests: 449 total, 0 failed, 0 skipped
  • Duration: 29.309s
  • Success Rate: 100%

✅ 📦 Submodules

  • Tests: 2476 total, 0 failed, 0 skipped
  • Duration: 54.502s
  • Success Rate: 100%

📊 Summary

  • Total Tests: 4476
  • Failed: 0
  • Skipped: 0
  • Status: ✅ All tests passed!

Last updated: Thu, 11 Dec 2025 13:51:12 GMT

@github-actions
Copy link

github-actions bot commented Dec 4, 2025

Student Install Page

Add courses widget verification to all test cases, fixing test failures
after the My Courses widget was added to the default widgets list.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@github-actions
Copy link

github-actions bot commented Dec 4, 2025

📊 Code Coverage Report

✅ Student

  • PR Coverage: 42.91%
  • Master Coverage: 42.76%
  • Delta: +0.15%

✅ Teacher

  • PR Coverage: 25.48%
  • Master Coverage: 25.45%
  • Delta: +0.03%

⚠️ Pandautils

  • PR Coverage: 22.51%
  • Master Coverage: 22.56%
  • Delta: -0.05%

📈 Overall Average

  • PR Coverage: 30.30%
  • Master Coverage: 30.25%
  • Delta: +0.04%

hermannakos and others added 6 commits December 4, 2025 13:34
Add a stub provider for CoursesWidgetBehavior in HorizonTestModule to
fix the Hilt dependency graph for Horizon test builds.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Remove assertion for "Favorite Courses and Groups" title that was removed from UI
- Remove assertion for "All Courses" button in loading state (button only appears after loading)
- All 18 tests now pass on Pixel 2 API 29 emulator

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
… widget

- Switch to getCourses() API endpoint instead of getFavoriteCourses() to ensure enrollment data with grades is properly loaded
- Use Course.getCourseGrade() method to properly calculate grades with grading period support
- Filter courses by dashboard card presence to exclude concluded/hidden courses
- Add getCourses() method to CourseRepository with depagination support
- Update LoadFavoriteCoursesUseCase to filter by both isFavorite and dashboard card presence
- Update all unit tests to use getCourses() and Student enrollment types

The getFavoriteCourses() endpoint was not reliably including enrollment data,
causing grades to appear as locked. The getCourses() endpoint (same as legacy
dashboard) includes full enrollment data needed for grade calculations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
- Add parentCourseColor property to GroupCardItem
- Group card background and placeholder use group's own color
- Parent course name text displays in parent course's color
- Use CanvasContext.color extension for theme-aware colors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Copy link
Contributor

@tamaskozmer tamaskozmer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Dashboard not updating when favorites change.
  • We only use light course/group colors regardless of the theme.
  • Course and group item background color is different in dark mode.
  • The colors are incorrect on the course color/image. Text colors in dark mode should always be dark on colored backgrounds. Also the background of the icons and numbers of colored text should be dark. See the design.
  • All courses button should be at the top right according to the design. We would also need a "Favorite Courses and Groups" title next to it.
  • We should reconsider how we scale the UI when the font size is changed. Especially the All Courses button. (see screenshot)
Screenshot_20251210_171239

Copy link
Contributor

@tamaskozmer tamaskozmer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA + 1

@hermannakos hermannakos merged commit b43da3b into master Dec 11, 2025
45 of 50 checks passed
@hermannakos hermannakos deleted the MBL-19461-dashboard-courses branch December 11, 2025 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants