Skip to content

rhyanvargas/8p3p-lms-nextjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

8P3P Learning Management System (LMS)

8P3P LMS

A production-ready LMS platform for EMDR therapist training

Next.js TypeScript AWS Amplify TailwindCSS

πŸš€ Features

πŸ” Complete Authentication System

  • Multi-layer Security: Server-side middleware + client-side protection
  • Social Login: Google OAuth integration
  • Session Management: Automatic timeout handling and session recovery
  • MFA Support: Optional SMS-based multi-factor authentication
  • Smart Redirects: Preserves intended destination after login

πŸ“š Enhanced Learning Management

  • Course Catalog: Browse and enroll in EMDR training courses
  • Smart Course Navigation: Auto-expanding sidebar with active chapter detection
  • Simple Progress Tracking: Chapter-based completion tracking with visual indicators
  • Interactive Dashboard: Personalized learning dashboard with widgets
  • Enhanced Quiz System:
    • Pass/fail feedback with conditional navigation
    • "Next Chapter" button on quiz success
    • "Retry Quiz" + "Ask Question" on failure
    • Question interaction tracking per chapter
  • AI-Powered Help: Tavus conversational video AI for asking questions about chapter content
    • Real-time video conversations with AI instructor
    • Chapter-specific context injection
    • 4-minute sessions with countdown timer
    • Camera/microphone controls
  • Community Features: Social learning with posts and interactions
  • SEO-Friendly URLs: Human-readable slugs with reliable ID references
  • Hierarchical Structure: Course β†’ Chapters β†’ Sections with auto-collapse navigation

⏱️ MVP Time Management System

  • Simple Time Estimation: Basic formula for course completion times
    • totalTime = videoTime + quizTime + learningCheckTime
    • Default 8-minute chapters with configurable quiz and learning check times
  • Progress Calculation: Two simple approaches
    • Time-based: (timeSpent / totalTime) * 100
    • Chapter-based: (completedChapters / totalChapters) * 100
  • Reusable Timer Component:
    • Countdown timers for quizzes and learning activities
    • Multiple variants (compact, default, large)
    • Built with shadcn countdown hook for reliability

🎨 Modern UI/UX

  • Responsive Design: Mobile-first approach with adaptive layouts
  • Consistent Progress Indicators: Reusable ChapterProgress component
  • Smart Sidebar: Only active section expanded, others auto-collapsed
  • Modal System: Accessible dialogs with proper ARIA support
  • Dark/Light Themes: CSS-first theming with next-themes integration
  • Accessibility: WCAG compliant components and navigation
  • Loading States: Smooth transitions and professional loading indicators
  • Interactive Video Player: Next-video with Mux streaming + synchronized WebVTT transcripts
    • Auto-highlighting transcript segments during playback
    • Click-to-seek: Jump to any part by clicking transcript text
    • Callback ref pattern for seamless video-transcript sync
    • VTT parser with precise timestamp handling
  • Breadcrumb Navigation: Clear hierarchical navigation paths
  • Timer Components: Comprehensive timer system for quizzes, AI interactions, and progress tracking
  • Content Estimation System: AI-powered time estimation for reading and video content with personalized learning recommendations

πŸ› οΈ Tech Stack

Category Technology
Framework Next.js 15 (App Router)
Language TypeScript 5.9
Authentication AWS Amplify Gen2 + Cognito
AI Conversations Tavus CVI (@tavus/cvi-ui)
Video Streaming next-video + Mux
Transcripts WebVTT with custom parser
Forms React Hook Form + Zod
State Management React Context + useState
Code Quality ESLint + TypeScript strict mode
Deployment AWS Amplify Hosting

πŸ“– Content Structure

The 8P3P LMS follows a hierarchical content structure designed for optimal learning flow:

Course β†’ Chapters β†’ Sections

Course {
  id: "1"
  title: "EMDR Therapy Fundamentals"
  chapters: [
    Chapter {
      id: "chapter_1"
      title: "EMDR Foundations"
      sections: [
        Section { id: "section_1_1", title: "Introduction", sectionType: "video" }
        Section { id: "section_1_2", title: "How EMDR Works", sectionType: "video" }
        Section { id: "section_1_3", title: "Trauma and the Body", sectionType: "video" }
        Section { id: "section_1_4", title: "Closing", sectionType: "video" }
        Section { id: "section_1_5", title: "Learning Check", sectionType: "ai_avatar" }
        Section { id: "section_1_6", title: "Knowledge Assessment", sectionType: "quiz" }
      ]
    }
  ]
}

Section Types

  • πŸ“Ή Video: Video content with transcript and progress tracking
  • πŸ€– AI Avatar: Interactive conversation with AI instructor (Tavus integration)
  • πŸ“ Quiz: Assessment with multiple choice questions and retry logic
  • πŸ“š Learning Check: Interactive review and reinforcement activities

Dynamic Ordering

