Case study

Ankiety

Ankiety (“Surveys”) is a guest-survey app for the reception desk of a guesthouse. No backend, offline, PDF export.

2025–2026 guesthouse with a spa client project

Context

The reception filled everything in on paper. And a guest survey isn't a formality: the health questionnaire is what qualifies a massage as a medical service exempt from VAT, so the form has to be filled in, stored, found, and shown on request. The app's job was to digitize that process — not replace the document, just stop writing it by hand.

What I built

  • A start screen with survey type selection (therapeutic massage / spa & treatments) and accessibility toggles.
  • A two-step form: client details, then a health questionnaire with a checklist of contraindications.
  • A survey preview before printing, identical to what ends up in the PDF.
  • PDF generation with a VAT-exempt health declaration, ready for archiving.
  • ZIP export bundling the PDFs, a CSV file, and a JSON backup for restoring the app's state.

Crux

Technical decisions

Why doesn't the app have a backend?

Because the survey includes a health questionnaire — blood clots, pregnancy, heart conditions, cancer. That's health data, a special category of personal data under GDPR Article 9, subject to a stricter regime than a plain name.

Having no server isn't a cost-saving move here — it's a way to sidestep that entire class of problems. Surveys stay in the reception's browser IndexedDB, so guest data never leaves the device: there's no database to leak, no server to secure, no data processor to appoint. As a bonus, the app keeps working when the guesthouse's internet drops, and hosting costs nothing, because it's static files.

Why a PWA, not a native app?

Reception installs it straight from the browser — no store, no developer account. Updates land the moment it's reopened — I'm not waiting on review, and I'm not asking anyone to click anything.

Why does export produce a ZIP, not a single PDF?

Reception archives surveys in batches, not one at a time. The bundle contains PDFs named by client surname and treatment date, a dane.csv for spreadsheets, and a backup.json for restoring the app's state. Since the data only lives in the browser, the backup isn't an extra — it's what makes this architecture honest.

Why did .htaccess need an AddType for .mjs?

Apache on OVH doesn't map a MIME type for .mjs, so the browser refused to run the pdf.js worker — a module served with an empty MIME type gets blocked. The second rule disables caching for sw.js: without it, PWA updates don't land, because the browser keeps holding onto the old service worker. Neither shows up in a screenshot, but without them the app simply doesn't work.

What it looks like

Stack

  • Vite
  • PWA / Workbox
  • IndexedDB (idb)
  • pdf.js
  • JSZip
  • Web Share API
  • Arima
Back to top