A full-stack LMS built with Laravel, React, and TypeScript that models real education workflows: public discovery, gated learning, and role-based authoring.
This project focuses on clean API design, structured data flow, and a frontend that reflects backend permissions rather than duplicating logic.

- Token-based authentication with Laravel Sanctum
- Role and permission handling using Spatie Laravel Permission
- Public course browsing with protected enrollment paths
- Instructor draft → publish workflow
- Modular course structure (courses → modules → lessons)
- Progress tracking at the lesson level
- Typed API consumption on the React side
- Clear separation between public, learner, and authoring areas
The goal was to build something that feels closer to a real product than a demo CRUD.
Visitor
- Browse course catalog
- View course details
Student
- Enroll in courses
- Track lesson completion
- View personal learning dashboard
Instructor / Admin
- Create and manage courses
- Organize modules and lessons
- Publish when ready
Permissions are enforced server-side and reflected in the UI.
- Laravel 12
- PHP 8.2+
- Laravel Sanctum
- Spatie Laravel Permission
- MySQL (SQLite supported for local testing)
- React 18
- TypeScript
- Vite
- React Router
- Tailwind CSS
- Axios
cd backend
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed
php artisan serveAPI runs at: http://127.0.0.1:8000
cd client
npm install
npm run devClient runs at: http://localhost:5173
- Admin →
admin@example.com/password - Instructor →
instructor@example.com/password - Student →
student@example.com/password
Routes live in backend/routes/api.php.
Public
GET /api/coursesGET /api/courses/{slug}
Auth
POST /api/registerPOST /api/loginPOST /api/logoutGET /api/profilePUT /api/profilePATCH /api/profile/password
Student
GET /api/my-coursesPOST /api/courses/{course}/enrollGET /api/courses/{course}/enrollmentGET /api/lessons/{lesson}PATCH /api/lessons/{lesson}/complete
Instructor/Admin
- Course CRUD + publish toggle
- Module CRUD
- Lesson CRUD
Defined in client/src/App.tsx.
/– landing/courses– catalog/courses/:courseRef– course details/login,/signup/my-courses,/profile,/lessons/:lessonId(authenticated)/instructor/*(role-restricted)
Route guards rely on the API profile response rather than client-side role assumptions.
Learning Management System/
backend/ Laravel API (models, policies, controllers, seeders)
client/ React + TypeScript app (pages, services, components)
---
## Architectural notes
* Business rules live in the backend (policies, permissions, publish state)
* The frontend consumes typed DTO-style responses
* Enrollment and completion are modeled as separate resources to support analytics later
* Draft content is never exposed through public endpoints
---
## Useful scripts
### Backend
```bash
php artisan migrate --seed
php artisan serve
php artisan test
npm run dev
npm run type-check
npm run buildPlanned improvements for a hosted environment:
- CI pipeline (tests, type checks, linting)
- API rate limiting and stricter validation layers
- Centralized logging and error tracking
- End-to-end tests for each role flow
- Docker setup with environment templates
MIT