Sections can be reordered dynamically without changing IDs:

  • Each section has an order field for flexible sequencing
  • Content can be reorganized based on learning objectives
  • Maintains consistent URLs and progress tracking

πŸš€ Getting Started

Prerequisites

  • Node.js 18+ and npm
  • AWS Account (for authentication)
  • Git
  • Radix UI components (@radix-ui/react-slider, @radix-ui/react-radio-group)

1. Clone & Install

git clone https://github.com/your-org/8p3p-lms-nextjs.git
cd 8p3p-lms-nextjs
npm install

2. Environment Setup

AWS Amplify (Authentication)

# Start Amplify sandbox (handles auth backend)
npx ampx sandbox

Tavus AI (Ask a Question Feature)

# Copy environment template
cp .env.example .env.local

# Edit .env.local and add your Tavus credentials:
# TAVUS_API_KEY=your_tavus_api_key_here
# TAVUS_REPLICA_ID=your_replica_id_here
# TAVUS_PERSONA_ID=your_persona_id_here  # Optional
# TAVUS_DEFAULT_CALL_DURATION=240  # Optional (default: 4 minutes)

Get Tavus Credentials:

  1. Sign up at https://www.tavus.io/
  2. Create a replica (AI avatar) in the Tavus dashboard
  3. Copy API Key and Replica ID to .env.local
  4. See Tavus Documentation for details

Start Development Server

# In another terminal, start development server
npm run dev

# Optional: Run linting and type checking
npm run lint
npm run type-check

3. Open Application

Visit http://localhost:3000

πŸ” Authentication Flow

Architecture Overview

graph TD
    A[User Request] --> B[Server Middleware]
    B --> C{Authenticated?}
    C -->|No| D[Redirect to /login]
    C -->|Yes| E[Allow Access]
    D --> F[Amplify Authenticator]
    F --> G[Google OAuth / Email]
    G --> H[Success]
    H --> I[Redirect to Intended Page]
Loading

Security Layers

πŸ›‘οΈ Layer 1: Server-Side Protection

  • Middleware: middleware.ts validates sessions before page load
  • API Routes: Protected endpoints with server-side auth checks
  • Cannot be bypassed: Works even with JavaScript disabled

🎯 Layer 2: Client-Side Enhancement

  • Route Guards: ProtectedRoute components for smooth UX
  • Session Handling: Automatic session timeout detection
  • Smart Redirects: Preserves user's intended destination

πŸ”’ Layer 3: Component-Level

  • Conditional Rendering: Auth-aware UI components
  • Loading States: Professional loading indicators during auth checks
  • Error Boundaries: Graceful error handling and recovery

User Flows

πŸ†• New User Registration

  1. Visit protected route (e.g., /dashboard) β†’ Redirect to /login
  2. Click "Create Account" β†’ Fill registration form
  3. Email verification β†’ Account confirmed
  4. Automatic redirect to originally requested page

πŸ”„ Returning User Login

  1. Visit /login or click "Login" in navbar
  2. Choose email/password or Google OAuth
  3. Successful authentication β†’ Redirect to /dashboard
  4. Session persists across browser sessions

⏰ Session Management

  1. Session expires β†’ Automatic detection
  2. User redirected to /login with return path stored
  3. After re-authentication β†’ Return to original page
  4. Logout β†’ Clear session and redirect to /login

πŸ§ͺ MVP Testing Strategy

Q&A Testing Approach (MVP Focus)

For rapid MVP development, we prioritize manual Q&A testing over comprehensive unit testing:

βœ… Manual Testing Scenarios

Route Protection:

  1. Visit protected routes without login β†’ Should redirect to /login
  2. Login and access protected routes β†’ Should work normally
  3. Logout β†’ Should clear session and redirect appropriately

Course Navigation:

  1. Browse course catalog β†’ All courses should display correctly
  2. Navigate through chapters β†’ Progress should update
  3. Complete quizzes β†’ Should track completion status

Timer Functionality:

  1. Start quiz timer β†’ Should countdown properly
  2. Timer expiration β†’ Should trigger appropriate actions
  3. Different timer variants β†’ Should display correctly

βœ… Code Quality Checks

# Run linting (enforced in CI/CD)
npm run lint:strict

# Run type checking
npm run type-check

# Validate build
npm run validate

βœ… Post-MVP Testing Plan

After feature completion, we'll implement:

  • Comprehensive unit test suite
  • Integration tests for key workflows
  • End-to-end testing with Playwright
  • Performance and accessibility testing

πŸ“ Project Structure

