Skip to content

[FRONTEND] Build Add Module Modal UX with Module Type Selection and Search #263

Description

@smattymatty

Claiming This Task

Before you start working, check the Assignees section. If no one is assigned, leave a comment claiming the issue and assign it to yourself. This prevents duplicate work.

See the Community Wiki for contributing guidelines and git workflow.

Problem

The Course Detail Page's "Add Module" modal currently shows a "coming soon" placeholder. We need a proper UX that lets teachers select a module type and then search for / pick content to attach to their course.

The old modal asked for raw content_type and object_id fields — completely unusable. The new flow should feel intuitive: pick a type, search for content, select it, done.

Requirements

Step 1: Module Type Selection

  1. Type picker screen — when the modal opens, show a grid/list of available module types:
    • Slideshow (icon + label) — the first and currently only supported type
    • Future types (Quiz, Notes, Assignment, etc.) can be shown as disabled/greyed "coming soon" cards
  2. Clicking a type moves to the search screen for that type

Step 2: Search & Select Content (Slideshow)

  1. Search input — debounced text input (300ms) that queries GET /api/slideshows/search/?q=<query>&mine=true (searches user's own slideshows)
  2. Toggle: "My Slideshows" / "All Slideshows" — lets teachers switch between searching their own slideshows and all public slideshows
  3. Results list — show title, description (truncated), visibility badge, published status for each slideshow
  4. Selecting a slideshow — clicking a result selects it, showing its title in a confirmation view
  5. Module title — optional text input for the module title (defaults to the slideshow title)

Step 3: Confirm & Add

  1. Add button — calls the existing addModule(courseId, data) API from coursesApi.ts with the correct content_type and object_id
  2. Back button — go back to type selection or search
  3. Success — close modal and refresh the modules list

General

  1. Dark mode — full dark mode support matching the existing glass-morphism styling
  2. Responsive — works on mobile with touch-friendly list items
  3. Error handling — show errors inline (module already exists, slideshow not found, etc.)
  4. Empty state — if no slideshows found, show a helpful message ("No slideshows found. Create one first!")

Architecture Context

  • The modal lives at src/components/courses/AddModuleModal.tsx and is rendered from ModulesTab.tsx
  • It uses createPortal(... , document.body) to escape the backdrop-blur container
  • The existing coursesApi.ts already has addModule() for creating modules
  • The content_type value for slideshows needs to be resolved — either hardcode the Django ContentType ID or add a lookup. The backend accepts content_type as the ContentType model ID. Consider adding a GET /api/courses/module-types/ endpoint (separate backend issue) or looking it up from content_type app_label/model.
  • Consider building the search/select as a step-based flow within the modal (type → search → confirm)

API Dependencies

Needs to be built first (separate backend issue):

  • GET /api/slideshows/search/?q=<query> — text search across title/description, with visibility filtering and pagination. Supports mine=true filter.

Already built:

  • GET /api/slideshows/?mine=true — can be used as a fallback for listing user's slideshows (no text search, but has filtering)
  • POST /api/courses/<id>/modules/ — creates a module (already wired in coursesApi.ts)

ContentType resolution:
The addModule call needs a content_type ID (Django's ContentType PK for the Slideshow model). Options:

  1. Backend provides a GET /api/courses/module-types/ endpoint returning supported types with their content_type IDs (cleanest, but needs backend work)
  2. Hardcode the content_type ID (fragile across environments)
  3. Use GET /api/content-types/?app_label=slideshows&model=slideshow if such an endpoint exists (it doesn't currently)

Recommendation: Option 1 is cleanest long-term but adds backend scope. For an MVP, the backend could accept app_label + model strings instead of a raw content_type ID in the module creation endpoint — that's a small backend change worth considering.

Files to be Altered

  • src/components/courses/AddModuleModal.tsx — replace placeholder with multi-step type selection + search + confirm flow
  • src/services/slideshowApi.ts — add function for slideshow search endpoint (or extend existing)
  • src/types/slideshows.types.ts — TypeScript types for slideshow search results (if not existing)
  • src/i18n/locales/en.json — add module modal translations (type labels, search placeholder, empty state, etc.)
  • src/i18n/locales/ar.json — Arabic translations

Testing Requirements

  • Unit tests for AddModuleModal (type selection → search → select → confirm flow)
  • Test the debounced search behavior (verify API not called on every keystroke)
  • Test "My Slideshows" vs "All Slideshows" toggle
  • Test empty state when no results found
  • Test error handling (API failure, module already attached)
  • Manual: verify dark mode and mobile responsiveness
  • Manual: verify the full flow end-to-end with a real backend

Additional Context (Optional)

  • Depends on: Backend slideshow search API issue (must be built first for text search, but the existing list endpoint with ?mine=true filtering can be used for an initial version)
  • The CourseModule model uses Django's GenericForeignKey, so it can link to any model — the type selection UI is designed to scale as new content types are added
  • The slideshow list endpoint already returns a lightweight serializer (SlideshowListSerializer) with title, description, visibility, subject, language, country, is_published — perfect for search results
  • Consider caching the content_type ID resolution to avoid repeated lookups

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions