An enterprise-grade, production-ready salon management platform engineered specifically for the Indian salon industry. Salvio replaces notebooks, spreadsheets, and fragmented tools with an integrated system featuring billing automation, GST-compliant email invoicing, a multi-tier commission engine, 12 specialized financial reports, multi-salon management, a 4-role RBAC system, in-app support tickets, and a full admin panel — built with Next.js 14, PostgreSQL, Prisma ORM, NextAuth.js, Recharts, and Zod.

The Indian salon industry is enormous — millions of independent salons, barbershops, and beauty parlours operating daily across every city and town in the country. And yet, the vast majority of them are run on the same three tools: a notebook for appointments, a calculator for bills, and a mental ledger for employee commissions. The result is financial opacity, commission disputes, no customer data, and zero visibility into what the business is actually doing.
Salvio is my answer to that problem. It's a full-stack, enterprise-grade salon management platform built specifically for the Indian salon industry — designed to replace every fragmented tool a salon owner currently uses with a single, integrated system that handles billing, employee management, commission calculation, financial analytics, customer intelligence, GST compliance, and multi-location management in one place.
This is the most architecturally complex and feature-complete project I've built. It has four distinct user roles with completely separate interfaces, a commission engine that supports multiple compensation models simultaneously, twelve specialized financial report types, a GST-compliant email invoicing system, a full in-app support ticket system with priority tracking, and a platform-wide admin panel. It's not a CRUD app with a dashboard — it's a SaaS product.
Most salon management software available in India falls into one of two categories: it's either too expensive and complex for small-to-medium salon owners to adopt, or it's too basic to handle the real operational complexity of running a salon with multiple employees, variable commission structures, and multiple service types. The specific pain points I designed Salvio to solve are:
useState for form state, useEffect for side effects and subscriptions, useCallback for memoized handlers in complex forms, useRef for imperative DOM operations, and custom hooks for shared logic like data fetching patterns.groupBy, aggregate, and raw query escape hatches for performance-critical aggregations./^[6-9]\d{9}$/ mobile regex, 6-digit postal code validation, and 3-character alphanumeric salon code enforcement.ResponsiveContainer.#6d28d9) as the primary accent with complementary purple and a full neutral gray ramp. CSS custom properties handle theme tokens that can't be expressed as Tailwind utilities.Salvio has four completely distinct user types, each with their own authentication flow, session structure, UI layout, and permission set. This is Role-Based Access Control implemented at every layer of the stack — not just frontend route guards, but server-side validation on every API request.
The salon business proprietor. Owners have the most expansive access in the system. They can manage one or multiple salon locations, create and manage employees, configure services and commission rules, generate all financial reports, create and void bills, manage expenses, and view aggregated analytics across all their salons. Owners see the full sidebar with dashboard, billing, reports, employees, customers, services, expenses, commissions, and settings modules. The owner experience is designed around the question: "what does my business look like today, this week, and this month?"
A senior employee with elevated permissions within a specific salon. Managers can view employee performance data, generate reports for their assigned salon, and access customer information — but cannot modify commission configurations, create services, or access financial settings. This role exists because many salons have a floor manager who needs operational visibility without financial access.
The standard salon staff role — stylists, barbers, beauticians. Employees get a simplified interface showing their personal sales, commission earnings, customer interactions, and performance metrics. They cannot see other employees' data, cannot modify salon configuration, and cannot access financial reports. The employee dashboard is designed to answer: "how much have I earned today, and how am I tracking against my targets?" Commission visibility is a key feature — employees can see exactly how their commissions are calculated, which eliminates one of the most common friction points in salon employment.
The platform administrator role — used for Salvio's own operations team. Admins have platform-wide visibility: all salons, all owners, all users. They can activate or deactivate accounts, manage support tickets, post platform-wide announcements, review session audit logs, and monitor system health. The admin interface is a full internal operations tool, not just a settings panel.
Authentication in Salvio is significantly more complex than a typical single-role application because three completely different user types share the same platform but have different login flows, session structures, and password management paths.
NextAuth.js is configured with three CredentialsProviders — one for each role. When a user submits the login form, the request is routed to the appropriate provider based on the selected role. Each provider queries the correct database table, verifies the password with bcryptjs, and returns a session payload containing the user's ID, role, name, email, and associated salon ID (where applicable).
JWT-based sessions encode the role into the token, which is validated on every protected API request via NextAuth's getServerSession() call. The role in the session token determines which data the API returns — an employee hitting a report endpoint gets only their own data, while an owner gets salon-wide data. This isn't just frontend route guarding; it's enforced at the API layer on every request.
Owner registration uses a full email OTP verification flow. After entering their details, a 6-digit OTP is sent to their email via Nodemailer. The OTP is stored in the OtpTokens table with an expiration timestamp. Verification checks both the code match and the expiration, then activates the account and sends a welcome email.
Employees don't self-register. The owner creates employee accounts from the admin panel, and the employee receives an invitation email with a link to set their own password. This invitation flow uses a secure token stored in PasswordResetTokens with a short expiration window. Until an employee sets their password, the account shows "awaiting approval" status in the owner's employee management panel.
Password reset uses the same token mechanism — a unique, time-limited token is emailed to the user, and the reset form validates the token before allowing the password change. All tokens are single-use and deleted after consumption.
The billing module is the operational heart of Salvio — the feature that salon staff interact with dozens of times per day. It's designed for speed and accuracy: create a complete bill with multiple services, multiple employees, a discount, and a tip in under 60 seconds.
The bill creation interface follows a linear flow designed to match how salon staff actually think about a transaction:
On save, the system calculates the final bill total, generates a bill number in the salon's serial sequence, creates all commission records for each employee-service pair, updates the customer's visit history, and optionally sends the GST invoice to the customer's email — all in a single database transaction.
Indian salon services are subject to 18% GST (9% CGST + 9% SGST). Salvio implements GST as inclusive — the service prices shown to customers already include GST, and the invoice breaks down the implied tax component:
taxableValue = amount / 1.18
cgst = taxableValue * 0.09 // 9%
sgst = taxableValue * 0.09 // 9%
total = taxableValue + cgst + sgst = amount
The generated invoice includes the salon's GST number, the SAC code for salon services (996311), the full CGST/SGST breakdown, and the amount in words using Indian numbering conventions (Crores, Lakhs, Thousands). This level of invoice detail is what's expected for GST compliance in India, and it's completely automated — the owner doesn't need to understand the tax calculation to generate a compliant invoice.
When a bill is created with a customer email on file (or provided at billing time), Salvio sends a professional HTML email invoice. The email template is fully custom HTML with inline CSS for maximum email client compatibility — it renders correctly on Gmail, Outlook, Apple Mail, and mobile email clients. The invoice includes the salon name and address, customer details, service line items with individual pricing, discount calculation, tip, GST breakdown with SAC code, payment mode, and a unique bill reference number. The email is sent asynchronously via Nodemailer so it doesn't block the billing flow.
Every bill has a public URL accessible without authentication — /api/public/bill/[id] — allowing customers to view their bill details online after receiving it via SMS or WhatsApp link. This URL is designed for sharing in messaging apps and renders a clean, mobile-optimized bill view.
After creation, bills can be edited or voided with a full audit trail. Bill modifications track what changed, who changed it, and when. Void bills are preserved in the database with a voided status rather than deleted — financial records should never be deleted, only marked as void. The bill history is paginated and searchable by customer name, mobile, date range, and payment mode.
The commission system is the most technically sophisticated feature in Salvio, and it's the feature that most directly impacts employee trust and satisfaction. I designed it to handle the full range of compensation models that Indian salons actually use — from simple flat-percentage commissions to complex multi-tier performance bonuses.
The baseline commission model. Each service has a default commission percentage. Each employee can have a custom commission override for specific services. The calculation on every bill item is:
// Lookup priority:
1. Employee-specific override for this service (commission_rules table)
2. Service's default commission percentage
3. Salon's global default commission percentage
commission = billItem.price * (commissionPct / 100)
This three-level priority system means an owner can set a salon-wide default, override it per service, and further override it per employee — with the most specific rule always winning. It's flexible enough to handle situations like "this stylist negotiated a higher commission on color services specifically" without needing to touch every other service or employee's configuration.
Performance-based compensation tiers. An owner configures performance targets (daily, weekly, or monthly) with different commission percentages at each tier. Employees who hit higher targets earn higher commission rates. Salvio supports three distinct calculation modes:
Each tier can also include a bonus amount — a fixed cash reward for reaching that tier, added on top of the percentage commission. This is common in Indian salon culture where exceeding a monthly target earns a bonus payment.
Beyond pure commission structures, Salvio supports salary-based and hybrid compensation:
The payout module lets owners calculate exactly how much commission is owed to each employee for any date range. The calculation aggregates all bill item commissions for the period, subtracts any commissions already paid out in previous payouts, and shows the net amount due. Payout records are stored in the CommissionPayouts table with the date range, amount, and timestamp — creating a permanent record of every commission payment for dispute resolution.
Salvio has twelve distinct report types, each answering a specific business question. All reports are date-filtered (with calendar date range pickers), paginated where needed, and optimized for large datasets using Prisma aggregations rather than loading all records into memory.
The most-used report. Shows total revenue for the selected date range, broken down by payment mode (Cash / UPI / Card). Includes number of bills, average transaction value, and total tip collected. Designed to be the first thing an owner checks at the end of each day — "how much did we make today, and how did customers pay?"
Dedicated analysis of Cash vs UPI vs Card payment distribution. Shows totals and percentages for each mode, plus trend over time. Important for salon owners who have cash flow concerns or who want to encourage digital payments for accounting purposes.
Top customers ranked by total spending across the selected date range. Shows each customer's visit count, total spent, average transaction value, most common services, and last visit date. This report transforms the customer database from a billing convenience into a business intelligence tool — identifying who the salon's most valuable customers are and whether they're still coming in regularly.
Repeat customer percentage analysis. Calculates what fraction of customers in a period are returning vs first-time visitors. Tracks the trend over time. A declining return rate is the earliest warning sign of a customer satisfaction problem, and this report surfaces that signal before it becomes visible in revenue numbers.
Individual employee revenue contribution and commission calculation for the selected period. Shows each employee's total sales, number of services performed, commission earned, and their share of total salon revenue. Owners use this report for performance reviews and commission payout preparation.
Daily earnings per team member with trend analysis. Unlike the employee performance report (which is cumulative for a period), team insights shows the day-by-day breakdown — useful for identifying which days of the week each employee is most productive and spotting patterns in team performance.
Revenue contribution by service type. Shows which services (haircut, color, treatment, etc.) generate the most revenue, which are performed most frequently, and which have the highest average transaction value. This report informs pricing decisions — if a service is high-frequency but low-revenue, it might be worth a price adjustment.
Revenue trajectory month-over-month. Line chart showing total revenue for each month in the selected period with percentage change indicators. The key report for identifying seasonal patterns — many Indian salons have strong peaks around festivals like Diwali, Navratri, and wedding season.
Profitability analysis. Plots revenue and expenses on the same chart, calculates gross margin, and shows net position for each period. Expenses are entered separately by category (supplies, rent, utilities, marketing, etc.) and matched against billing revenue to show actual business profitability rather than just top-line revenue.
Aggregated multi-salon dashboard for owners who manage multiple locations. Shows revenue, bills, and performance metrics for each salon side by side, plus totals across all locations. An owner with three salons gets a single view of the entire operation without switching between salon contexts.
Customer lifetime value ranking. Similar to the customer insights report but focused on all-time metrics rather than a date range — identifying the salon's absolute most valuable long-term customers for loyalty program targeting.
The customer database is built automatically from billing — every time a bill is created with a customer mobile number, Salvio either finds the existing customer record or creates a new one. This means the customer database grows organically without any separate data entry burden on staff.
Each customer record stores: name, mobile number, email, gender, first visit date, total visit count, total amount spent, average transaction value, and the full bill history. The mobile-based lookup on the billing screen means staff can find any customer in the database by typing the first few digits of their number — the search is real-time and instant.
Customer intelligence surfaces through the analytics reports: top spenders, return rate, churn indicators (customers who haven't returned in 60+ days), and service preferences. These insights are visible to owners and managers without any manual analysis — Salvio surfaces them automatically from the billing data.
Owners can create and manage multiple salon locations from a single account. Each salon has its own configuration: a 3-character alphanumeric code (used in bill numbering), address, phone numbers, GST number, default commission percentage, and service catalog.
Services are managed per salon — a service that exists at one location doesn't automatically exist at another. Each service has a name, price, duration (minimum 5 minutes), and a default commission percentage. The duration field powers future appointment scheduling features. Services can be created, edited, and deactivated — deactivated services no longer appear in the billing service picker but their historical data is preserved.
The multi-salon architecture uses salon-level scoping throughout the data model. Every bill, employee, service, customer interaction, and expense is tagged with a salonId. Queries are always filtered by salonId from the session context, ensuring complete data isolation between salon locations even for the same owner.
The admin panel is a full platform operations tool for Salvio's internal team. It's completely separate from the owner and employee interfaces — both visually and in terms of data access.
Admins can view every salon and salon owner on the platform, activate or deactivate accounts, inspect individual salon operations (employee lists, bill counts, service catalogs), and monitor account usage patterns. Account deactivation is a soft operation — the data is preserved, the owner simply can't log in until reactivated.
Admins can post platform-wide announcements visible to specific user roles. Announcements appear in the relevant dashboards — an announcement targeted at owners appears in the owner dashboard, not in employee interfaces. This system handles release notes, feature announcements, maintenance windows, and policy updates.
A complete in-app helpdesk. Users (owners and employees) can create support tickets from any page via a persistent help button. Tickets have a type (BUG / FEATURE_REQUEST / BILLING / ACCOUNT / OTHER), a priority level (LOW / MEDIUM / HIGH / URGENT), and a status (OPEN / IN_PROGRESS / RESOLVED / CLOSED). The ticket system uses a conversation model — both the user and admin can post multiple messages in a thread. After resolution, users can rate their support experience. Admins manage the full ticket queue from /admin/support-tickets with filtering by status, priority, and type.
Every login and logout event is recorded with the user's ID, role, IP address, user agent string, and timestamp. Admins can browse the full session audit log, filtered by user or date range. This is both a security feature (detecting unusual login patterns) and a compliance feature (demonstrating who accessed the system when).
The database schema is the foundation that makes the entire feature set possible. With 20+ tables, careful normalization, and strategic indexing, it's designed for both data integrity and query performance.
The financial data model centers on four tables: Bills (the transaction record), BillItems (individual services in a bill), CommissionRules (employee-specific commission overrides), and ThresholdTiers (performance tier configuration). These four tables, together with Employees, Services, and Salons, form the core financial engine.
Salons (id, name, code, address, gstNumber, defaultCommissionPct, ownerId)
└── Services (id, name, price, duration, commissionPct, salonId)
└── Employees (id, name, email, role, status, salonId)
└── CommissionRules (id, employeeId, serviceId, commissionPct)
└── ThresholdTiers (id, employeeId, target, period, commissionPct, mode, bonus)
└── CommissionPayouts (id, employeeId, fromDate, toDate, amount)
└── Customers (id, name, mobile, email, gender, salonId)
└── Bills (id, billNumber, customerId, salonId, paymentMode, discountPct, tip, total)
└── BillItems (id, billId, serviceId, employeeId, price, commissionPct, commission)
└── Expenses (id, salonId, category, amount, date, description)
Performance on financial queries depends heavily on indexes. Salvio's indexing strategy is designed around the most common query patterns:
The API is organized as RESTful route handlers in Next.js's App Router structure, grouped by domain. With 40+ endpoints, the routing structure mirrors the domain model: auth routes, salon routes, employee routes, bill routes, service routes, customer routes, expense routes, commission routes, payout routes, report routes (12 sub-routes), admin routes, and support ticket routes.
Every endpoint that accepts input validates it through the corresponding Zod schema before touching the database. Validation errors return structured error objects with field-level messages, not generic 400 responses — the frontend displays field-specific error messages directly under the relevant form inputs.
Auth-protected endpoints call getServerSession() at the start of every handler and return 401 immediately if no valid session exists. Role-restricted endpoints check the session role against the required role and return 403 if insufficient. This two-layer check (authenticated + authorized) is consistent across every protected endpoint.
Security in a financial application isn't optional — it's a core requirement. Salvio implements security at multiple layers.
At the authentication layer: bcrypt password hashing with 10 rounds, JWT sessions with expiration, OTP tokens with short expiration windows, single-use password reset tokens, and session audit logging with IP addresses. At the API layer: Zod validation on all inputs prevents malformed data from reaching the database, Prisma's parameterized queries prevent SQL injection at the driver level, and session validation on every request prevents unauthorized data access. At the database layer: foreign key constraints, non-negative constraints, and unique constraints enforce data integrity regardless of what the application layer sends.
The session audit log deserves specific mention as a security feature. Every login records the user's ID, role, IP address, user agent, and device information. This creates a permanent record that helps detect compromised accounts (unusual IP addresses), unauthorized access attempts, and provides an audit trail for compliance purposes. Admins can inspect any user's login history from the admin panel.
The frontend component architecture is organized into four major areas: auth components (login forms, OTP verifier, registration flows), layout components (sidebars, headers, breadcrumbs, navigation), feature components (billing interface, report pages, employee management), and shared UI components (tables, charts, modals, form inputs).
Because the four user roles have fundamentally different interfaces, Salvio uses a multi-layout architecture. The Owner Layout wraps every owner page with the full sidebar navigation (10+ modules), the breadcrumb system, and the notification area. The Employee Layout uses a simplified sidebar showing only the modules relevant to employees. The Admin Layout uses a different navigation structure entirely, optimized for platform management. The Auth Layout is a split-screen design with a branded hero panel on the left and the login/registration form on the right — no sidebar, no navigation.
These layouts are implemented as separate Next.js layout files in the App Router directory structure, meaning the correct layout is applied automatically based on the route — no conditional rendering or runtime role checking needed for layout selection.
The billing interface required several specialized form components that don't exist in standard component libraries:
Most management interfaces (bills list, employee list, customer list, expense list) use a consistent data table component with sorting (click column header), filtering (search input plus dropdown filters), and pagination. The table component is generic — it accepts column definitions and row data, making it reusable across every list view with consistent behavior and styling.
A financial application used multiple times per day needs to feel fast. Several performance decisions were made specifically to keep the application responsive under real operational load.
Report queries use Prisma's groupBy and aggregate functions rather than loading all records and aggregating in JavaScript. A daily sales report for a salon with 10,000 bills doesn't load 10,000 bill records — it fires a single SQL GROUP BY query and returns the aggregated totals. The difference between these approaches is the difference between 20ms and 20 seconds for large datasets.
Dynamic imports are used for heavy dependencies that aren't needed on initial load. JSZip is only loaded when a user clicks an export button. Chart components are lazy-loaded on report pages. This keeps the initial JavaScript bundle small and the first meaningful paint fast.
SWR's stale-while-revalidate caching strategy keeps dashboard data feeling live without unnecessary API calls. When a user navigates back to the dashboard after visiting another page, SWR immediately shows the cached data (no loading spinner) and silently revalidates in the background, updating the display only if the data has changed.
The billing form uses controlled inputs only where real-time validation feedback is needed (customer mobile, discount percentage). Service items use defaultValue with refs for fields that don't need real-time validation — this reduces re-renders on the most complex and frequently used form in the application.
Salvio is designed explicitly for the Indian market, and several features reflect that specificity rather than being generic SaaS defaults.
/^[6-9]\d{9}$/ enforces this at the API level, not just the frontend.Salvio is the project that taught me what it actually means to build software for a specific industry. Every feature decision was grounded in understanding how Indian salons actually operate — how commission disputes happen, why GST compliance matters, how employees think about their earnings, what questions an owner asks at the end of each day. The technical complexity of the commission engine, the 12 report types, and the 4-role RBAC system isn't complexity for its own sake — it reflects the real operational complexity of running a salon business.
Building Salvio end-to-end taught me database design for financial applications (where data integrity is non-negotiable), multi-role authentication architecture, performance optimization for aggregate queries, GST compliance requirements, email template engineering, and the difference between building features that technically work and building features that people actually want to use. It's the project I'm most proud of, and the one that best represents what I can build when I understand the problem deeply before writing a single line of code.
// Tech Stack