A full-stack internal company management system built for Teamo Digital Solutions using Laravel 12 on the backend and Vue.js 3 with Inertia.js on the frontend. The architecture is a monolithic SPA: server-side Laravel handles all data, authentication, and business logic while Inertia.js passes page props directly to Vue components — no separate API contract required, no page reloads.
The system operates on a strict four-tier role hierarchy — developer, superadmin, admin, and employee — each with its own dedicated dashboard, sidebar, and route group. Navigation is powered by a fully database-driven dynamic menu system: admins create menu items in the database, assign them to roles via a pivot table, and the MenuService assembles a hierarchical tree at request time (cached for 900 seconds), which Inertia then injects into every page as shared data without any hard-coded nav lists in the codebase.
The HR and operations layer covers the complete employee lifecycle: clock-in/clock-out attendance with break tracking and working-hour calculation, leave requests across annual, sick, maternity, and other types with a multi-step approval workflow, task assignment where admins assign multi-type tasks with deadlines and a files-assigned list, and employee work-report submission where staff log files worked on, goals met, issues encountered, and collaborators — which admins then review and approve. Analytics reports can be exported as PDF or Excel, emailed directly, or scheduled for recurring delivery. A companion public-facing website (rendered in Blade) presents the company services, blog with full SEO, careers/job vacancies, image gallery, and a newsletter subscription, all managed from within the same admin panel.
73 features built into this project
Technical problems encountered during development and how each was resolved.
The biggest architectural challenge was building the dynamic menu system. Every other company management system I had seen hard-coded navigation in a config file or in the Blade/Vue template, meaning adding a new section required a code deploy. I moved the entire menu into the database — each item has a route, icon, parent (for nesting), sort order, and a pivot table mapping it to roles. The MenuService builds the tree recursively, caches the result per role for 900 seconds, and regenerates it on any menu change. Inertia's HandleInertiaRequests middleware injects the built tree into every page as shared props, so Vue components receive a fully-resolved menu without a single hard-coded nav link in the frontend.
Integrating Inertia.js with Laravel Jetstream's authentication scaffold required careful middleware ordering. Jetstream generates Blade-based auth views by default, but every authenticated page needed to be a Vue component served through Inertia. I rewired the post-login redirect through a custom LoginResponse class that detects the user's primary role and returns the correct Inertia page rather than a Blade redirect, while keeping Fortify's CSRF and 2FA pipeline fully intact.
The task and work-report system needed to model a relationship that does not map cleanly to standard CRUD: an admin assigns files to an employee, the employee later logs which of those files they actually worked on alongside notes, and the admin reviews the diff. I designed the assign_tasks table to hold both files_assigned (what the admin specified) and files_worked_on (what the employee reported) as JSON arrays in the same row, with a reviewed_by_admin_id and reviewed_at for the approval step — keeping the full assignment-to-completion lifecycle in one queryable record.
Building a public-facing SEO blog inside the same Laravel application as the internal HR dashboard required keeping the two concerns completely separate. The public blog is rendered with Blade for fast server-side HTML (important for search engine indexing), while the internal dashboard is a Vue SPA delivered by Inertia. I wrote a Blog model method that generates JSON-LD Article schema, canonical URLs, and Open Graph tags from stored fields, and a SitemapController that dynamically queries published posts and services to produce a valid sitemap.xml — ensuring new content is indexed without any manual update.