8p3p-lms-nextjs/
β”œβ”€β”€ πŸ“ amplify/                 # AWS Amplify backend configuration
β”‚   β”œβ”€β”€ auth/resource.ts        # Authentication setup
β”‚   └── backend.ts              # Backend entry point
β”œβ”€β”€ πŸ“ src/
β”‚   β”œβ”€β”€ πŸ“ app/                 # Next.js App Router
β”‚   β”‚   β”œβ”€β”€ πŸ“ (auth)/         # Auth route group
β”‚   β”‚   β”‚   └── login/         # Login page
β”‚   β”‚   β”œβ”€β”€ πŸ“ dashboard/      # Protected dashboard
β”‚   β”‚   β”œβ”€β”€ πŸ“ courses/        # Protected courses
β”‚   β”‚   β”‚   └── πŸ“ [id]/       # Course detail pages
β”‚   β”‚   β”‚       └── πŸ“ [chapterId]/  # Chapter pages
β”‚   β”‚   β”‚           └── πŸ“ sections/  # Section content
β”‚   β”‚   β”‚               └── πŸ“ [sectionId]/  # Individual sections
β”‚   β”‚   β”œβ”€β”€ πŸ“ api/            # API routes
β”‚   β”‚   β”‚   β”œβ”€β”€ tavus/         # Tavus AI conversation API
β”‚   β”‚   β”‚   β”‚   └── conversation/  # Create conversation endpoint
β”‚   β”‚   β”‚   └── user/          # Protected user endpoint
β”‚   β”‚   └── layout.tsx         # Root layout with auth provider
β”‚   β”œβ”€β”€ πŸ“ components/         # Reusable components
β”‚   β”‚   β”œβ”€β”€ πŸ“ auth/           # Authentication components
β”‚   β”‚   β”‚   β”œβ”€β”€ AuthProvider.tsx      # Amplify provider wrapper
β”‚   β”‚   β”‚   β”œβ”€β”€ ProtectedRoute.tsx    # Client-side route guard
β”‚   β”‚   β”‚   β”œβ”€β”€ AuthRedirect.tsx      # Login page redirect logic
β”‚   β”‚   β”‚   β”œβ”€β”€ SessionHandler.tsx    # Session timeout handler
β”‚   β”‚   β”‚   └── SignIn.tsx            # Amplify authenticator
β”‚   β”‚   β”œβ”€β”€ πŸ“ course/         # Course components
β”‚   β”‚   β”‚   β”œβ”€β”€ course-sidebar.tsx    # Course navigation sidebar
β”‚   β”‚   β”‚   β”œβ”€β”€ course-overview.tsx   # Course overview page
β”‚   β”‚   β”‚   β”œβ”€β”€ breadcrumb-nav.tsx    # Breadcrumb navigation
β”‚   β”‚   β”‚   └── πŸ“ chapter-content/   # Chapter components
β”‚   β”‚   β”‚       β”œβ”€β”€ index.tsx         # Main chapter wrapper
β”‚   β”‚   β”‚       β”œβ”€β”€ video-player.tsx  # Video player component
β”‚   β”‚   β”‚       β”œβ”€β”€ interactive-script.tsx # Transcript component
β”‚   β”‚   β”‚       β”œβ”€β”€ chapter-quiz.tsx  # Quiz component
β”‚   β”‚   β”‚       └── ask-question.tsx  # Question form component
β”‚   β”‚   β”œβ”€β”€ πŸ“ common/         # Common reusable components
β”‚   β”‚   β”‚   └── πŸ“ timer/      # Timer component system
β”‚   β”‚   β”‚       β”œβ”€β”€ Timer.tsx          # Main timer component (Client)
β”‚   β”‚   β”‚       └── TimerDisplay.tsx   # Timer display (Server)
β”‚   β”‚   β”œβ”€β”€ πŸ“ cvi/            # Tavus CVI components (auto-generated)
β”‚   β”‚   β”‚   β”œβ”€β”€ πŸ“ components/ # CVI UI components
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ conversation/     # Conversation component
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ cvi-provider/     # CVI provider
β”‚   β”‚   β”‚   β”‚   └── device-select/    # Device management
β”‚   β”‚   β”‚   └── πŸ“ hooks/      # CVI hooks
β”‚   β”‚   β”‚       └── use-cvi-call.tsx  # CVI call hook
β”‚   β”‚   └── πŸ“ ui/             # shadcn/ui components
β”‚   β”œβ”€β”€ πŸ“ hooks/              # Custom React hooks
β”‚   β”‚   β”œβ”€β”€ useTimer.ts        # Timer hook with state management
β”‚   β”‚   β”œβ”€β”€ use-mobile.ts      # Mobile detection hook
β”‚   β”‚   └── use-params.ts      # Next.js 15+ params hook
β”‚   β”œβ”€β”€ πŸ“ lib/                # Utility functions
β”‚   β”‚   β”œβ”€β”€ auth-server.ts     # Server-side auth utilities
β”‚   β”‚   β”œβ”€β”€ course-utils.ts    # Course utility functions
β”‚   β”‚   β”œβ”€β”€ mock-data.ts       # Mock data for development
β”‚   β”‚   β”œβ”€β”€ utils.ts           # General utilities
β”‚   β”‚   └── πŸ“ utils/          # Utility modules
β”‚   β”‚       └── time-formatting.ts # Time formatting and validation
β”‚   β”œβ”€β”€ πŸ“ types/              # TypeScript type definitions
β”‚   β”‚   └── tavus.ts           # Tavus API types
β”‚   └── amplify_outputs.json   # Amplify configuration
β”œβ”€β”€ πŸ“ context/              # Documentation and context
β”‚   β”œβ”€β”€ course-page-structure.md  # Course page structure guide
β”‚   └── url-slugs-guide.md    # URL slug implementation guide
β”œβ”€β”€ middleware.ts              # Server-side route protection
β”œβ”€β”€ eslint.config.mjs          # ESLint configuration for Next.js 15+
β”œβ”€β”€ amplify_outputs.json       # Root Amplify config
└── package.json

