HarshPatel

Ahmedabad, Gujarat
Back to Projects
Next.jsReactPostgreSQLPrismaTailwind CSSNextAuth.js

Salvio

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.

May 202625 views📷 8 photos
Salvio 1
1 / 8

Introduction

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.

Why this project exists

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:

  • Commission disputes — when commissions are calculated manually at the end of the month, there's always room for error and mistrust. Salvio calculates commissions automatically on every bill, in real time, with full visibility for both owners and employees.
  • Financial opacity — salon owners have no easy way to know which services are most profitable, which employees generate the most revenue, which customers spend the most, or how today's revenue compares to last month. Salvio's 12 report types answer all of those questions with one click.
  • GST compliance — Indian salons are subject to GST on services, but most small salons handle this informally. Salvio generates fully GST-compliant invoices with CGST/SGST breakdown, SAC code, and taxable value calculations baked in.
  • Customer data loss — without a system, salons lose customer data every time a new notebook is started. Salvio builds a permanent customer database automatically from billing, tracking visit history, spending patterns, and return frequency.
  • No employee accountability — employees have no visibility into their own performance. Salvio gives every employee a personal dashboard showing their sales, commissions, and customer history in real time.

Tech stack

  • Next.js 14 (App Router) — the full application runs on Next.js with the App Router, using both Server Components and Client Components strategically. Server Components handle data-heavy report pages where no client interactivity is needed. Client Components handle the billing interface, real-time form validation, and interactive dashboards. Next.js API Route Handlers serve as the entire backend API layer.
  • React 18 — extensive use of hooks: 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.
  • PostgreSQL — the relational database. The data model has 20+ tables with proper foreign keys, cascading deletes, composite indexes, and unique constraints. PostgreSQL's relational integrity is essential for a financial application — incorrect commission calculations or orphaned bill records are unacceptable.
  • Prisma ORM — type-safe database access throughout. Prisma's migration system manages schema versioning with 8 tracked migrations. The generated Prisma Client provides TypeScript types for every database entity, eliminating an entire category of runtime errors from invalid field access. Complex report queries use Prisma's groupBy, aggregate, and raw query escape hatches for performance-critical aggregations.
  • NextAuth.js — authentication framework with three separate CredentialsProviders: Owner, Employee, and Admin. Each provider has its own database lookup, password verification, and session payload structure. JWT-based sessions include the user's role, salon association, and ID — available on every request without an additional database round-trip.
  • bcryptjs — all passwords are hashed with 10 salt rounds before storage. Plain text passwords never enter the database under any code path.
  • Zod — runtime schema validation on every API endpoint that accepts input. Zod schemas define exact shapes for bills, employees, services, salons, and commissions — with Indian-specific validation rules like the /^[6-9]\d{9}$/ mobile regex, 6-digit postal code validation, and 3-character alphanumeric salon code enforcement.
  • SWR — client-side data fetching with automatic revalidation. Used throughout the dashboard and report pages to keep displayed data fresh without manual refresh, with built-in loading and error states.
  • Recharts — data visualization for all analytics dashboards. Line charts for revenue trends, bar charts for payment mode breakdowns, area charts for cumulative performance, and composed charts for sales vs expenses comparisons. All charts are responsive via Recharts' ResponsiveContainer.
  • Tailwind CSS — utility-first styling with custom design tokens. The color system is built around violet (#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.
  • shadcn/ui (derivatives) — the component library foundation. Core primitives like Dialog, Sheet, Popover, Select, and Table are adapted from shadcn's architecture but customized for Salvio's design system and business requirements.
  • Sonner — toast notification system for user feedback on every significant operation: bill creation, commission update, employee approval, export completion, and error states.
  • Lucide React — icon library providing 400+ icons used throughout the UI for navigation items, status indicators, payment mode icons, action buttons, and metric cards.
  • Nodemailer / Custom SMTP — email delivery for GST-compliant HTML invoices, welcome emails, OTP verification, and password reset links.
  • JSZip (dynamic import) — loaded on demand for export operations, keeping the initial bundle lean.
  • Vercel — deployment and hosting with managed PostgreSQL or external provider (Railway, Supabase, or Neon).

User roles and access control

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.

Owner

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?"

Manager

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.

Employee

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.

Admin

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 system

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.

Registration and verification flow

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.

Billing and invoicing system

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.

Bill creation flow

The bill creation interface follows a linear flow designed to match how salon staff actually think about a transaction:

  • Customer lookup — staff enter the customer's mobile number. The system instantly searches the customer database and auto-fills the customer name and email if the customer exists. For new customers, the name is collected and the customer record is automatically created when the bill is saved. This makes customer database building completely frictionless — it happens as a side effect of normal billing, not as a separate data entry task.
  • Service selection — services are selected from the salon's configured service catalog. Each service line requires selecting which employee performed it. This per-service employee assignment is what powers the commission calculation — if three employees each performed one service, each gets their own commission from that bill.
  • Owner-operated services — if the salon owner personally performs a service, they can assign it to themselves and receive the commission accordingly. This is a common scenario in smaller salons where the owner is also a working stylist.
  • Discount and tip — a percentage discount is applied at the bill level (affecting the subtotal before tip). Tip is added as a flat amount, tracked separately for analytics (tips are not included in revenue calculations but are tracked for employee records).
  • Payment mode — Cash, UPI, or Card, each with a distinct icon. Payment mode is stored on every bill and used in the payment breakdown reports to track digital vs cash revenue.

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.

GST compliance

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.

Email invoicing

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.

Public bill view

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.

Bill management

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.

Commission engine

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.

Service-based commission

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.

Threshold-based commission

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:

  • PROGRESSIVE — commission is calculated at each tier rate for the portion of sales in that tier, and the amounts are summed. Like a tax bracket system — you earn tier-1 rates on your first ₹10,000 of sales, tier-2 rates on the next ₹10,000, and so on. This is the most employee-friendly model because every rupee of sales earns commission at the rate for that tier.
  • TIER_LOCKED — once a tier is reached, that tier's rate applies to the entire sales amount. If you hit ₹20,000 in sales and the tier-2 rate is 15%, all ₹20,000 earns 15%. This creates stronger incentives to hit the next tier.
  • HYBRID — progressive calculation for the base tiers, then a different (usually higher) rate applies to all sales above the top threshold. Used when owners want graduated tiers for normal performance but a special "star performer" rate for employees who significantly exceed targets.

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.

Salary models

Beyond pure commission structures, Salvio supports salary-based and hybrid compensation:

  • Fixed salary + commission — employee receives a fixed salary (daily, weekly, or monthly) plus percentage commission on services performed. Common for senior employees who have a guaranteed income floor.
  • Commission-only — pure percentage commission with no base salary. Standard for freelance-style stylists.
  • Pure salary — no commission, just a fixed payment. Used for reception and cleaning staff who don't perform billable services.

Commission payouts

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.

Financial analytics and reporting

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.

Daily sales report

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?"

Payment breakdown report

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.

Customer insights report

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.

Customer return rate report

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.

Employee performance report

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.

Team insights report

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.

Service performance report

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.

Monthly trend report

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.

Sales vs expenses report

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.

Owner performance report

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.

Top customers report

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.

Customer management

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.

Salon and service management

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.

Admin panel

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.

User and salon management

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.

Announcements

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.

Support ticket system

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.

Session audit logs

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).

