Skip to main content
Charlie Macnamara - Technical Writer and Developer
  • About
  • Blog
  • Portfolio

4 Cloudflare Workers and a Kilt Shop

How a 1927 Edinburgh kilt shop went from paper notes and missed appointments to automated booking — powered by Cloudflare Workers, Google Calendar, and robust tests.

Recently my sister got married — congrats again, Floyd — so I needed a kilt. Luckily, Davison Menswear & Kilt Hire is a few doors down from me. Outside, the shops easy to spot: hand-painted marquee with logo, Est. 1927, and a previously broken url.

Thought I'd introduce myself while I got my threads.

Davison Menswear hero section showing the full-page scroll layout with archival background

The full hero viewport — archival photos bleed through as you scroll

The owner, Marty, hand-sews 12-yard kilts from memory. Generations of talent. He works alone in a showroom that hasn't changed much since his Fathers's day: notepad by the phone, paper diary, appointment slips.

The methods were proven, but dated. Calendar bookings misplaced, missed calls, staying late to rewrite orders. Marty was keen on upgrading - old site had been down for several years - so we sat down and talked through needs.

The Requirements

Dislikes and boundaries were my largest consideration for this project. Expecting a sole trading generational kilt maker to learn several new tools, after he's been working on paper for decades, is unrealistic. It was important tools felt familiar and intuative, while staying within his scope. Why build a feature that won't be used?

I'd already built Sicamon — a full Stripe CMS with digital cataloguing. Understandably, this was too much change. Not looking to become an online shop. I came up with a design that was modern, but familiar and wouldn't overwelm by changing his core practices: No online inventory, no card payments. Bank transfers. What he's always done.

Project runs on:

Workers, D1,

KV, Turnstile, Resend, GitHub Actions — and costs nothing to run.

Heritage was the identity I decided; made sense. A century in 2027 — I wanted a design to reflect. I was given a binder: Marketing photos from the 1920s through the 1950s (took ages to scan).

Original archival photo of Davison Menswear from the binder, showing the historic shopfront

One of the original archive photos from the binder — the shopfront before Edinburgh's trams were pulled up

I scanned, and composited a site background. Every section uses a translucent white overlay so the archival photos bleed through as you scroll.

The site's background image composites archival photos of Davison Menswear from the 1920s-1950s

Archival photos composited into the site background — the binder became the brand


The Main Site

The user site is primarily a single page: header, map, services, tartan search, reviews, a "Book a Fitting" button. Tailwind keeps every layout consistent — utility classes enforce a design system, no magic numbers. Typography scales fluidly with CSS clamp() — no breakpoint jumps, no layout surprises.

Mostly minimal. The tartan search is a nice-to-have novelty — searchable directory of clan and district patterns - should help with SEO, though, not essential, but fun.

Tartan search directory showing clan and district patterns

Searchable directory of clan and district tartans — a fun bonus for visitors

Primary user functionality is the booking system; ran via Cloudflare workers and existing Google Calendar. What happens when you click:

The booking form with date, time, and customer details fields

The booking form — Turnstile bot protection runs invisibly on submit

The Booking Pipeline

  • Bot detection — Turnstile, invisible
  • Input validation — malformed data rejected
  • Closure check — reads [Closed] calendar events
  • Slot availability — no double-booking
  • Event creation — writes to Google Calendar
  • Cancel link — signed JWT in the event
  • Notifications — async emails to both customer and site owner, never blocks

Everything is secured with Cloudflare. Deterministic security was top of mind. Someone else books the slot mid-form? You're told immediately.

Closures are set by adding [Closed] — Christmas, holidays, any day off. The worker updates automatically.

Booking confirmation screen showing the successfully booked appointment
Branded booking confirmation email sent to the customer
Owner alert email notifying of a new booking

Booking confirmed — the customer sees this first, then both parties receive email alerts

Google Calendar showing a booked fitting appointment

Google Calendar is the database — already in use, the booking system reads and writes to it

Testing

This runs a sole trader's business. Every commit, every deploy — it has to work. Not "mostly work." Work.

Testing pipeline from static audits through to post-deploy monitoring

Seven layers between a push and production

So there are seven layers between a push and production. Static audits catch hardcoded secrets and broken contracts before a test even runs. Worker tests mock every external dependency so internal logic can be validated deterministically — no network, no credentials. Live API tests then prove the real integrations work (Google Calendar, Resend, Turnstile). E2E and integration tests drive the full booking flow and admin lifecycle headless.

A pre-deploy check probes all four workers before anything ships. If something's degraded, the deploy stops. Once live, scheduled monitoring probes every six hours, and the calendar worker self-checks every sixty seconds.

It's a lot of ceremony for a booking form and an admin panel. But if a booking drops, it's not an alert on a dashboard — it's a customer standing outside a locked shop.


The Workers

Frontend Worker

The shop window. Serves every page a visitor lands on — hero, map, services, the tartan search widget, reviews, and the booking button. Static build, globally distributed. No server to babysit.

Calendar Worker

Looks after the booking system — a separate worker with its own credentials. Google Calendar is the database, zero new infra, already in use. Reads [Closed] events, checks slot availability, writes new bookings, and generates cancel links using signed

JWTs in the event description — no database lookup needed. Turnstile over CAPTCHA on the booking form: free, invisible, no learning curve. Email fires asynchronously so the booking never blocks on a delivery failure.

Reviews Worker

Pulls Google Reviews on a schedule into KV. Stays current without input — fresh content, zero effort.

Google Reviews displayed on the site

Live Google Reviews pulled on a schedule — fresh content with zero effort


The Admin Dashboard

The notepad replacement — its own Cloudflare Worker backed by D1. The same order in the diary, on a slip, and on a sales pad — three times, one customer. Now it's a page.

Full admin dashboard view with all management features

Full admin dashboard — 139 tests verify the whole system

Four statuses: In Progress, Ready, Delivered, Cancelled. Every transition logged immutably — who changed it, when, and whether the email went out. Group orders for wedding parties — groom, best man, ushers under one accordion. Measurements: kilt waist, seat, length, jacket chest, sleeve, back, height, build notes. All optional.

Emails fire on status changes via Resend. The receipt includes bank details — stored as Worker secrets, never in the codebase. On creation the customer gets payment instructions. When it's ready, a collection notice. Not a word typed. CSV export: current tax year, last year, or custom range. Clean totals at the bottom for the accountant.

Quick Add for reconciling past orders. Archived orders auto-purge after 30 days. No payments on-platform — bank transfer, cash, the till. Progress without overwhelm.

I'm a message away if a fix is needed. The binder is back on the shelf. And the marquee with qualitykilts.com is still there — now it works.

© 2026 Charlie Macnamara. All rights reserved.

Built on: Next.jsReactTailwindCSSAWS