πŸš€ Deployment

AWS Amplify (Recommended)

# Deploy to AWS Amplify
npx ampx pipeline-deploy --branch main

Manual Deployment

# Lint and type check before building
npm run lint
npm run type-check

# Build for production
npm run build

# Start production server
npm start

πŸ› οΈ Development Workflow

Pre-commit Validation

Run local checks before pushing to prevent CI/CD failures:

# Run all validation checks locally
npm run pre-commit

# Individual checks
npm run lint
npm run type-check
npm run build

CI/CD Pipeline

Automated validation on every PR and push:

  • ESLint: Code quality and style checking
  • TypeScript: Type safety validation
  • Build: Production build verification
  • Caching: Optimized with Node.js 20 and enhanced caching
  • Security: Minimal permissions and concurrency control

πŸ”§ Configuration

Environment Variables

# .env.local (auto-generated by Amplify)
NEXT_PUBLIC_AWS_PROJECT_REGION=<your-region>
NEXT_PUBLIC_AWS_COGNITO_REGION=<your-region>
# ... other Amplify-generated variables

Theme System Configuration

The theme system uses Tailwind CSS v4 with a dual-layer approach for maximum flexibility:

🎨 Adding New Colors

  1. Define in @theme directive (required for Tailwind v4):
/* src/app/globals.css */
@theme {
	/* Add your new color with both light and dark variants */
	--color-success: oklch(67.31% 0.162 144.21);
	--color-success-dark: oklch(55% 0.15 144);
	--color-success-foreground: oklch(100% 0 0);
	--color-success-foreground-dark: oklch(0% 0 0);
}
  1. Reference in CSS custom properties (for runtime theme switching):
:root {
	--success: var(--color-success);
	--success-foreground: var(--color-success-foreground);
}

.dark {
	--success: var(--color-success-dark);
	--success-foreground: var(--color-success-foreground-dark);
}
  1. Use in components:
// As Tailwind utility
<div className="bg-success text-success-foreground">Success message</div>

// As CSS variable
<div style={{ backgroundColor: 'var(--success)' }}>Success message</div>

🌈 Brand Color System

Brand colors are pre-defined and available as both utilities and CSS variables:

/* Available brand colors */
--color-brand-blue: oklch(28.08% 0.051 260.2); /* Primary brand */
--color-brand-gold: oklch(76.65% 0.139 91.06); /* Accent/CTA */
--color-brand-gray: oklch(71.37% 0.019 261.32); /* Neutral */
--color-brand-light-blue: oklch(96.53% 0.007 247.9); /* Backgrounds */
--color-brand-green: oklch(67.31% 0.162 144.21); /* Success */
--color-brand-red: oklch(64.27% 0.215 28.81); /* Error/Warning */

Usage:

<button className="bg-brand-gold text-brand-blue">CTA Button</button>

πŸ”§ Extending shadcn/ui Components

To customize shadcn/ui component colors:

  1. Modify the base theme colors in @theme directive
  2. Components automatically inherit the new colors
  3. Override specific components using CSS custom properties:
/* Override specific component styling */
@layer components {
	.custom-button {
		background-color: var(--color-brand-gold);
		color: var(--color-brand-blue);
	}
}

⚑ Production Build Requirements

Critical: All theme colors must be defined in the @theme directive for Tailwind v4 production builds:

/* βœ… CORRECT - Will work in production */
@theme {
	--color-custom: oklch(50% 0.1 180);
}

/* ❌ INCORRECT - Will be stripped in production */
:root {
	--custom-color: #somecolor;
}

Authentication Settings

Edit amplify/auth/resource.ts to customize:

  • User attributes (email, name, custom fields)
  • Password policies
  • MFA settings
  • OAuth providers
  • Callback URLs

External Identity Providers Setup

Google OAuth 2.0 Configuration

Required: Configure Google OAuth 2.0 Console with Cognito user pool domains when deploying to new environments:

  1. Visit Google Cloud Console: console.cloud.google.com
  2. Navigate to: APIs & Services β†’ Credentials β†’ OAuth 2.0 Client IDs
  3. Select your OAuth client and click the Edit button

Step 1: Configure Authorized JavaScript Origins

