Hvammsvik Booking


Overview
Hvammsvik Booking is the online booking system for Hvammsvik Hot Springs, a geothermal bathing destination in Iceland. Visitors reserve admission packages for specific dates and time slots, add extras such as body oils, buy gift cards, and pay online — in Icelandic or English.
The site wraps the Tegra "BC Booking Service" activity-booking API: orders, reservations, and slot availability are created and queried through that external service rather than a local database. Payments are handled by Datatrans through Next.js API routes, with the gift card, refund, and manage-booking flows completing the lifecycle after purchase.
Content editors manage all page copy, packages, and email templates in Prismic, while SWR drives client-side data fetching for slot availability and activity info.
Tech Stack & Rationale
Layer | Technology | Why |
|---|---|---|
Framework | Next.js 13 (Pages Router), React 18 | Server-rendered booking pages plus a pages/api route layer |
Language | TypeScript | Typed booking, payment, and gift-card flows |
Styling | Tailwind CSS 3.3 | Shared design system from a Next.js starter |
CMS | Prismic + Slice Machine | Pages, packages, products, and email content |
Client data fetching | SWR 2.2 | Slot availability and activity info with caching |
Booking engine | Tegra "BC Booking Service" REST API | Orders, reservations, and availability (external) |
Payments | Datatrans | Transaction init, status checks, and refunds |
State | Redux Toolkit + redux-persist, Zustand | Booking wizard state and manage-booking store |
React Email + Resend | Transactional emails (confirmation, refund) | |
@react-pdf/renderer | Gift card PDF attachments | |
Media | Cloudinary | Image hosting and delivery |
Feature flags | Statsig | Experimentation and analytics |
Testing | Vitest + Testing Library | Unit tests for pricing and reschedule utilities |
i18n | Next.js built-in i18n (en-us, is) | Icelandic/English locale routing |
Architecture
A booking flows from the Tegra catalog through a Next.js API proxy into Datatrans for payment, then back into Tegra for reservation creation and later manage-booking mutations. There is no local database — state lives in the external services and is re-fetched with SWR.
The flow runs: Booking page (package · extras · date) → SWR hooks (slot availability) → Secure Fields (Datatrans payment) → /api/datatrans/ (init · status · refund) → Datatrans (transactions); Booking page (package · extras · date) → SWR hooks (slot availability) → /api/tegra/ (orders · reservations) → Tegra API (BC Booking Service).
- Catalog & availability — core/swrs/useResourceSlotAvailabilities.ts (and the activity-info hooks) fetch Tegra availability through the /api/booking/loadResouceSlotAvailabilities route, cached and revalidated by SWR keyed by date.
- Order creation — core/utils/tegra/booking/api.ts POSTs createOrder and createReservation through /api/tegra/* proxy routes, which authenticate against the Tegra endpoint using the server-side token.
- Payment — the checkout builds Datatrans payloads (core/utils/booking/createPaymentDatatransData.ts) and initializes a transaction via /api/datatrans/initTransactions; the Secure Fields page collects card details and Datatrans verifies the transaction.
- Confirmation & email — on success, /api/booking/success finalizes the booking and Resend/React Email templates send the confirmation (and thank-you or refund emails), with gift-card PDFs rendered by @react-pdf/renderer.
- Manage booking — authenticated visitors reschedule, upgrade, or refund through /api/manage-booking/* routes, with Datatrans refunds gated by a JWT cookie verified in middleware.ts.
Folder Structure
hvammsvik-booking/
├── pages/
│ ├── booking/ # booking, securefields-payment, success, declined
│ ├── giftcard/ # purchase, payment, success, declined
│ ├── manage-booking/ # modify, reschedule, upgrade, refund
│ ├── admin/ # login, refund (staff-only)
│ └── api/ # tegra, datatrans, booking, giftcard, email, manage-booking, admin-auth
├── components/ # _layouts, _shared (EmailTemplate, Icons), components/*
├── core/
│ ├── cloud/ # datatrans.ts, hvammsvik.ts, tegra.ts (Axios wrappers)
│ ├── fetchers/ # slot availability, activity info, modify/refund/reschedule
│ ├── swrs/ # useResourceSlotAvailabilities, useServerDateTime, …
│ ├── redux/ # auth + booking slices (with redux-persist)
│ ├── stores/ # manage-booking Zustand store
│ ├── utils/ # booking, datatrans, email, giftcard, manage-booking, tegra
│ ├── prismic/ # client + query utilities
│ └── @types/ # auth, booking, datatrans, email, giftcard, payment
├── slices/ # Hero, GiftCardList, ManageBooking, Faq, …
├── customtypes/ # booking_page, package, product, giftCard, manage_booking, …
├── __tests__/ # Vitest unit tests for pricing/reschedule utils
├── middleware.ts · prismicio.ts · next.config.js · tailwind.config.js
└── api_documentation.json # Postman collection for the Tegra BC Booking ServiceContent Model
Prismic holds the booking content and email copy; order and payment state stays in Tegra and Datatrans. Key custom types:
Custom type | Kind | Purpose |
|---|---|---|
booking_page | Singleton | The main booking experience — steps, packages, and copy |
booking_step | Repeatable | Individual steps in the booking flow |
package | Repeatable | Admission packages and their contents |
product | Repeatable | Extras and add-on products |
section | Repeatable | Reusable page sections |
giftCard | Singleton | Gift card purchase page content |
manage_booking | Singleton | Manage-booking landing and instruction copy |
refund_and_reschedule | Singleton | Refund/reschedule policy content |
fully_booked_dates | Singleton | Closed/sold-out date overrides |
book_with_transfer | Repeatable | Bookings that include a transfer option |
email_content / thank_you_email / pdf_content | Singleton | Transactional email and gift-card PDF templates |
The slice library includes Hero, GiftCardList, ManageBooking, ManageBookingHero, Faq, Accommodation, and TermsAndConditions slices.
Impact & Results
- In production for Hvammsvik Hot Springs (Iceland), taking paid reservations and gift-card purchases in Icelandic and English.
- Integrates the Tegra BC Booking Service for availability and Datatrans for payments.
My Contributions
Contributed to the Hvammsvik booking platform as part of the VISKA team:
- I implemented multi-currency support across the booking flow — currency selector with flags, price conversion on the booking page, checkout summary, and success page.
- I added Datatrans transaction verification and the currency field on the Prismic package/product types.
- I rebuilt the booking calendar and time-slot availability UI (MonthCalendar, slot availabilities via SWR).
- I implemented promo-code and gift-card validation utilities.
- I contributed to the manage-booking flow (reschedule, upgrade, refund) and fixed timezone-related date formatting issues.
- I added Icelandic/English translations and currency formatting across the booking pages.
- I wrote Vitest unit tests for the pricing, reschedule, and upgrade calculation utilities.
References
[1] Hvammsvik, "Hvammsvik Hot Springs," hvammsvik.is. [Online]. Available: https://hvammsvik.is
[2] Vercel, "Next.js Documentation," nextjs.org. [Online]. Available: https://nextjs.org/docs
[3] Prismic, "Prismic Documentation," prismic.io. [Online]. Available: https://prismic.io/docs
[4] Vercel, "SWR — React Hooks for Data Fetching," swr.vercel.app. [Online]. Available: https://swr.vercel.app
[5] Datatrans, "Datatrans API Documentation," docs.datatrans.ch. [Online]. Available: https://docs.datatrans.ch
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!