# Willow Hollow B&B — Product Requirements (v1.8)

Willow Hollow B&B is a guesthouse website. Visitors browse rooms, choose a stay by dragging across a month calendar, book with their details and send the owner a message; the owner signs in to an admin panel to manage rooms, see bookings on a report calendar, read messages and change the branding shown publicly.

**Problem.** A small guesthouse needs bookings that can never double-book a room, honest validation for guests and a simple back office whose changes show up on the public page immediately.

## Users
- **Guest** — Pick dates, see the price and book a free room in one pass; ask the owner a question.
- **Owner / admin** — Keep rooms, bookings, messages and branding up to date.

## Goals
- A room is never booked twice for the same night, yet a stay may begin on the day the previous guest leaves.
- Every invalid input is reported per field, before anything is stored.
- Admin changes (rooms, branding, deletions) are reflected everywhere they are used.

## Front page (FRT)

Branding, rooms and room features.

- **FR-FRT-01** (Must) The front page shows the guesthouse name and description and every room in room-number order with its price per night.
  - Heading "Willow Hollow B&B" with the "family-run bed and breakfast" description
  - Five cards: Single $95, Double $150, Suite $225, Twin $120, Family $190, each "per night"
  - The accessible (wheelchair) icon appears on the Double and Family cards only
- **FR-FRT-02** (Could) Each room card shows a badge for exactly the features configured for the room.
  - Suite: WiFi, TV, Radio, Refreshments, Safe, Views
  - Twin: WiFi, Views

## Booking (BKG)

Drag-to-select stay dates and guest details.

- **FR-BKG-01** (Must) A stay is chosen by pressing on the check-in day and dragging to the check-out day; the summary shows the dates, the number of nights and the total price (nights × nightly price).
  - Twin, 3 → 6 May 2027: "Check-in Mon 3 May 2027 · Check-out Thu 6 May 2027 · 3 nights · $360 total"
  - Single, 13 → 15 March 2027: "2 nights · $190 total"
- **FR-BKG-02** (Must) A valid guest books the stay and receives a confirmation; the nights become Unavailable in the calendar and the check-out day stays free.
  - Dialog "Booking Successful!" — "Your booking has been confirmed for: 2027-05-03 - 2027-05-06"
  - Reopened calendar: days 3, 4 and 5 Unavailable, day 6 free
- **FR-BKG-03** (Must) Existing bookings show their nights as Unavailable; the check-out day is not a booked night.
  - Single, March 2027: days 10, 11, 12, 20 and 21 Unavailable; days 13 and 22 are not
- **FR-BKG-04** (Must) A stay overlapping any booked night of the room — including the booking’s last night — is refused and nothing is stored.
  - Single, 11 → 14 March and 12 → 14 March 2027: error "The room dates are either invalid or are already booked"; no success dialog
- **FR-BKG-05** (Should) A stay may begin on the day a previous guest checks out.
  - Single, 13 → 15 March 2027 books successfully for 2027-03-13 - 2027-03-15
- **FR-BKG-06** (Should) A single-day click selects zero nights and cannot be booked.
  - Summary "No nights selected — drag from check-in day to check-out day"
  - Booking shows "Drag across the calendar to choose at least one night"
- **FR-BKG-07** (Should) Stays starting before today are refused with the same dates error.
  - Dragging 10 → 12 in the previous month: days greyed as past, "2 nights"; Book shows "The room dates are either invalid or are already booked"
- **FR-BKG-08** (Should) Guest fields are required and length-checked, with one message per invalid field.
  - Blank: "Firstname must not be blank", "Lastname must not be blank", "Email must not be blank", "Phone must not be blank"
  - Firstname 3–18, lastname 3–30 and phone 11–21 characters; "Al" and a 10-digit phone give "Firstname must be between 3 and 18 characters." and "Phone must be between 11 and 21 characters."
  - "Bea" with an 11-digit phone books successfully
- **FR-BKG-09** (Could) Cancel closes the booking panel on the card without booking and restores "Book this room".

## Contact form (CNT)

Messages from guests to the owner.

- **FR-CNT-01** (Must) A valid message thanks the sender by name and echoes the subject.
  - "Thanks for getting in touch Nora Vance!" then "We'll get back to you about Parking for two cars as soon as possible."
- **FR-CNT-02** (Should) An empty form lists all five required-field errors at once.
  - "Name may not be blank", "Email may not be blank", "Phone may not be blank", "Subject may not be blank", "Message may not be blank"
- **FR-CNT-03** (Should) Subject must be 5–100 characters and message 20–2000.
  - "Help" and a 19-character message give "Subject must be between 5 and 100 characters." and "Message must be between 20 and 2000 characters."
  - "Help!" and a 20-character message are accepted
- **FR-CNT-04** (Must) A submitted message arrives in the admin inbox as unread and raises the unread badge.
  - After one new message the badge shows 3 and "Nora Vance — Parking for two cars" is the first, bold row

## Admin sign-in (ADM)

Access control for the back office.