Add your Cognito user pool domains (not your app URLs):

# From amplify_outputs.json - check your actual domains
https://<your-local-user-pool-domain>.auth.<region>.amazoncognito.com #local dev
https://<your-main-user-pool-domain>.auth.<region>.amazoncognito.com #main branch
https://<your-dev-user-pool-domain>.auth.<region>.amazoncognito.com #dev branch

Step 2: Configure Authorized Redirect URIs

Add your Cognito user pool domains with /oauth2/idpresponse endpoint:

# From amplify_outputs.json - check your actual domains
https://<your-local-user-pool-domain>.auth.<region>.amazoncognito.com/oauth2/idpresponse #local dev
https://<your-main-user-pool-domain>.auth.<region>.amazoncognito.com/oauth2/idpresponse #main branch
https://<your-dev-user-pool-domain>.auth.<region>.amazoncognito.com/oauth2/idpresponse #dev branch

Step 3: Find Your Cognito Domains

Method 1: From amplify_outputs.json (Local Development)

# Check your amplify_outputs.json file
cat amplify_outputs.json | grep "domain"

Method 2: From AWS Amplify Console (Deployed Branches)

For each deployed branch, follow these steps to find the Cognito User Pool domain:

  1. Navigate to Branch Deployments:

    # Replace with your actual region, app ID, and branch name
    https://<region>.console.aws.amazon.com/amplify/apps/<your-app-id>/branches/main/deployments
    https://<region>.console.aws.amazon.com/amplify/apps/<your-app-id>/branches/dev/deployments
    
  2. Access Backend Resources:

    • Scroll down to find the "Deployed backend Resources" tab
    • Click on the tab to expand the resources table
  3. Locate User Pool:

    • In the search bar, type: UserPool
    • Find and click on "AWS::Cognito::UserPool" in the "Type" column
  4. Find Domain Settings:

    • A new browser window opens to AWS Cognito Console
    • In the left sidebar, click "Domain" under the "Branding" section
    • Copy the "Cognito Domain" from the main content area

Example Cognito domains by branch:

# Local Development (from amplify_outputs.json)
https://<local-user-pool-id>.auth.<region>.amazoncognito.com

# Main Branch (from AWS Console)
https://<main-user-pool-id>.auth.<region>.amazoncognito.com

# Dev Branch (from AWS Console)
https://<dev-user-pool-id>.auth.<region>.amazoncognito.com

Step 4: Configure Secrets

In AWS Amplify Console β†’ Secrets:

# Replace with your actual region and app ID
https://<region>.console.aws.amazon.com/amplify/apps/<your-app-id>/secrets

Add the following secrets:

  • GOOGLE_CLIENT_ID: Your Google OAuth client ID
  • GOOGLE_CLIENT_SECRET: Your Google OAuth client secret

Reference: AWS Amplify External Identity Providers Guide

Timer Components System

Comprehensive timer functionality for quizzes, AI interactions, and progress tracking:

πŸ• Core Timer Features

  • Multiple Variants: Countdown, stopwatch, and progress timers
  • Accurate Timing: High-resolution timestamps with tab visibility handling
  • Accessibility: Full WCAG compliance with screen reader support
  • Keyboard Controls: Spacebar/Enter to toggle, Ctrl+R to reset, Escape to pause
  • Visual Indicators: Color-coded urgency (green β†’ yellow β†’ red)
  • Context Validation: Different time limits for quiz, AI, learning, and general contexts

🧩 Component Architecture

// Main Timer Component (Client Component)
<Timer
  duration={300}                    // 5 minutes
  variant="countdown"               // countdown | stopwatch | progress
  context="quiz"                    // quiz | ai | learning | general
  showControls                      // Play/pause/reset buttons
  showProgress                      // Progress bar for progress variant
  onComplete={handleSubmit}         // Callback when timer completes
  onTick={updateState}             // Callback every second
/>

// Compact Timer (for navigation/cards)
<CompactTimer
  duration={240}                    // 4 minutes
  variant="countdown"
  context="ai"
  autoStart
/>

// Timer Display Only (Server Component)
<TimerDisplay
  time={180}                        // Current time in seconds
  color="warning"                   // Visual urgency indicator
  showLabel                         // "3 minutes remaining"
  size="lg"                         // sm | md | lg
/>

βš™οΈ Timer Hook Usage

// Custom timer logic with full control
const timer = useTimer(300, {
	variant: "countdown",
	autoStart: true,
	onComplete: () => submitQuiz(),
	onTick: (state) => updateProgress(state.progress),
});

// Timer state and controls
const {
	time, // Current time (seconds)
	isRunning, // Timer running state
	isPaused, // Timer paused state
	isCompleted, // Timer completed state
	progress, // Progress percentage (0-100)
	start,
	pause,
	resume, // Control functions
	reset,
	toggle, // Control functions
	getFormattedTime, // "05:30" format
	getTimeWithLabel, // "5 minutes 30 seconds remaining"
	getColorTheme, // "success" | "warning" | "danger"
} = timer;