Database design

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.

Core table structure

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)

Indexing strategy

Performance on financial queries depends heavily on indexes. Salvio's indexing strategy is designed around the most common query patterns:

  • salonId indexes on every table that has salon-scoped data — bills, employees, services, customers, expenses. These are the most-used filter in every query.
  • Composite index on (salonId, createdAt) on the Bills table — used by every date-range report query. Without this index, a monthly trend report on a salon with 50,000 bills would do a full table scan.
  • Composite index on (salonId, paymentMode, createdAt) — used by the payment breakdown report, which filters on both payment mode and date simultaneously.
  • Index on customerMobile — used by the real-time customer lookup on the billing screen, which needs sub-10ms response time.
  • Index on (employeeId, billId) on BillItems — used by the commission calculation queries that aggregate all items for an employee in a date range.

Data integrity constraints

  • Cascading deletes — deleting a salon cascades to its employees, bills, services, and customers. Deleting a bill cascades to its bill items and commission records. This prevents orphaned records that would corrupt financial calculations.
  • Unique constraints — employee email is unique per salon (not globally — the same email can work at two different salons). Customer mobile is unique per salon. Salon codes are globally unique.
  • Non-negative constraints on financial fields — price, commission, amount, and salary fields have database-level non-negative constraints, not just application-level validation.
  • Foreign key integrity — every relationship has a proper foreign key constraint, enforced at the database level. A bill item cannot reference a service that doesn't belong to the same salon.

API design

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 implementation

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.

UI and component architecture

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).

Multi-layout system

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.

Form components

