A full-featured PKL/Magang internship management system built for PT Global Intermedia Nusantara, handling the complete participant lifecycle from OTP-verified registration to final assessment and digital certificate generation.

This project was commissioned to solve a critical operational problem at PT Global Intermedia Nusantara (GI). The company manages dozens of PKL (*Praktik Kerja Lapangan*) and Magang (internship) participants from various universities and high schools every period. Previously, participant data, daily attendance, report submissions, and assessment scoring were handled manually using spreadsheets, printed forms, and scattered documents. There was no centralized system to track participant progress, making it difficult for admins to monitor attendance trends, evaluate performance, or generate certificates at the end of each period. Reports were submitted via WhatsApp messages or email, making them hard to organize and review systematically. The goal was to build a digital end-to-end platform that streamlines the entire participant management workflow while maintaining GPS-verified attendance, structured report reviews, and automated certificate generation.
GItrack is a Laravel 12 application with a Blade + Tailwind CSS + Alpine.js frontend and PostgreSQL database. The system uses a custom ID generator producing identifiers in the format `gi-{type}-{YYMM}-{XXX}`, dynamically regenerating participant IDs when their activity type (PKL/Magang) changes. OTP-based email verification handles registration and password resets with hashed codes and queue-driven delivery. The attendance module records GPS coordinates (latitude/longitude) at check-in and check-out, supports WFO/WFA work modes, and enforces business rules like check-in-before-check-out. The assessment engine allows admins to define per-participant scoring criteria with scores from 0–100, auto-calculating final grades with letter mappings (A through E). Certificates are generated using DomPDF with custom fonts and decorative borders, with auto-incrementing certificate numbers per year. A scheduled task runs daily at 1:00 AM to auto-archive expired participants and purge archives older than one month. The system also features an activity audit log, two-way feedback with star ratings, and partner institution management with logo upload.
Secure email OTP verification for registration and password reset. OTP codes are bcrypt-hashed before storage, delivered via queued email jobs, with a 5-minute expiry window. Includes username/email availability checks via AJAX and rate limiting (30 req/min) on auth endpoints.
Participants perform check-in and check-out with real GPS coordinates recorded at each action. Supports WFO (Work From Office) and WFA (Work From Anywhere) modes. Enforces business rules — check-in must precede check-out, and check-out auto-forces Hadir status. Duplicate prevention ensures one entry per type per day.
Participants submit daily activity reports with title, description, and PDF attachments (max 5MB for daily, 10MB for final). Reports follow a Dikirim → Disetujui / Revisi workflow. Admins review, approve, or request revisions with detailed notes. Final reports are limited to one per participant.
Admins define per-participant scoring criteria (e.g., Kedisiplinan, Keterampilan, Kerjasama, Inisiatif, Komunikasi) with individual scores 0–100. The system auto-calculates the final score as an average and maps it to a letter grade (A: 90+, B: 80+, C: 70+, D: 60+, E: <60). Criteria with existing scores are protected from deletion.
Generates beautiful PDF certificates with a decorative border design using DomPDF with custom Certificate.ttf font. Certificate numbers follow the format `No. {sequence}/{PKLorMAG}/PT.GIN/{romanMonth}/{year}` and auto-increment per year. Company info, signer name, and location are fully configurable.
An interactive dashboard with Chart.js visualizations showing attendance trends at hourly, daily, weekly, and monthly granularity. Displays statistics cards (Total PKL, Total Magang, Active, Completed), attendance breakdown (Hadir/Izin/Sakit), university vs school distribution, and latest feedback — all with AJAX-powered filtering and pagination.
Participants send feedback with optional 1–5 star ratings. Admins can reply directly within the system. Feedback marked as tampilkan (show) is displayed as public testimonials on the landing page carousel. Includes read/unread tracking for both sides.
A scheduled task runs daily at 1:00 AM to auto-archive participants past their end date. Archives older than 1 month are permanently purged along with all associated data (absences, reports, feedback, assessments, user accounts, and stored files). Admins can manually restore or delete archives.
Custom ID format `gi-{type}-{YYMM}-{XXX}` that auto-generates sequential identifiers based on participant type (PKL/Magang), registration date, and running number. The system dynamically regenerates the ID when a participant's `jenis_kegiatan` changes, updating both the `user` and `peserta` tables seamlessly.
Tracks every significant action with user ID, action type, target table, target record ID, and IP address. Provides full traceability for administrative actions across the entire system.
The email-based OTP system depends entirely on SMTP server availability. If the SMTP connection fails or is rate-limited by the provider (e.g., Gmail 535 errors), users are completely blocked from registering or resetting their passwords.
Implemented a database-driven queue to decouple OTP email delivery from the request lifecycle, allowing retries on failure. Added comprehensive error handling for SMTP authentication failures (535), connection timeouts, and mail server unavailability, presenting user-friendly error messages instead of generic 500 errors.
When an admin changes a participant's `jenis_kegiatan` (e.g., PKL → Magang), the system must regenerate the participant ID because the ID encodes the activity type. This requires updating both the `user` and `peserta` tables and re-authenticating the logged-in user session mid-request, which is a complex state mutation.
Handled via Eloquent boot events on the Peserta model. The `saved` event detects changes to `jenis_kegiatan`, calls `IdGenerator::generate()` for a new ID, updates both tables in a data-safe sequence, and re-login the user if they are the one being modified, ensuring session consistency without data integrity issues.
Admins could accidentally delete assessment criteria that already have scores assigned to them, leading to orphaned score records and corrupted assessment data. Standard soft deletes weren't sufficient because the related scores still referenced the deleted criteria.
Implemented a pre-delete validation rule (documented as BUG-7 in code comments) that checks for existing `penilaian_details` records linked to the criteria. If scores exist, the deletion is blocked and the admin is shown a clear message explaining that scores must be removed first. This is enforced at the controller level before the Eloquent destroy call.
The attendance analytics dashboard relies heavily on PostgreSQL-specific SQL functions like `EXTRACT(HOUR FROM ...)`, `TO_CHAR(...)`, and `EXTRACT(ISODOW FROM ...)` for grouping attendance data by hour, day, week, and month. These functions are not portable to MySQL or SQLite, making database migrations difficult.
The application was designed specifically for PostgreSQL (`DB_CONNECTION=pgsql`) and all raw SQL queries are written with PostgreSQL syntax. The `config/database.php` configuration includes fallback defaults for SQLite and MySQL, but the production environment is locked to PostgreSQL 18.3. The `.env.example` explicitly documents this dependency.
Many Blade views load Tailwind CSS, Font Awesome, Boxicons, and Chart.js from CDN links rather than bundling them through Vite. This creates a hard dependency on internet connectivity for the frontend to render correctly, and increases page load times compared to a purged, bundled build.
The project uses Vite with laravel-vite-plugin for local asset bundling (~37 entry points), but the CDN approach was retained for rapid prototyping and iteration during development. For production deployment, the plan is to migrate all CDN assets to the Vite build pipeline and enable Tailwind CSS purging to reduce the final CSS bundle size.