🎯 Context-Specific Validation

// Automatic validation based on context
validateTimerDuration(300, "quiz"); // βœ… Valid (30s - 1hr)
validateTimerDuration(600, "ai"); // ❌ Too long (1min - 5min)
validateTimerDuration(30, "learning"); // ❌ Too short (1min - 30min)
validateTimerDuration(7200, "general"); // βœ… Valid (1s - 2hr)

πŸ”§ Time Formatting Utilities

formatTime(90); // "01:30"
formatTime(3661, true); // "1:01:01"
formatTimeWithLabel(90, "remaining"); // "1 minute 30 seconds remaining"
calculateProgress(30, 60); // 50 (percent)
getTimerColorTheme(10, 60); // "danger" (< 25% remaining)

πŸŽ₯ Interactive Video Player with Transcript Sync

The 8P3P LMS features a production-ready video player with synchronized WebVTT transcripts, providing an accessible and engaging learning experience.

✨ Key Features

  • πŸ“Ή Mux Streaming: Adaptive HLS streaming with automatic quality adjustment
  • πŸ“ Live Transcripts: Real-time synchronized captions with precise timestamps
  • 🎯 Auto-Highlighting: Active transcript segment highlights as video plays
  • πŸ‘† Click-to-Seek: Jump to any video moment by clicking transcript text
  • β™Ώ Accessibility: Full screen reader support and keyboard navigation
  • πŸ”„ Bidirectional Sync: Video controls transcript, transcript controls video

πŸ› οΈ Technical Implementation

Video Player Architecture

// InteractiveVideoPlayer - Full featured player with transcript
<InteractiveVideoPlayer
  src={video_url}                     // next-video Asset or string URL
  showTranscript={true}               // Toggle transcript panel
  transcript={parsedVTT}              // Array of TranscriptSegment[]
  layout="default"                    // default | theater | compact
  autoPlay={false}
  muted={false}
/>

VTT Parser

Converts WebVTT format to structured transcript segments:

import { parseVTT } from '@/lib/utils/vtt-parser';

const vttContent = `WEBVTT

1
00:00:00.000 --> 00:00:05.330
Welcome to chapter one of the EMDR course.`;

const segments = parseVTT(vttContent);
// [{ id: "1", startTime: 0, endTime: 5.33, text: "Welcome..." }]

Callback Ref Pattern

Ensures reliable video-transcript synchronization:

// VideoPlayer uses callback ref for event listeners
const setVideoRef = useCallback((element: HTMLVideoElement | null) => {
  if (element) {
    // Attach listeners immediately when element mounts
    element.addEventListener('timeupdate', handleTimeUpdate);
    element.addEventListener('durationchange', handleDurationChange);
  }
}, []);

<Video ref={setVideoRef} ... />

πŸ“Š How Sync Works

Video β†’ Transcript (Auto-highlight)

const handleTimeUpdate = (currentTime: number) => {
  // Find active segment based on current time
  const active = transcript.find(
    seg => currentTime >= seg.startTime && currentTime < seg.endTime
  );
  setActiveSegmentId(active?.id);
};

Transcript β†’ Video (Click-to-seek)

const handleSegmentClick = (startTime: number) => {
  // Seek video to timestamp
  videoPlayerRef.seekTo(startTime);
  setCurrentTime(startTime);
};

🎬 Video Management Workflow

1. Video Processing

Videos are uploaded to Mux and processed automatically:

# Videos stored in /videos directory
videos/
  β”œβ”€β”€ Intro-8p3p-Ch1-Section-1-1.mp4
  β”œβ”€β”€ Intro-8p3p-Ch1-Section-1-1.mp4.json  # Auto-generated metadata
  └── ...

2. Fetching VTT Transcripts

# Fetch VTT files from Mux (requires playback ID + track ID)
node scripts/fetch-mux-vtt.js

# Script outputs formatted VTT content to console
# Copy the videoVTT snippets directly to mock-data.ts

3. Mock Data Integration

{
  id: "section_1_1",
  title: "Introduction",
  videoUrl: video_1_1,           // next-video Asset
  videoScript: "...",             // Plain text script
  videoVTT: `WEBVTT...`,         // WebVTT format transcript
  sectionType: "video"
}

πŸ”§ Component API

InteractiveVideoPlayer Props

interface InteractiveVideoPlayerProps {
  src: Asset | string;                  // Video source
  poster?: string;                       // Thumbnail image
  transcript?: TranscriptSegment[];      // Parsed VTT segments
  showTranscript?: boolean;              // Show/hide transcript
  layout?: "default" | "theater" | "compact";
  autoPlay?: boolean;
  muted?: boolean;
  className?: string;
}

TranscriptSegment Type

