Skip to content
ActiveDeveloper and designer

Strand Hudpleie

Website and admin panel for a skincare clinic in Tønsberg, replacing WordPress and WooCommerce. The admin panel is where the clinic is actually run from.

Screenshot from Strand Hudpleie
Next.js 16React 19TypeScriptTailwind CSS 4SupabasePostgreSQLTipTapResendDockerCoolify

Strand Hudpleie & Velvære is a skincare clinic in the centre of Tønsberg. Their website ran on WordPress with WooCommerce for gift cards, and like many such setups it was both more than they needed and less than they wanted. I rebuilt the website and admin panel from scratch, and the admin panel is where the clinic is actually run from.

What the staff do themselves

  • Handle enquiries and gift card orders in a status flow with defined transitions and optimistic locking, so that two employees don't overwrite each other
  • Adjust the entire price list in a single form, with percentage adjustment per category that has to be approved in a separate step before it's saved
  • Publish campaigns with a time window and a discount per treatment. Lowest price wins, and it's calculated in the database, not in the interface
  • Run return-visit rounds against customers the system has calculated ought to be coming back, based on the customer's own visiting rhythm
  • Edit the text on all the public pages directly in the browser, with field locking, version history and one-click rollback

The technical side

The text is code, not database. JSON files in the repo are the source of truth, and the database is a staging layer. A nightly job writes the edits the staff have made back to the repo as a pull request, and cleanup only happens once the change is confirmed to be running in production. The clinic can edit freely, while the content is still versioned together with the code. A build gate fails if text in the interface doesn't come from a text file.

Password vault that is encrypted and decrypted in the browser. The server never sees the key. Built on Web Crypto without a third-party crypto library, with separate key spaces per access level.

Webhook from the booking system logs the raw payload before processing and acknowledges everything it manages to store, so that deliveries don't stop because of our own errors. Failed deliveries are replayed from the admin panel, and the payload is sanitized for special categories of personal data before it's logged.

Visitor statistics without cookies, session ID, IP or browser fingerprint. The clinic sees what is being read, without collecting anything about who is reading.

Access is enforced in four layers, with the database as the last and decisive layer. Server actions instead of a REST layer.

Stack

Next.js 16, React 19 and TypeScript, Tailwind CSS 4, self-hosted Supabase on PostgreSQL, TipTap for editing, Resend for email, and Docker on Coolify.

This text is machine translated and has not been reviewed yet.