A modern, open-source school management system built with Laravel 12, React 18, and Inertia.js. Designed for Malaysian educational institutions with a full 5-tier role-based access control (RBAC) system.
EduFlow is designed around real school operations: course management, classroom assignment, subject ownership, student records, lecturer responsibilities, class representatives, and attendance tracking.
The system includes a 5-tier role system with policy-based permissions:
| Role | Description |
|---|---|
| Admin | Full system access. |
| Moderator | Administrative access similar to Admin, but cannot manage Admin users/roles. |
| Lecturer / Course Manager | Lecturer promoted to manage one assigned course. |
| Lecturer / Teacher | Lecturer assigned to teach subjects in one or more classrooms/courses. |
| Classrep | Class representative who can record attendance within their allowed course/classroom scope. |
| Student | Basic student role with limited authenticated access. |
- View dashboard statistics and system overview.
- Create, read, update, and delete courses.
- Assign a lecturer as Course Manager for a course.
- Create, read, update, and delete classrooms.
- Assign a Classrep to a classroom.
- Assign lecturers to teach specific subjects in specific classrooms.
- Create, read, update, and delete subjects/mata pelajaran.
- Create, read, update, and delete lecturers.
- Create, read, update, and delete students.
- View, create, and edit attendance records.
- Create, read, update, and delete users across all roles.
- Access system settings for logo, site title, favicon, and branding-related values.
- Similar administrative workflow to Admin.
- Manage courses, classrooms, subjects, lecturers, students, attendance, and users.
- Cannot create, edit, assign, or manage Admin-level users/roles.
A Course Manager is a lecturer assigned as the manager of one course. One course can only have one manager.
- View courses they teach or manage.
- Manage classrooms within their assigned course.
- Manage subjects/mata pelajaran within their assigned course.
- Manage students within their assigned course.
- Create attendance for multiple classrooms under their managed course.
- Edit attendance only for subjects/classes they teach.
- View related academic records without gaining full system-wide admin access.
- View courses they teach.
- View classrooms related to their teaching assignments.
- View subjects/mata pelajaran related to their teaching assignments.
- View students in classrooms/courses they teach.
- Create attendance only for subjects they are assigned to teach.
- Edit attendance only for subjects they are assigned to teach.
- Record attendance for subjects within their allowed course/classroom scope.
- View attendance sessions relevant to their course/classroom scope.
- Course management with optional Course Manager assignment.
- Classroom management with course linking, Classrep assignment, and subject-teacher assignment.
- Subject/mata pelajaran management by course.
- Student profile management with classroom assignment.
- Session-based attendance grouped by subject, classroom, date, and recorder.
- Attendance filtering by date, classroom, and subject.
- Attendance detail pages for reviewing student attendance status.
- Login using email, username, or phone number.
- Account registration with inactive-by-default approval flow.
- Email verification with custom Malay email notification.
- Password reset with custom Malay email notification.
- Profile management with password update, account deletion, and profile picture upload.
- Smart search for users and lecturers using full-name matching, email, and username.
- Queue-ready notification system using Laravel queues.
- Development and production seeders.
- 47 automated tests covering authentication, courses, classrooms, subjects, students, lecturers, attendance, and profile flows.
| Layer | Technology |
|---|---|
| Backend | PHP 8.2+, Laravel 12 |
| Frontend | React 18, Inertia.js v2 |
| Styling | Tailwind CSS v4 |
| Build Tool | Vite 7 |
| Database | MySQL / SQLite |
| Testing | PHPUnit 11 |
Before you begin, ensure your system has the following installed:
- PHP >= 8.2 (with extensions:
pdo,mbstring,openssl,bcmath,tokenizer,xml,ctype,json) - Composer >= 2.x
- Node.js >= 18.x & npm >= 9.x
- MySQL >= 8.0 (or SQLite for local development)
- A working mail provider for production email delivery (SMTP by default)
git clone https://github.com/azrilsyamin/school.git
cd schoolcomposer installnpm installCopy the example environment file and generate the application key:
cp .env.example .env
php artisan key:generateFor production, make sure these values are changed before the application goes live:
APP_NAME=EduFlow
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.comOpen the .env file and update the database settings:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=school
DB_USERNAME=root
DB_PASSWORD=your_passwordTip: For quick local development using SQLite, set
DB_CONNECTION=sqliteand create a blank database file withtouch database/database.sqlite.
The project uses Laravel's database queue by default:
QUEUE_CONNECTION=databaseKeep this value unless you already know you want to use Redis, SQS, or another queue driver.
# For local development (includes dummy data):
php artisan migrate:fresh --seed
# For production (only essential data, no dummy data):
php artisan migrate --seedOnce the database is migrated and seeded with essential data, create your first admin account:
php artisan make:adminphp artisan storage:linkThis is required for uploaded profile pictures and other public storage files.
# For production:
npm run build
# For local development (with hot reload):
npm run devSeveral emails and notifications are queued, so they will not be sent until a queue worker is running:
php artisan queue:workFor local development, open another terminal and keep this command running while testing registration, email verification, password reset, and other email-related features.
The easiest way to run the full development stack is using the built-in Composer script, which starts the Laravel server, queue worker, log viewer, and Vite dev server concurrently:
composer run devAlternatively, you can run them separately in different terminals:
# Terminal 1: Laravel development server
php artisan serve
# Terminal 2: Vite frontend dev server
npm run dev
# Terminal 3: Queue worker for queued emails/notifications
php artisan queue:workThe application will be available at http://localhost:8000.
Email is required for account verification, registration notifications, password reset links, and other system notifications.
By default, Laravel uses MAIL_MAILER=log, which only writes emails to the log file. This is useful for local development, but it will not send real emails in production.
You can keep the log mailer while developing:
MAIL_MAILER=log
MAIL_FROM_ADDRESS="no-reply@example.test"
MAIL_FROM_NAME="${APP_NAME}"For local SMTP testing, you may use tools such as Mailpit, Mailhog, or a sandbox SMTP provider.
Update your .env with real SMTP credentials from your email provider:
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your_smtp_username
MAIL_PASSWORD=your_smtp_password
MAIL_SCHEME=tls
MAIL_FROM_ADDRESS="no-reply@your-domain.com"
MAIL_FROM_NAME="${APP_NAME}"After changing mail settings in production, clear and rebuild cached configuration:
php artisan config:clear
php artisan config:cacheThis project ships with standard Laravel mail configuration only. If you want to use an API provider such as Mailgun, Postmark, Amazon SES, Resend, or another service, install the required Laravel/Symfony mailer package and add the provider-specific environment variables yourself.
Example only:
MAIL_MAILER=postmark
POSTMARK_TOKEN=your-tokenDo not add API credentials to the repository. Keep them in .env or your hosting provider's secret manager.
This project uses queued notifications. If the queue worker is not running, users may register successfully but never receive verification emails, password reset emails, or other queued notifications.
Run the queue worker manually:
php artisan queue:workThe composer run dev command already starts a queue listener together with the Laravel server and Vite.
For production servers, run the worker with a process manager such as Supervisor or systemd so it restarts automatically.
Supervisor example:
[program:eduflow-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/eduflow/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/eduflow/storage/logs/worker.log
stopwaitsecs=3600Restart workers after every deployment:
php artisan queue:restartMany shared hosting providers do not allow long-running processes. In that case, add a cron job that runs the queue for a short time and exits.
Cron example, every minute:
* * * * * cd /home/username/path-to-project && /usr/local/bin/php artisan queue:work --stop-when-empty --tries=3 >> /dev/null 2>&1Adjust the PHP path and project path based on your hosting provider.
If you add scheduled tasks later, configure Laravel's scheduler cron as well:
* * * * * cd /home/username/path-to-project && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1The scheduler is different from the queue worker. The scheduler runs timed commands; the queue worker processes queued jobs.
Before going live, review this checklist:
- Set
APP_ENV=production,APP_DEBUG=false, and the correctAPP_URL. - Configure a real database and run
php artisan migrate --seed --force. - Create the first admin account with
php artisan make:admin. - Configure real email credentials and test password reset/email verification.
- Run
npm run buildand upload/serve the generated production assets. - Run
php artisan storage:linkfor public uploads. - Make sure
storage/andbootstrap/cache/are writable by the web server. - Start a queue worker using Supervisor/systemd, or configure a shared-hosting cron fallback.
- Point the web server document root to the
public/directory, not the project root. - Change all default/development passwords and avoid running development seeders in production.
Recommended production dependency install:
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan migrate --seed --force
php artisan storage:link
php artisan config:cache
php artisan view:cacheIf you change .env, routes, config, or views after caching, clear/rebuild the relevant cache:
php artisan optimize:clear
php artisan config:cache
php artisan view:cacheAfter running migrate:fresh --seed, the following accounts are available:
| Role | Username | Password | Notes | |
|---|---|---|---|---|
| Admin | admin |
admin@example.com |
password |
Full development admin access. |
| Moderator | moderator |
moderator@example.com |
password |
Admin-like access, except protected Admin role management. |
| Lecturers | (randomly generated) | (randomly generated) | password |
Login as Admin first, then open the lecturer/user list to see generated lecturer emails. |
| Seeded Classreps | classrep1, classrep2, etc. |
classrep1@example.com, classrep2@example.com, etc. |
password |
Created only by the local development seeder. |
The login form accepts email, username, or phone number.
When a Classrep is appointed manually from a student record in the classroom edit screen, the system creates a new user account with the student's email if one does not already exist. The default password for that newly created Classrep account is:
password123
β οΈ Warning: Change all passwords immediately in a production environment.
| Feature | Admin | Moderator | Lecturer (Manager) | Lecturer (Teacher) | Classrep |
|---|---|---|---|---|---|
| Full System Access | β | β | β | β | β |
| Manage Courses | β | β | β | β | β |
| Manage Classes | β | β | β (Own Course) | β | β |
| Manage Subjects | β | β | β (Own Course) | β | β |
| Manage Students | β | β | β (Own Course) | β | β |
| Record Attendance | β | β | β (Own Course) | β (Own Subject) | β (Own Class) |
| Edit Attendance | β | β | β (Own Course) | β (Own Subject) | β |
# Run all 47 tests
php artisan test
# Run a specific test file
php artisan test --filter LecturerTest
# Run tests with detailed output
php artisan test --verboseschool/
βββ app/
β βββ Http/
β β βββ Controllers/ # Resource controllers (User, Teacher, Student, etc.)
β β βββ Middleware/ # RoleMiddleware for route protection
β βββ Models/ # Eloquent models (User, Role, Student, Classroom, etc.)
β βββ Policies/ # UserPolicy for fine-grained authorization
βββ database/
β βββ factories/ # Model factories for testing & seeding
β βββ migrations/ # Database schema migrations
β βββ seeders/
β βββ Dev/ # Development seeder (with dummy data)
β βββ RoleSeeder.php # Seeds the 5 default roles
β βββ SettingSeeder.php # Seeds default branding settings
βββ public/
β βββ images/ # Static assets (default.jpg avatar, hero image)
βββ resources/
β βββ js/
β βββ Layouts/ # AuthenticatedLayout, GuestLayout
β βββ Pages/ # Inertia React pages (Dashboard, Users, Teachers, etc.)
βββ routes/
β βββ web.php # All application routes with role middleware
βββ tests/
βββ Feature/ # PHPUnit feature tests
| Variable | Description | Default |
|---|---|---|
APP_NAME |
Application name | EduFlow |
APP_ENV |
Environment (local, production) |
local |
APP_URL |
Application base URL | http://localhost |
DB_CONNECTION |
Database driver (mysql, sqlite) |
mysql |
DB_DATABASE |
Database name | school |
QUEUE_CONNECTION |
Queue driver for queued jobs/notifications | database |
MAIL_MAILER |
Mail driver for notifications (log, smtp, or provider driver) |
log |
MAIL_HOST |
SMTP host for production email | 127.0.0.1 |
MAIL_PORT |
SMTP port | 2525 |
MAIL_SCHEME |
SMTP scheme, usually tls for port 587 or ssl for port 465 |
null |
MAIL_USERNAME |
SMTP username | null |
MAIL_PASSWORD |
SMTP password | null |
MAIL_FROM_ADDRESS |
Sender email address | hello@example.com |
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new feature branch:
git checkout -b feature/your-feature-name - Make your changes and add tests where applicable.
- Ensure all tests pass:
php artisan test - Commit your changes:
git commit -m 'feat: add your feature' - Push to your branch:
git push origin feature/your-feature-name - Open a Pull Request.
Please follow PSR-12 coding standards for PHP code.
This project is open-source and licensed under the MIT License.
MIT License
Copyright (c) 2026 EduFlow Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Built with β€οΈ using Laravel & React