interface TranscriptSegment {
  id: string;           // Unique segment identifier
  startTime: number;    // Start time in seconds (e.g., 5.33)
  endTime: number;      // End time in seconds
  text: string;         // Transcript text content
}

πŸ“– Usage Example

import { InteractiveVideoPlayer, parseVTT } from '@/components/video';

export function VideoSection({ section }) {
  const transcript = section.videoVTT 
    ? parseVTT(section.videoVTT)
    : createTranscriptFromScript(section.videoScript, 90);

  return (
    <InteractiveVideoPlayer
      src={section.videoUrl}
      showTranscript={true}
      transcript={transcript}
      layout="default"
    />
  );
}

🎯 Benefits

  • πŸ“š Enhanced Learning: Visual + audio + text reinforcement
  • β™Ώ Accessibility: Deaf/hard-of-hearing support, non-native speakers
  • πŸ” Quick Reference: Click transcript to review specific concepts
  • πŸ“± Mobile-Friendly: Responsive design with touch support
  • ⚑ Performance: Lazy-loaded transcripts, optimized rendering

Data Models

Enhanced tracking capabilities:

// Chapter completion tracking
interface Chapter {
	videoCompleted?: boolean;
	quizPassed?: boolean;
	questionAskedCount?: number;
	timeSpent?: number; // Time spent in seconds
	quizAttempts?: number; // Number of quiz attempts
}

// Course progress tracking
interface Course {
	lastViewedChapter?: string;
	completedChapters: string[];
	totalTimeSpent?: number; // Total learning time
	averageQuizScore?: number; // Average quiz performance
}

// Timer interaction tracking
interface TimerSession {
	context: "quiz" | "ai" | "learning";
	duration: number; // Configured duration
	timeUsed: number; // Actual time used
	completed: boolean; // Whether timer completed naturally
	pauseCount?: number; // Number of pauses
}

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for detailed information on:

  • πŸš€ Getting Started: Setup and development workflow
  • πŸ“ PR Guidelines: Stacked PRs, size limits, and review process
  • πŸ§ͺ Testing Standards: Required tests and validation
  • πŸ“‹ Code Quality: Standards, comments, and best practices
  • πŸ”„ Release Process: Sprint-based development and deployment

Quick Start

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/amazing-feature)
  3. Run pre-commit checks (npm run pre-commit)
  4. Commit changes (git commit -m 'Add amazing feature')
  5. Push to branch (git push origin feature/amazing-feature)
  6. Open Pull Request

πŸ“– For detailed guidelines, see CONTRIBUTING.md

Code Quality Standards

  • Next.js 15+ Compliance: Server components first, client only when needed
  • Data Schema Compliance: Update mock-data models for new state/properties
  • Dependency Compliance: Verify required packages before implementation
  • Pre-commit Validation: All checks must pass locally before pushing
  • ESLint Rules: Strict enforcement with no unused variables/imports

Development Rules

  • Server Components: Default choice for better performance
  • Client Components: Only for interactivity (useState, event handlers, browser APIs)
  • Link Navigation: Use Next.js Link instead of useRouter when possible
  • Route Parameters:
    • Server Components: Use await params directly
    • Client Components: Use useParams hook from @/hooks/use-params
    • NEVER: Use React.use() directly - always use established patterns
  • Component Reuse: Leverage existing components (ChapterProgress, etc.)
  • TypeScript Compliance: Layout props must use Promise-only params type for Next.js 15+

πŸ“Š Content Estimation System

Overview

The Content Estimation System provides intelligent time estimation for mixed educational content (text + video) with personalized learning recommendations. Built following our component reusability protocol using existing timer infrastructure and shadcn/ui components.

Features

🧠 Smart Content Analysis

  • Text Analysis: Word count, reading speed calculation (150-250 WPM), complexity assessment
  • Video Analysis: Duration parsing, type classification (lecture/demo/interactive), engagement factors
  • Mixed Content: Combined analysis with interaction time calculation
  • Complexity Detection: Simple/Moderate/Complex classification based on technical terms and structure

πŸ‘€ Personalization Engine

  • User Profiles: Reading speed tracking, completion rate history, learning pace preferences
  • Adaptive Estimates: Personalized time calculations based on user performance
  • Confidence Scoring: Reliability assessment for time estimates (0-1 scale)
  • Profile Updates: Automatic learning from actual completion times

🎯 Learning Recommendations

  • Session Planning: Optimal session length and break frequency suggestions
  • Pace Recommendations: Fast/Moderate/Slow based on content complexity and user profile
  • Progress Tracking: Real-time progress monitoring with remaining time calculations

Components

Core Analysis Engine

// Text analysis with complexity detection
import { analyzeTextContent } from "@/lib/content-analysis";
const analysis = analyzeTextContent(content);

// Video analysis with engagement factors
import { analyzeVideoContent } from "@/lib/content-analysis";
const videoAnalysis = analyzeVideoContent(metadata);

