Full-page reservation template for a concrete offer. Allows users to select dates (check-in/check-out), guest counts (adults, children, babies), rental option (House A / House B / Both), paid extras, and an optional message to the landlord. Includes a live price summary sidebar and a primary Reserve CTA. Designed for the MVP inquiry/approval flow β reservation request is sent to the landlord for confirmation. Demonstrates two states: available (form enabled, Reserve active) and unavailable (no option selectable, Reserve disabled, change-dates guidance shown).
This is a standard-context public/guest page reached from the offer detail page ("Reserve" CTA). In the MVP booking flow: user submits the reservation form β landlord receives a request β landlord approves/rejects β user is notified. No payment is collected at this step.
<!-- __('key') -->
comments for PL/EN localisation.
Two-column layout: left form column (dates, guests, rental options, extras, message) and right sticky price summary sidebar. Toggle between the available and unavailable states using the button below.
π Navbar is composed separately β see navigations/navbar.html.
Reservation for:
No rental option available for your selection
Try adjusting your dates or reducing the number of guests. You can also browse other available offers.
π Footer is composed separately β see navigations/footer.html.
Core HTML structure of the reservation page. Paste into your app template and replace mock values with dynamic data. CSRF token and offer ref are hidden inputs β populate server-side. Remove Alpine attributes when wiring to real backend logic.
<section class="bg-white dark:bg-neutral-900 border-b border-neutral-200 dark:border-neutral-700 py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<a href="/offer/{ref}" class="inline-flex items-center gap-2 t-small t-muted hover:text-primary-600 dark:hover:text-primary-400 mb-4">
<!-- back arrow svg -->
<!-- __('reservation.back_to_offer') --> Back to offer
</a>
<p class="t-small t-muted mb-1"><!-- __('reservation.context_label') --> Reservation for:</p>
<h1 class="t-h2 mb-2"><!-- offer title --></h1>
<div class="badge-group">
<span class="badge badge--muted"><!-- dates --></span>
<span class="badge badge--muted"><!-- nights --></span>
<span class="badge badge--overlay"><!-- __('status.available') --> β Available</span>
</div>
</div>
</section>
<div class="bg-white dark:bg-neutral-800 rounded-2xl shadow-sm border border-neutral-200 dark:border-neutral-700 p-6">
<h2 class="t-h4 mb-5"><!-- __('reservation.dates.heading') --> π
Dates</h2>
<div class="form-layout">
<div class="form-grid">
<div class="field">
<label class="field__label field__label--required"><!-- __('reservation.dates.checkin') --> Check-in</label>
<div class="field__control">
<input type="date" class="input" name="checkin" required />
</div>
</div>
<div class="field">
<label class="field__label field__label--required"><!-- __('reservation.dates.checkout') --> Check-out</label>
<div class="field__control">
<input type="date" class="input" name="checkout" required />
</div>
</div>
</div>
<p class="field__hint"><!-- __('reservation.dates.convention_hint') --> Check-in inclusive Β· Check-out exclusive.</p>
</div>
</div>
<div class="bg-white dark:bg-neutral-800 rounded-2xl shadow-sm border border-neutral-200 dark:border-neutral-700 p-6">
<h2 class="t-h4 mb-5"><!-- __('reservation.guests.heading') --> π₯ Guests</h2>
<div class="form-layout">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div class="field">
<label class="field__label field__label--required"><!-- __('reservation.guests.adults') --> Adults</label>
<div class="field__control">
<select class="select" name="guests_adults">
<option value="1">1</option>
<option value="2" selected>2</option>
<!-- ... -->
</select>
</div>
<p class="field__hint"><!-- __('reservation.guests.adults_hint') --> Age 15+</p>
</div>
<div class="field">
<label class="field__label"><!-- __('reservation.guests.children') --> Children</label>
<div class="field__control">
<select class="select" name="guests_children">
<option value="0" selected>0</option>
<!-- ... -->
</select>
</div>
<p class="field__hint"><!-- __('reservation.guests.children_hint') --> Ages 3β14</p>
</div>
<div class="field">
<label class="field__label"><!-- __('reservation.guests.babies') --> Babies</label>
<div class="field__control">
<select class="select" name="guests_babies">
<option value="0" selected>0</option>
<!-- ... -->
</select>
</div>
<p class="field__hint"><!-- __('reservation.guests.babies_hint') --> Under 3 Β· Free Β· Count towards capacity</p>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-neutral-800 rounded-2xl shadow-sm border border-neutral-200 dark:border-neutral-700 p-6">
<h2 class="t-h4 mb-5"><!-- __('reservation.rental_option.heading') --> π Rental Option</h2>
<div class="space-y-3">
<!-- Available option -->
<label class="flex items-start gap-4 p-4 rounded-xl border border-neutral-200 dark:border-neutral-700
hover:border-primary-300 dark:hover:border-primary-600 cursor-pointer transition-colors">
<input type="radio" class="radio mt-1 flex-shrink-0" name="rental_option" value="house_a" />
<!-- Thumbnail: replace src with actual property photo URL -->
<div class="w-24 h-20 flex-shrink-0 rounded-lg overflow-hidden">
<img src="/media/images/house-a-thumb.jpg" alt="House A" class="w-full h-full object-cover" loading="lazy" />
</div>
<div class="flex-1 min-w-0">
<div class="flex flex-wrap items-center justify-between gap-2 mb-1">
<span class="t-body font-semibold"><!-- __('rental_option.house_a.name') --> House A</span>
<span class="badge badge--overlay"><!-- __('status.available') --> β Available</span>
</div>
<p class="t-small t-muted mb-1"><!-- capacity --> Up to 6 guests Β· 3 bedrooms</p>
<p class="t-small font-semibold text-primary-700 dark:text-primary-300">450 PLN / night</p>
</div>
</label>
<!-- Unavailable option (disabled state) -->
<label class="flex items-start gap-4 p-4 rounded-xl border border-neutral-200 dark:border-neutral-700
bg-neutral-50 dark:bg-neutral-800/50 opacity-50 cursor-not-allowed">
<input type="radio" class="radio mt-1 flex-shrink-0" name="rental_option" value="house_b" disabled />
<!-- Thumbnail -->
<div class="w-24 h-20 flex-shrink-0 rounded-lg overflow-hidden">
<img src="/media/images/house-b-thumb.jpg" alt="House B" class="w-full h-full object-cover" loading="lazy" />
</div>
<div class="flex-1 min-w-0">
<div class="flex flex-wrap items-center justify-between gap-2 mb-1">
<span class="t-body font-semibold"><!-- __('rental_option.house_b.name') --> House B</span>
<span class="badge badge--muted"><!-- __('status.unavailable') --> β Not available</span>
</div>
<p class="t-small t-muted mb-1">Up to 4 guests Β· 2 bedrooms</p>
<p class="t-small font-semibold text-primary-700 dark:text-primary-300">390 PLN / night</p>
</div>
</label>
</div>
</div>
<div class="bg-white dark:bg-neutral-800 rounded-2xl shadow-sm border border-neutral-200 dark:border-neutral-700 p-6">
<h2 class="t-h4 mb-2"><!-- __('reservation.extras.heading') --> β¨ Extras</h2>
<p class="t-small t-muted mb-5"><!-- __('reservation.extras.hint') --> Extras are priced per stay.</p>
<div class="form-layout">
<!-- Simple extra (no quantity) -->
<div class="field">
<div class="field__control">
<label class="form-field__option">
<input type="checkbox" class="checkbox" name="extra_firepit" value="1" />
<span class="flex-1">
<span class="t-body"><!-- __('extra.firepit.name') --> π₯ Firepit evening package</span>
<span class="t-small t-muted block mt-0.5">Wood, kindling, fire starter kit</span>
</span>
<span class="t-small font-semibold text-primary-700 dark:text-primary-300 ml-4 flex-shrink-0">150 PLN</span>
</label>
</div>
</div>
<!-- Extra with quantity (Alpine x-data on the field wrapper) -->
<div class="field" x-data="{ qty: 0 }">
<div class="field__control">
<div class="form-field__option items-start">
<input
type="checkbox"
class="checkbox mt-0.5 flex-shrink-0"
name="extra_breakfast"
:value="qty > 0 ? qty : ''"
:checked="qty > 0"
@change="qty = $event.target.checked ? 1 : 0"
/>
<span class="flex-1">
<span class="t-body"><!-- __('extra.breakfast.name') --> π₯ Breakfast set</span>
<span class="t-small t-muted block mt-0.5">Fresh bread, cold cuts, cheese, juice & coffee</span>
<!-- Quantity stepper β shown when checked -->
<div class="flex items-center gap-2 mt-2" x-show="qty > 0" x-cloak>
<span class="t-small t-muted"><!-- __('extra.qty.label') --> Qty:</span>
<button type="button" class="btn btn--secondary btn--sm"
style="padding: 0 0.5rem; min-width: 2rem;"
@click="qty = Math.max(1, qty - 1)">β</button>
<span class="t-body font-semibold w-5 text-center" x-text="qty"></span>
<button type="button" class="btn btn--secondary btn--sm"
style="padding: 0 0.5rem; min-width: 2rem;"
@click="qty = Math.min(20, qty + 1)">+</button>
<span class="t-small t-muted"><!-- __('extra.breakfast.unit_hint') --> Γ 45 PLN / set / day</span>
</div>
</span>
<span class="t-small font-semibold text-primary-700 dark:text-primary-300 ml-4 flex-shrink-0">
<template x-if="qty > 0"><span x-text="(qty * 45 * nights) + ' PLN'"></span></template>
<template x-if="qty === 0"><span>45 PLN / set / day</span></template>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-neutral-800 rounded-2xl shadow-sm border border-neutral-200 dark:border-neutral-700 p-6 lg:sticky lg:top-6">
<h2 class="t-h4 mb-5"><!-- __('reservation.summary.heading') --> π Reservation Summary</h2>
<!-- Summary rows -->
<div class="flex justify-between items-start py-3 border-b border-neutral-100 dark:border-neutral-700">
<span class="t-small t-muted"><!-- __('reservation.summary.dates') --> Dates</span>
<span class="t-small text-right">Jun 5β8, 2026<br><span class="t-muted">3 nights</span></span>
</div>
<div class="flex justify-between items-center py-3 border-b border-neutral-100 dark:border-neutral-700">
<span class="t-small t-muted"><!-- __('reservation.summary.lodging') --> Lodging</span>
<span class="t-small font-semibold">1 350 PLN</span>
</div>
<div class="flex justify-between items-center py-3 border-b border-neutral-100 dark:border-neutral-700">
<span class="t-small t-muted"><!-- __('reservation.summary.extras') --> Extras</span>
<span class="t-small font-semibold">150 PLN</span>
</div>
<div class="flex justify-between items-center py-4">
<span class="t-body font-semibold"><!-- __('reservation.summary.total') --> Total</span>
<span class="text-xl font-bold text-primary-700 dark:text-primary-300">1 500 PLN</span>
</div>
<p class="field__hint mb-5"><!-- __('reservation.summary.disclaimer') -->
Final confirmation by landlord required. No payment collected at this step.
</p>
<!-- Primary CTA -->
<button type="submit" class="btn btn--primary btn--full">
<!-- __('reservation.cta_reserve') --> Reserve
</button>
<!-- Secondary -->
<a href="#" class="btn btn--secondary btn--full mt-2">
<!-- __('reservation.cta_back_to_offer') --> Back to offer
</a>
<!-- Reassurance -->
<p class="t-xs t-muted text-center mt-4"><!-- __('reservation.reassurance.no_payment') --> π No payment at this step</p>
<p class="t-xs t-muted text-center mt-1">
<a href="#" class="underline hover:text-primary-600 dark:hover:text-primary-400">
<!-- __('reservation.reassurance.cancellation_link') --> Cancellation policy
</a>
</p>
</div>
This page is the reservation step reached from the Offer Detail page. In the MVP flow, submitting this form sends a reservation request to the landlord for approval β no payment is collected. The user receives email confirmation when the landlord approves or rejects.
csrf_token β CSRF protection token.offer_ref β reference code of the offer being
reserved; passed from Offer Detail page.
All visible labels include
<!-- __('key') --> comments for PL/EN
localisation. Key prefix: reservation.*,
rental_option.*, extra.*,
status.*.