Overview
Polyboy started as a landing page for a classic barbershop in Zagreb, Croatia, and quickly grew into a custom CRM with bookings, customer management and automated emails. That first version proved the idea. Version 2 is a rebuild with a bigger goal: turn a system made for one shop into a product that any salon can run - barbers, hair studios, beauty salons - without code changes.
Version 1: One Barbershop
The first version was built for a single shop. Next.js and shadcn/ui gave it a modern public site, and behind it sat a CRM for bookings, customers, contact form submissions and a statistics dashboard.
Within 3 months of launch, the user database grew from 800 initial users to over 1,200 - a 50% increase driven by the better booking experience and a more reliable system.
Why it matters:
The numbers showed the core worked: people book more when booking is easy. But the code assumed one business everywhere - one address, one schedule, one price list, names and contact details hard-coded into pages and emails. Selling it to a second salon would have meant forking it.
Version 2: From Shop to Product
v2 removes those assumptions. The salon’s name, legal name, email, phone and address (in Croatian and English) live in a business profile that is edited in the admin and used across the public site, emails and legal pages. The color theme can be set per deployment through an environment variable.
Parts that existed only for the original shop were taken out with migrations that move the data first and drop the old columns after, so existing bookings and history survive the change.
Why it matters:
A product has to be configured, not rewritten. Every salon-specific value that moves from code into the database is one less reason to touch the code for the next customer.
Multiple Locations
A business can now run several salons from one site. Each location has its own address, contact details, opening hours, team, photos and price list. With only one active location, the site simply behaves like a single-salon site.
Booking follows the same model: the customer picks a salon, then a staff member who works there, then a free slot. Slots are derived from that location’s opening hours, and the API rejects a booking if the chosen person does not work at that location. Staff can be assigned to more than one salon.

Why it matters:
Growing salons open a second chair, then a second shop. Building locations into the data model from the start means the product grows with the customer instead of hitting a wall.
Pricing That Matches How Salons Work
The price menu supports categories, combos (a base service plus an add-on, such as a haircut with a beard trim), discounts, premium prices and per-staff price overrides that fall back to the menu price when not set. The menu can be reordered by drag and drop.
Every booking stores a snapshot of the price at the moment it was made, so editing the menu later never rewrites history or statistics.
For Croatian price rules, each service shows its reference price with the date it applies from, and the system publishes machine-readable CSV price lists for every salon and for the webshop. A nightly cron job republishes them, and previous lists stay publicly available for 30 days.
Why it matters:
Legal requirements are easy to forget until an inspection. Generating the compliant lists from the same data that powers the menu means they are never out of date.
Webshop with Stripe
v2 adds a webshop with product groups, stock, brands, net quantities and unit prices per litre or kilogram. Checkout runs through Stripe Checkout, and a Stripe webhook turns completed payments into orders that customers can see in their account and admins can manage. The shop can be switched on or off from the admin and stays hidden until Stripe is configured.
Admin, Roles and Permissions
The admin covers bookings (with a calendar view), customers, staff, locations, prices, products, orders, inquiries, blogs, gallery, academy courses and statistics. There are three roles - user, admin and super admin - and a super admin can decide which admin sections each role can see. Admins can issue warnings and bans, which are checked by every relevant API route, and super admins can post notifications that show up in the admin.
Content, Academy and Two Languages
Everything public is bilingual (Croatian and English), including blog posts with their own SEO fields per language. The academy section lets a salon publish courses with a custom application questionnaire built in the admin from eight question types (text, number, date, email, phone, radio, checkbox, long text), plus course reviews.
Emails and Reliability
Transactional emails are sent with Resend and built with React Email: account verification, password reset, booking confirmation, reminders, cancellations, warnings and replies to contact messages. Bookings come with a calendar file (.ics). A Resend webhook flags addresses that bounce, complain or are suppressed, so the admin knows when a customer is not getting emails.
Scheduled jobs on Vercel send daily booking reminders, mark past bookings as completed and republish price lists. Customers can log in with email or phone number and a password, or with Google.
Type Safety with Zod
As in v1, Zod validates data on both the frontend and the backend. In v2 the Zod schemas are generated from the Prisma schema, and environment variables are validated at startup, so a missing secret fails the build instead of a request in production.
Why it matters:
When the database, the API and the forms share one source of truth, a change to the data model shows up as a type error everywhere it matters.
Tech Stack
| Category | Technology |
|---|---|
| Framework | Next.js (App Router), React |
| Language | TypeScript |
| UI | shadcn/ui, Tailwind CSS |
| Data | Prisma, PostgreSQL, TanStack Query |
| Auth | NextAuth (credentials, Google) |
| Validation | Zod, zod-prisma-types |
| i18n | next-intl (Croatian, English) |
| Payments | Stripe |
| Resend, React Email | |
| Hosting | Vercel (crons, Blob storage) |
Key Takeaways
- A version for one customer is the best research for a product
- Move every business-specific value from code into data
- Model locations, roles and prices for growth from day one
- Snapshot what you charged - never recompute history from today’s prices
- Build legal requirements into the system, not into a checklist