The billing interface required several specialized form components that don't exist in standard component libraries:

  • Mobile number input with real-time customer lookup — debounced search that fires after 10 digits are entered, displays customer name if found, and gracefully handles the "new customer" case.
  • Service picker with employee assignment — a dynamic list where each row has a service selector and an employee selector. Rows can be added and removed. The service selector filters by the salon's service catalog. The employee selector shows only active employees at that salon.
  • OTP verifier — a 6-digit segmented input with auto-advance on each digit, backspace handling, paste support, and a resend countdown timer.
  • Commission tier builder — a dynamic form for building threshold tier configurations, with different fields shown based on the selected commission mode (PROGRESSIVE / TIER_LOCKED / HYBRID).
  • Date range picker — used on every report page, built on top of a calendar component with preset ranges (Today, This Week, This Month, Last Month, Last 90 Days, Custom).

Data tables

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.

Performance optimizations

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.

India-specific design decisions

Salvio is designed explicitly for the Indian market, and several features reflect that specificity rather than being generic SaaS defaults.

  • Mobile number validation — Indian mobile numbers start with 6, 7, 8, or 9 and are exactly 10 digits. The Zod schema /^[6-9]\d{9}$/ enforces this at the API level, not just the frontend.
  • GST compliance — 18% GST with CGST/SGST breakdown, SAC code 996311 for salon services, inclusive tax calculation (the listed price includes GST, the invoice shows the implied breakdown). This is exactly how Indian GST-registered businesses are required to invoice.
  • INR currency formatting — amounts are displayed in Indian numbering: Lakhs (₹1,00,000) and Crores (₹1,00,00,000), not the Western millions/billions. The amount-in-words feature on invoices uses Indian numbering (Twenty-Five Thousand Five Hundred, not Twenty-Five Thousand and Five Hundred).
  • Postal code validation — exactly 6 digits (Indian PIN codes are always 6 digits).
  • Payment modes — Cash, UPI, and Card map exactly to how Indian consumers pay. UPI (Unified Payments Interface — Google Pay, PhonePe, Paytm) is the most common digital payment method in India and is treated as a first-class payment mode alongside card and cash.
  • Commission culture — commission-based compensation is deeply embedded in Indian salon employment culture. The complexity of the commission engine isn't overengineering — it reflects the variety of real commission structures that Indian salon owners actually use and negotiate with employees.
  • Multi-location support — many successful Indian salon businesses are small chains of 2-5 locations owned by a single proprietor. Salvio's multi-salon architecture serves this market segment directly.

Project scale

  • 350+ total files across app, components, lib, prisma, and public directories
  • 40+ API endpoints organized across 12 domain routers
  • 20+ database tables with proper foreign keys, constraints, and indexes
  • 100+ reusable React components across auth, layout, feature, and UI layers
  • 4 distinct user roles with separate layouts, navigation, and permission sets
  • 12 specialized report types covering every dimension of salon business analytics
  • 8 Prisma migrations tracking the full schema evolution
  • 3 authentication credential providers for Owner, Employee, and Admin
  • 3 commission calculation modes (PROGRESSIVE, TIER_LOCKED, HYBRID)
  • 5+ Zod validation schemas with India-specific validation rules

What I'd build next

  • Appointment scheduling — a calendar-based booking system is the most requested missing feature. The service duration field is already in the schema in anticipation of this — it would plug directly into a time-slot allocation system.
  • WhatsApp integration — sending bills via WhatsApp (the dominant messaging app in India) instead of or in addition to email would dramatically increase invoice delivery rates. WhatsApp Business API integration via Twilio or official providers is the path.
  • Razorpay integration — online payment collection for pre-payments and deposits. Razorpay is the dominant Indian payment gateway and has a well-documented API.
  • Inventory management — salons sell retail products (shampoo, hair color, styling products) in addition to services. A stock tracking module for retail inventory would extend Salvio from a service business tool to a complete salon business platform.
  • AI churn prediction — the customer return rate data is already being collected. A machine learning layer that predicts which customers are at risk of churning (based on visit frequency decline) and surfaces them for targeted outreach would turn Salvio into a proactive business intelligence tool, not just a reporting one.
  • Mobile app — a React Native app for owners and employees would allow commission checking, customer lookup, and basic report viewing on mobile — important for salon owners who spend most of their time on the floor, not at a desk.
  • Loyalty program — a points-based reward system for customers, redeemable against future services. The customer visit data and billing history make this straightforward to implement on top of the existing data model.
  • Multi-language support — Hindi, Tamil, Telugu, and Kannada language interfaces would make Salvio accessible to salon owners and employees who are more comfortable in their regional language than in English.

Conclusion

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

Next.jsReactPostgreSQLPrismaTailwind CSSNextAuth.js