// Complete time estimation
import { calculateTimeEstimate } from "@/lib/content-analysis";
const estimate = calculateTimeEstimate(mixedContent, userProfile);

UI Components

// Time estimate display (multiple variants)
import { TimeEstimate } from '@/components/common/content-estimation';
<TimeEstimate
  estimate={estimate}
  variant="detailed"
  showBreakdown={true}
  complexity="moderate"
/>

// Content estimator with analysis
import { ContentEstimator } from '@/components/common/content-estimation';
<ContentEstimator
  content={mixedContent}
  userProfile={userProfile}
  onEstimateReady={(estimate) => console.log(estimate)}
  trackProgress={true}
/>

// Progress tracking
import { ProgressIndicator } from '@/components/common/progress';
<ProgressIndicator
  progress={45}
  estimatedTime={1800}
  elapsedTime={810}
  variant="linear"
/>

Custom Hooks

// Content estimation with caching
import { useContentEstimation } from "@/hooks/useContentEstimation";
const { estimate, isLoading, error, analyze } = useContentEstimation({
	content: mixedContent,
	userProfile: userProfile,
	autoAnalyze: true,
});

// Progress tracking
import { useProgressTracking } from "@/hooks/useContentEstimation";
const { elapsedTime, remainingTime, progress, startTracking, stopTracking } =
	useProgressTracking(estimatedTime);

Integration Examples

Basic Usage

import { ContentEstimator } from '@/components/common/content-estimation';

function LessonPage({ lesson }) {
  const handleEstimateReady = (estimate) => {
    console.log(`Estimated time: ${estimate.total} seconds`);
    console.log(`Confidence: ${estimate.confidence * 100}%`);
  };

  return (
    <div>
      <ContentEstimator
        content={{
          text: lesson.content,
          videos: lesson.videos,
          contentType: "lesson"
        }}
        userProfile={user.learningProfile}
        onEstimateReady={handleEstimateReady}
        variant="card"
        showBreakdown={true}
      />
    </div>
  );
}

Advanced Usage with Progress Tracking

import { useContentEstimation, useProgressTracking } from '@/hooks/useContentEstimation';

function InteractiveLearningSession({ content, userProfile }) {
  const { estimate, isLoading } = useContentEstimation({
    content,
    userProfile,
    autoAnalyze: true
  });

  const {
    progress,
    elapsedTime,
    remainingTime,
    startTracking,
    stopTracking,
    updateProgress
  } = useProgressTracking(estimate?.total || 0);

  const handleStartLearning = () => {
    startTracking();
  };

  const handleProgressUpdate = (completionPercentage) => {
    updateProgress(completionPercentage);
  };

  return (
    <div>
      {estimate && (
        <ProgressIndicator
          progress={progress}
          estimatedTime={estimate.total}
          elapsedTime={elapsedTime}
          variant="linear"
          showTimeRemaining={true}
        />
      )}
    </div>
  );
}

Development & Integration

The MVP system focuses on simple, reliable functionality:

  • Basic time calculations using configurable constants
  • Reusable timer component with multiple variants
  • Simple progress tracking based on chapter completion
  • Q&A testing approach for rapid iteration
# Validate code quality
npm run validate

# Run development server
npm run dev

Mock Data

Comprehensive mock data available for development and testing:

import {
	mockLessons,
	mockUserProfiles,
	mockTimeEstimates,
} from "@/lib/mock-data/content-estimation";

// Use in development
const estimate = mockTimeEstimates.mediumLesson;
const userProfile = mockUserProfiles.fastReader;
const content = mockLessons.comprehensiveChapter;

πŸ› Troubleshooting

Common Issues

Build Errors

# Fix TypeScript errors
npm run type-check

# Fix linting issues
npm run lint:fix

# Clean build
rm -rf .next && npm run build

Tailwind CSS v4 Production Build Issues

# CSS variable recognition errors in production
# Root cause: Custom CSS variables not integrated with Tailwind v4 theme system
# Solution: Define colors in @theme directive using --color-* namespace

# Check for CSS variable issues
grep -r "var(--" src/ --include="*.css"

# Verify Tailwind v4 theme configuration
# Ensure colors are defined in @theme { --color-* } format
# Reference: docs/design-system-rules.md#tailwind-v4-production-build-issues

TypeScript Layout Constraint Errors

# Common issue: Layout params type incompatible with Next.js 15+
# Solution: Use Promise<{ id: string }> instead of union types
# Client components: Use useParams hook from @/hooks/use-params
# Server components: Use await params directly

Missing Dependencies

# Install shadcn/ui components
npx shadcn@latest add dialog
npx shadcn@latest add progress

# Verify package.json dependencies
npm ci

Authentication Issues

# Restart Amplify sandbox
npx ampx sandbox --delete
npx ampx sandbox

πŸ“ License

MIT License - see LICENSE file for details.

πŸ†˜ Support


Built with ❀️ by Rhyan Vargas and 8P3P

Β© 2025 8P3P

About

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published