- **FR-ADM-01** (Must) The admin signs in with valid credentials; wrong credentials are refused and the fields outlined.
  - Wrong credentials: "Invalid credentials" and both fields outlined red
  - Success: navbar Rooms, Report, Branding, Messages (unread badge), Front Page, Logout; rooms 101–105 listed
- **FR-ADM-02** (Should) Admin pages require a session: logged-out visits land on the login form and Logout ends the session.
  - Opening #/admin/report signed out redirects to #/admin with "Log into your account"
  - After Logout, #/admin/messages shows the login form

## Room management (ROM)

Create, edit and delete rooms.

- **FR-ROM-01** (Must) A created room appears in the admin list and on the front page with its price and feature badges.
  - Room 106, Double, not accessible, price 140, WiFi + TV: the list row reads "106 · Double · false · 140 · WiFi, TV"; the front page has a sixth card Double $140
- **FR-ROM-02** (Should) Room creation is validated with itemised messages: room number required and unique, price a whole number from 1 to 999.
  - Empty: "Room name must be set" and "must be greater than or equal to 1"
  - Duplicate 101: "Room 101 already exists"
  - Price 1000: "must be less than or equal to 999"; 12.5: "Price must be a whole number"
- **FR-ROM-03** (Must) Both price limits are valid: 1 and 999 save.
  - Rooms 201 (price 1) and 202 (price 999) are created
- **FR-ROM-04** (Could) Editing a room updates the admin view and the front page.
  - Room 104 set to price 130 with TV added: details "WiFi, TV, Views"; the Twin card shows $130 and WiFi, TV, Views badges
- **FR-ROM-05** (Must) Deleting a room removes it and all its bookings everywhere, including the report calendar.
  - Deleting room 103 leaves four rooms and no "Tomas Lindqvist" bar on 2–5 April 2027

## Report and booking edits (RPT)

Owner’s calendar of all bookings.

- **FR-RPT-01** (Should) The report calendar draws each booking as a bar across its nights labelled with the guest name; clicking a bar shows the booking.
  - March 2027: "Jules Ferro" 10–12, "Priya Nandakumar" 12–14, "Ada Okafor" 20–21
  - Jules Ferro dialog: Room 101, "2027-03-10 - 2027-03-13 (3 nights)", Deposit paid: true
- **FR-RPT-02** (Should) A booking can be edited from its room page, but an edit that overlaps another booking is refused and the booking is unchanged.
  - Priya’s check-out changed to 2027-03-16 is saved
  - Jules Ferro’s check-out changed to 2027-03-21 shows "The room dates are either invalid or are already booked"; it stays 2027-03-10 → 2027-03-13
- **FR-RPT-03** (Should) Deleting a booking removes it from the room page and the report and frees its nights for guests.
  - After deleting Ada Okafor’s booking only Jules Ferro remains for room 101 and days 20 and 21 are no longer Unavailable

## Messages inbox (MSG)

Reading and clearing guest messages.

- **FR-MSG-01** (Must) The inbox lists messages newest first with unread ones in bold; the navbar badge equals the number of unread messages and drops when a message is opened.
  - Seed: Rafael Costa, Mei Tanaka, Hollis Grant; Rafael and Hollis bold; badge 2
  - Opening Rafael Costa shows phone 07700900777, email and message; after closing it is no longer bold and the badge reads 1
- **FR-MSG-02** (Should) Deleting a message removes its row and updates the badge.
  - With an unread new message the badge goes from 3 to 2 after deleting it

## Branding (BRN)

Public name and contact details.

- **FR-BRN-01** (Must) Saved branding replaces the public name and contact details on the front page.
  - Dialog "Branding updated!"
  - Name "Willow Hollow Guesthouse" becomes the front-page heading; contact phone 01234500000 appears on the contact card
- **FR-BRN-02** (Could) Invalid branding is refused with one message per field and no success dialog.
  - Name "AB": "Name must be between 3 and 100 characters."
  - Latitude 95: "Latitude must be a number between -90 and 90"
  - Email "stay-at-home": "must be a well-formed email address"

## Booking and admin API (API)

REST access to rooms, bookings and reports.

- **FR-API-01** (Should) Public endpoints (rooms, bookings) are open; admin endpoints require a bearer token; overlaps are rejected with 409.
  - GET /api/inn/rooms: 200 with five rooms
  - GET /api/inn/report without a token: 401
  - With a token: 200 with four bookings ordered by check-in
  - POST /api/inn/bookings for room 1, 2027-03-11 → 2027-03-12: 409 "The room dates are either invalid or are already booked"

## Non-functional requirements

- **NFR-01** (Must) Security: admin pages and admin API calls are inaccessible without a signed-in session or token.
- **NFR-02** (Should) Data format: dates are shown as YYYY-MM-DD in confirmations and reports; prices are whole dollar amounts.

## Glossary and rules

- **Night** — A day on which a guest sleeps in the room; the check-out day is not a night.
- **Unavailable** — Calendar state of a night that is already booked.

## Out of scope

- Payments and deposits
- Email notifications
- Map rendering
