Laravel / Vue.js / Inertia.js

Teamo Digital Solutions — Company Management Platform

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.


Teamo Digital Solutions — Company Management Platform screenshot

Key Features

73 features built into this project

Four-tier role hierarchy: developer
superadmin
admin
and employee — each with dedicated dashboard
route group
and sidebar
Dynamic database-driven menu system: menu items and role permissions stored in the database
assembled into a cached hierarchical tree by MenuService
injected into every page via Inertia shared data
Monolithic SPA architecture: Laravel 12 backend with Vue.js 3 and Inertia.js — no page reloads
no separate API contract
Clock-in / clock-out attendance tracking with break start/end
total break time
working-hour calculation
and session history stored as JSON
Leave request workflow: employee submits with leave type
dates
reason
and attachment — superadmin or admin approves or declines with notes
Task assignment: admins assign tasks with type (multi-select)
priority
deadline
files-assigned list
project name
and department
Employee work-report submission: files worked on
files assigned
goals met
issues encountered
collaborators
and unfinished tasks — reviewed and approved by admin
Analytics dashboard with PDF export (DomPDF)
Excel/CSV export (Maatwebsite)
email delivery
and schedulable recurring reports
Spatie Permission RBAC: roles and permissions stored in database
synced via AccessControl panel
enforced by CheckRole middleware with a developer > superadmin > admin > employee hierarchy
Spatie ActivityLog automatic audit trail: all model creates
updates
and deletes logged with user attribution
Laravel Jetstream + Fortify authentication with two-factor authentication (TOTP)
browser session management
and API token management
Role switching: users with multiple roles can switch active role from the dashboard without re-logging
Public-facing Blade website: homepage
services
about
contact
gallery
careers/job vacancies
and blog — all managed from the admin panel
Blog with full SEO: slug-based URLs
JSON-LD schema markup
meta title/description
Open Graph tags
canonical URLs
reading time
and dynamic XML sitemap
Job vacancy and application management: post vacancies
accept public applications with CV upload
and track status from the admin panel
Login activity logging: every login attempt recorded with IP address
browser
device type
platform
and failure reason
Gmail SMTP email delivery for notifications
password resets
and scheduled report delivery
User import/export (Excel)
bulk actions
and full audit log for developer and superadmin roles

Challenges & Solutions

Technical problems encountered during development and how each was resolved.

1

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.

2

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.

3

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.

4

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.