Skip to content

artsofpixel/aaft

Repository files navigation

MiniLMS — Learning Management System

A modern, responsive Learning Management System built with Next.js, TypeScript, Redux Toolkit, and Tailwind CSS. Features separate Admin and Student portals with full CRUD operations, video lesson playback with progress tracking, and analytics dashboards.

Table of Contents


Setup Instructions

# 1. Clone the repository
git clone <repo-url>
cd aaft

# 2. Install dependencies
npm install

# 3. Copy environment file (no variables required for mock mode)
cp .env.example .env.local

# 4. Start the dev server
npm run dev

Open http://localhost:3000 in your browser.

Demo Accounts

Role Email Password
Admin admin@lms.io admin123
Student aarav@lms.io student123
Student priya@lms.io student123

Environment Variables

See .env.example for reference. Currently no environment variables are required — all API calls are mocked in src/services/api.ts.

If connecting to a real backend, set:

NEXT_PUBLIC_API_URL=http://localhost:3001/api

Available Scripts

Script Command Description
dev npm run dev Start development server
build npm run build Create production build
start npm start Serve production build
lint npm run lint Run ESLint

Redux State Structure

The store uses feature-based slices via Redux Toolkit's createSlice and createAsyncThunk:

store/
├── store.ts          # configureStore with all reducers
├── hooks.ts          # Typed useAppDispatch / useAppSelector
├── selectors.ts      # Reusable memoized selectors (createSelector)
└── slices/
    ├── authSlice.ts      # Auth: user, token, isAuthenticated, loading, error
    ├── coursesSlice.ts   # Courses: courses[], selectedCourse, CRUD thunks
    ├── studentsSlice.ts  # Students: students[], selectedStudent, enrollment thunks
    ├── progressSlice.ts  # Progress: videoProgress{}, courseProgress{}
    └── uiSlice.ts        # UI: sidebarOpen, theme, notifications[]

Slice shapes:

  • Auth{ user, token, isAuthenticated, loading, error }
  • Courses{ courses: Course[], selectedCourse, loading, error }
  • Students{ students: Student[], selectedStudent, loading, error }
  • Progress{ videoProgress: Record<lessonId, VideoProgress>, courseProgress: Record<courseId, CourseProgressInfo> }
  • UI{ sidebarOpen, theme: 'light'|'dark', notifications[] }

Selectors (store/selectors.ts):

Reusable selectors built with createSelector for memoized derived data:

  • selectCurrentUser, selectUserRole, selectIsAuthenticated
  • makeSelectFilteredCourses(searchTerm), makeSelectFilteredStudents(searchTerm)
  • selectCompletedLessonsCount, selectTotalWatchTime, selectCompletedCoursesCount
  • selectAnyLoading — combined loading state across all slices

Component Architecture

src/
├── app/                        # Next.js App Router pages
│   ├── admin/                  # Admin portal (protected)
│   │   ├── page.tsx            # Dashboard with charts
│   │   ├── students/           # Student CRUD + profile
│   │   ├── courses/            # Course CRUD + lesson management
│   │   ├── assignments/        # Multi-select course enrollment
│   │   └── reports/            # Analytics with real data
│   ├── student/                # Student portal (protected)
│   │   ├── dashboard/          # Personal stats + progress
│   │   └── courses/            # Course library + video player
│   └── login/                  # Auth page with demo presets
├── components/
│   ├── common/                 # Shared: Sidebar, Header, Modal, etc.
│   ├── admin/                  # Admin-specific: AdminCourseCard
│   └── student/                # Student-specific: StudentCourseCard, StudentStatsRow
├── hooks/                      # Custom hooks
│   ├── useAuth.ts              # Login/logout logic
│   └── useVideoPlayer.ts       # YouTube iframe + progress tracking
├── store/                      # Redux store + slices + selectors
├── services/
│   └── api.ts                  # Centralized mock API (all endpoints)
├── types/
│   ├── index.ts                # All TypeScript interfaces
│   └── youtube.d.ts            # YouTube IFrame API types
└── utils/
    └── index.ts                # Shared helpers (formatDuration, getInitials, etc.)

API Integration

All API calls go through src/services/api.ts, a centralized mock layer that simulates REST endpoints with realistic delays:

Method Endpoint Description
POST /api/auth/login Authenticate user
GET /api/admin/students List all students
POST /api/admin/students Create student
PUT /api/admin/students/:id Update student
DELETE /api/admin/students/:id Delete student
GET /api/admin/courses List all courses
POST /api/admin/courses Create course
POST /api/admin/enrollments Assign courses to student
GET /api/admin/reports/students/:id Student progress report
GET /api/student/courses Get enrolled courses
POST /api/student/progress Update video watch progress

Pattern: Redux thunks (createAsyncThunk) call API functions → API returns mock data → slice reducers update state → components re-render via selectors.

To switch to a real backend: replace the mock functions in api.ts with actual fetch/axios calls. The thunks and reducers stay the same.


Design Decisions

Why Redux Toolkit over Context API?

The app has deeply nested state relationships (student enrollments → course progress → video tracking) that benefit from centralized state. RTK's createAsyncThunk handles async lifecycle (pending/fulfilled/rejected) cleanly.

Why mock API instead of json-server?

A single api.ts file with in-memory data is zero-config — no external process needed. It simulates realistic delays and error cases, and the mock data stays consistent for demos.

Video Player Architecture

The YouTube IFrame API integration is encapsulated in a custom hook (useVideoPlayer) to keep the page component focused on layout. Key decisions:

  • Refs over state for the player instance and completion flags to avoid stale closures in the progress interval
  • 5-second progress interval balances accuracy vs. API call frequency
  • 90% completion threshold automatically marks lessons complete
  • Auto-resume seeks to lastWatched position on player ready

Styling Approach

Using Tailwind CSS v4 with CSS custom properties for theming (dark/light). All colors are semantic tokens (--primary, --surface, --border-color) so switching themes is a single data-theme attribute change. No hardcoded colors in components.

TypeScript Strict Mode

tsconfig.json has "strict": true. All state shapes, API payloads, and component props use explicit interfaces from types/index.ts.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors