suruosh
Alliya Massage Therapy Academy · April to October 2026

Alliya: from an AI-built site to a booking platform

Alliya Massage Therapy Academy, a clinic and training academy in Sweden, had a website made with an AI site builder. It looked finished, but it could not safely take bookings, follow Swedish consumer law or issue student certificates.

Case study11 min readalliyamassagetherapy.com

01 / 04The home page in the new design, top to bottom: a grey canvas, one white sheet, a jade gradient.

02 / 04The home page in dark: the same sheet, the jade deepened.

03 / 04The three treatments, each with its length, its price and a “Book Now” button.

04 / 04Contact: the form, beside the clinic's own address, phone and email.

Overview

Alliya Massage Therapy Academy is a clinic and training academy in Kista, Stockholm: massage, manual therapy and hijama for chronic pain, and its own certified levels for therapists in training.

Client
Alliya Massage Therapy Academy, Sweden
Role
Product, design, engineering and releases
Stack
React, Vite and Tailwind; Supabase for Postgres, authentication, storage and an edge function; Vercel for hosting, server functions and the daily job; i18next for the four languages
Timeline
April to October 2026, in two sprints
Languages
Swedish, English, Farsi, Arabic
From handover to releaseAlliya Massage Therapy Academy milestones, April to October 2026
  1. HandoverApr 4
  2. First sprint: repairsJul 20 to Jul 21
  3. Second sprint: the rebuildOct 3 to Oct 6
  4. Every screen testedOct 7

Some projects arrive all at once. This one came in seasons, alongside other work: a handover in spring, a first sprint of repairs in summer, and a second sprint in autumn that rebuilt it from the ground up. By the end I had turned it into a production system: a booking engine the database enforces, a legal pack built into the rules, certificates employers can verify with a QR code, and a site and admin in four languages.

A site that looked done

The founder built the first version by describing it to Hostinger Horizons, an AI website builder. In a short time it produced booking, an admin area, reviews and discounts in React with a Supabase database, and it looked like a finished product.

Taking it further was the problem. Every change was another prompt, and the results drifted. When the founder could not get what the academy needed out of it, I was brought in, and in April 2026 the exported code became my starting point.

01 / 02The first version's home page.

02 / 02Its booking page: one box for terms that weren't written yet, and Confirm Booking.

I didn't change it at first. I read the generated code and its database, worked out with the founder what the clinic and the academy needed, went through the Swedish consumer rules a booking has to follow, and let a design direction settle.

The first sprint went underneath. I fixed what was broken, from the brand's name, which had drifted into four versions across the code and the database, to a header that cut it short. It worked, but the design still felt old-school, and the clinic still wasn't getting what it wanted.

So the second sprint started from the ground up: a new layout, a stable system under it, and new features, from certificates with their own number and QR code, to a page that verifies them, to partner agencies the academy can add.

Before and after

Area AI-built site (April) Now (October)
Booking rules Checked in the browser; double bookings possible Enforced by the database; overlaps impossible
Data access Far wider than the site needed Visitors read only what the site shows; writes need a confirmed admin
Email Sent from the browser, by anyone Sent only by the server, once, retried on failure
Consumer law Not handled Withdrawal with Swedish holidays, terms, privacy, health-form consent
Texts per language 525 in English, 464 in the others 1,563, in all four languages
Database migrations 0 11, versioned in the repository
Automated tests 0 71, plus database test suites
Files in the repository 111 240

Securing the site, without taking it down

When I inspected the live database in October, the public website could reach far more than it needed. Everything else waited.

The fix went in first, so the old site kept working while the new one was built. A hardening pass followed, after a review of the fix itself, and a final lockdown came once the new booking site was live. Email moved to the server with that, and the contact form can only write to the clinic.

The hotfix and the hardening shipped with rollback scripts, and I checked each step against what a visitor can and cannot do.

A booking engine the database enforces

Quoting a price, listing free times and creating a booking are database functions now, and a constraint in the database makes overlapping bookings impossible. Each booking keeps its own duration, buffer and price, so editing the price list never rewrites history.

The booking page in light: Wellness Massage selected at 999 SEK, a strip of dates with Monday 12 October chosen, 10:00 picked among the free times, and a Your booking summary with the price filled in. Nothing is submitted.

01 / 02Booking. The database lists the free days and times and works out the price.

The same booking page in dark: Wellness Massage selected, Monday 12 October, 10:00 picked, and the summary showing 999 SEK.

02 / 02Booking in dark, with 10:00 and the price still in the summary.

Swedish consumer law is part of the rules. The 14-day withdrawal period ends on the right working day, past weekends and Swedish public holidays. There is an "Ångra bokning" withdrawal form, a health form with its own consent that deletes itself on schedule, and payment by wellness allowance (Epassi, Benifex) next to paying at the clinic.

The withdrawal page: a jade banner with the eyebrow ÅNGRA BOKNING, the title Withdraw a booking and the line You can withdraw a booking free of charge within 14 days of making it, until the treatment starts; below, fields for booking reference, email and name, and a black Confirm withdrawal button.
“Ångra bokning”: a booking reference, an email and a name are all it asks for.

Every booking change goes through the server, a ledger makes sure no email goes out twice, and one daily job retries failures and catches anything missed. One job, because the client's free hosting plan allows one a day.

Anything checked only in the browser can be skipped. Every booking now passes through the database.

An admin for the whole business

Behind the site sits a custom admin panel, and it reaches every part of the business: bookings, services and opening hours, customers, discounts, training videos, certificates and agencies, who has access, the website's own words and photos, and every email the system sends. It wears the same design as the public site, in light and in dark.

01 / 02Opening hours and the booking rules, each one explained in a line.

02 / 02Opening hours in dark. The rules read the same.

The clinic can read the booking rules here: a buffer between sessions, how much notice a booking needs, how far ahead customers can book, how long health forms are kept. A change applies to new bookings. The ones already made keep the terms they were booked with.

The clinic can change its own website here too. The Frontend editor lists what the site really shows, page by page, in each of the four languages, the terms and privacy policy included.

The Frontend editor: the site's words and photos, in Swedish, English, Farsi and Arabic.

Every booking email has its own template and its own switch: confirmations, reminders, receipts, cancellations, review requests. Saving is honest now, too. An audit found saves the database had quietly refused while the screen said "saved"; every write now checks that something actually changed.

The Email Templates page: nine cards, from Admin cancellation and Booking confirmation to Receipt and Review request, each with its subject line, an Active switch and an Edit button.
Nine emails the system sends, each with its own template and its own switch.

On a phone the tables become cards.

Admin bookings on a phone as cards with labelled fields: customer, service, date and time, price and payment, and a status select.
Admin bookings on a phone (demo data).
The same admin booking cards in Arabic, right to left, with phone numbers, prices and times still in reading order.
The same screen in Arabic, right to left.

Certificates employers can verify, and nobody can quietly change

From a client brief written in Farsi, I built the certificate system: one fixed design for Level 1 Foundation, Level 2 Advanced and Level 3 Advanced Clinical, plus an Authorized Representative certificate for partner agencies. Agencies are added in the admin, and a public page lists the academy's representatives by country.

Every certificate gets a number like ALLIYA-2026-L1-0001 and a QR code that opens its verification page: active, revoked or expired. Issued certificates cannot be edited or deleted. That was the client's brief. A mistake is fixed by a corrected reissue, and the old one stays on record, linked to the new one.

Privacy is part of verification. Typing the number of a student's certificate shows only their first name and initials; the QR code on the paper shows the full certificate while it is valid; and an admin can erase a person's data on request. The seal and lotus take the level's medal colour, so the level reads at a glance.

The Verify a certificate page: a jade panel with a number field showing ALLIYA-2026-L1-0001 and a Verify button, then three cards for A unique number, A QR code and A live status.
The verification page: enter the number, or scan the code on the paper.
The lotus emblem and the round seal in bronze for Level 1, silver for Level 2, and gold for Level 3 and the Representative certificate.
Bronze, silver and gold by level. Everything else keeps the design's gold.
A Level 1 Foundation certificate with demo data, filled to the longest values the form allows: a details column, a QR code and a bronze seal.
A certificate with demo data, filled to the longest values the form allows.

A bug from the client's phone

When the client saved a certificate as a PDF on an iPhone, the title came out as a solid brown bar. The gold headings were drawn with a CSS trick that clips a gradient to the letters, and Safari's print engine does not support it. The round seal, drawn as SVG, had printed perfectly in the same file. I redrew the headings as SVG text at exactly the same positions, and changed the print version to white paper instead of the screen's dark frame.

Stress-testing every screen, in four languages

The client reported a language menu cut off on a phone. Rather than fix one menu, I tested everything. I built a browser test that runs the real site against a fake copy of the database, so no live data is touched. It loads every page at widths from 320 to 1280 pixels, in all four languages, in light and dark mode. It opens every menu and dialog, fills in the forms, feeds in oversized, empty and failing data, and walks each page with the keyboard alone. Two independent code reviews added what a browser cannot see.

Check Before After
Content off-screen or cut off 1,400+ 0
Menus and popups off-screen 1,000+ 0
Text below contrast standards 230+ 0
Keyboard navigation problems 322 0

Then the same checks in WebKit, the engine behind Safari on iPhone, on the production build: 112 scenarios across iPhone 13 and iPhone SE sizes, in English and Arabic, with no issues.

The mobile menu with the language list running off the left edge of the screen.
Before. The reported bug: the language list runs off the screen.
The mobile menu with the language list fully on screen: English, Svenska, العربية and فارسی.
After. It now opens towards the side with room.

The fixes went well beyond the menu. Leaving a page with unsaved edits asks first, the browser's Back button included. An open tab reloads by itself after a new release instead of breaking. Unknown addresses get a proper page instead of a blank screen.

The 404 page: the site header, a jade panel reading 404 and Page not found, the line The page you were looking for does not exist or has moved, buttons for Go to the home page and Book a session, and the footer.
An unknown address gets a proper page, with a way home and a way to book.

Four languages, two directions

The clinic's customers and students speak Swedish, English, Farsi and Arabic, so the whole site and the whole admin come in all four, with Swedish as the binding version of the legal texts. Every one of the 1,563 texts exists in each language, with the same placeholders.

The booking terms in Swedish. Their last line: “Den svenska versionen gäller vid skillnader” (the Swedish version applies in case of differences).

Right-to-left layouts needed more than mirroring. English text on an Arabic or Farsi page keeps its own direction. Dates in Farsi showed a Gregorian day next to a Persian month, so 4 October read as "4 Mehr" when it is 12 Mehr. Dates, times, prices and phone numbers now stay in Latin digits and in reading order inside right-to-left text.

The mobile menu in Arabic, laid out right to left, with the language list open and on screen.
The mobile menu in Arabic.

Many mindsets, one system

In a project like this, the code is rarely the slow part. The slow part is listening, because everyone involved thinks in a different unit. An AI builder thinks in screens, since a screen is what a prompt gives back. The clinic thinks in sessions and care. The academy thinks in levels and proof. Swedish law thinks in days, records and consent.

The about page: a treatment on the couch, an arm being assessed, and the approach in the clinic's own words.

Much of my work was translation between them: finding where each way of thinking belongs in the system, and keeping any one of them from flattening the others. The booking rules live in the database, where they can't be skipped. The photography reads as care rather than pampering, because the academy teaches therapy. And the certificates never change, because verification is only worth something if they don't.

None of these were dramatic. Each was small, and each was the visible end of something larger. Following them is the curious part of the work. Fixing each one where it starts, not only where it shows, is what turns a site that looks finished into a system that holds.

The calls that shaped it

  • No "factory reset". My call: I removed the admin button that deleted every booking. Bookings are legal and bookkeeping records.
  • QR codes on the permanent domain. My call: a printed code can never be changed, so it points at the main domain rather than an old address from the design file.
  • No address required for no-shows. My call: instead of asking everyone for an address, the agreed policy, still to come, is a fee in the terms, tracking and a booking block for repeat no-shows. Less personal data for the same goal.
  • Stay on the free plan. The client's call: one scheduled job a day does reminders, review requests, retries and catch-up, and is safe to run twice.
  • A calm, clinical look. My call: a grey canvas, a white sheet and a jade gradient kept at 4.5:1 contrast for white text, with clinical photography instead of spa imagery.

Production discipline on a small project

The first site came from an AI builder, and it looked finished. What turned it into something a clinic can run on was the process around the code: clear requirements, independent review, tests, staged releases, and one person accountable for every change that reached production.

  • Every release is a pull request. Each change is described in plain language and merged by me. Merging deploys to production, so a merge is a release: 14 of them so far.
  • Database changes as migrations. Versioned files in the repository. The security and certificate changes ship with rollback scripts, and the ones that must not run twice or out of order refuse to.
  • Tested before production. Migrations ran first on Postgres in WebAssembly against a model of the live schema, then as dry runs on the real database inside a transaction that is rolled back.
  • Nothing live without approval. No change to the live database without an explicit go-ahead, and read-only checks before and after each one.
  • Independent review. 48 findings fixed before the booking launch, and four parallel reviews before the certificates.
  • Seen in real browsers. Headless Chrome in every language, light and dark, phone and desktop, keyboard only, before and after the fixes; then WebKit on the production build.

Live, and ready for what comes next

The platform runs at www.alliyamassagetherapy.com: online booking with Swedish consumer law built in, server-sent email, an admin in four languages, and certificates with QR verification.

  • Next to build: a no-show policy with tracking and a booking block, then SMS reminders.
  • In progress with the client: a lawyer's review of the terms and the privacy policy.

The screens were never the hard part. The work was underneath them, and in taking the time to see it first.

The certificate's holder details and the phone screenshots of admin bookings are demo data.