# Outfitter — Product Requirements (v1.0)

Outfitter is a practice online storefront: shoppers sign in, browse a six-item catalogue, sort it, open product pages, build a cart and complete a three-step checkout (information, overview, confirmation) with 8% sales tax.

**Problem.** Shoppers need to find items, trust the prices they see and complete an order without surprises; the order total must always equal item total plus tax.

## Users
- **Shopper** — Sign in, sort and browse items, add and remove them, and place an order with a clear price breakdown.
- **Locked-out shopper** — Be told clearly that the account cannot sign in.
- **Integrator** — Read the catalogue, validate a login and place orders through a small API.

## Goals
- Every account type gets the same correct catalogue, sorting, cart and checkout behaviour; only sign-in speed and lockout differ.
- The price a shopper sees is the list price on every page.
- Tax is exactly 8% of the item total, rounded to the cent, and the order total reconciles.

## Sign-in (SGN)

Login form with per-field errors and account states.

- **FR-SGN-01** (Must) A valid account signs in and lands on the Products page showing six products.
  - Page title "Products" and URL ending /inventory.html
  - Each of the six cards shows name, description, price and "Add to cart"
- **FR-SGN-02** (Must) Missing fields are reported one at a time, username first, then password.
  - Both empty: "Oh snap: Username is required"
  - Username only: "Oh snap: Password is required"
  - The ✕ on the error banner dismisses it
- **FR-SGN-03** (Must) Unknown users, wrong passwords and differently-cased usernames are rejected with one generic message and the shopper stays on the login page.
  - "Oh snap: Username and password do not match any user in this service"
  - Usernames are case-sensitive
- **FR-SGN-04** (Must) A locked-out account with the right password cannot sign in.
  - "Oh snap: Sorry, this user has been locked out." and the login page remains
- **FR-SGN-05** (Should) The performance-test account signs in only after about 3 seconds; the Login button shows "Logging in…" and is disabled meanwhile.
  - This delay is accepted, contracted behaviour for that account
  - After the delay the Products page opens

## Session and gating (SES)

Protected pages and logout.

- **FR-SES-01** (Should) Protected pages opened without signing in show the login form with an explanation naming the page.
  - /cart.html: "Oh snap: You can only access '/cart.html' when you are logged in."
  - The same for /inventory.html
- **FR-SES-02** (Must) Logout returns to the login page, empties the cart and re-gates protected pages.
  - After logging out and signing in again there is no cart badge
  - Opening /inventory.html after logout shows the gating message

## Catalogue and product detail (CAT)

Sortable product list and product pages, identical for every account.

- **FR-CAT-01** (Must) The catalogue opens sorted Name (A to Z) and can be re-sorted; the list always matches the chosen sort.
  - Name (A to Z): Backpack, Bike Light, Bolt T-Shirt, Fleece Jacket, Onesie, Test Everything T-Shirt (Red)
  - Name (Z to A) is the exact reverse
  - Price (low to high): $7.99, $9.99, $15.99, $15.99, $29.99, $49.99 — Bolt T-Shirt before the Red T-Shirt (equal prices keep name order)
  - Price (high to low): $49.99, $29.99, $15.99, $15.99, $9.99, $7.99
- **FR-CAT-02** (Must) Every product shows its list price on the Products page, matching the cart and checkout.
  - Backpack $29.99, Bike Light $9.99, Bolt T-Shirt $15.99, Fleece Jacket $49.99, Onesie $7.99, Test Everything T-Shirt (Red) $15.99
- **FR-CAT-03** (Should) Each product shows its own product image, and every account sees the same product art.
  - The six images are distinct from one another
- **FR-CAT-04** (Should) Clicking a product name opens its detail page with name, description, price and an Add to cart / Remove toggle that shares the cart with the list.
  - The Fleece Jacket opens /inventory-item.html?id=5 showing $49.99
  - "← Back to products" returns to the list
  - Adding on the detail page shows "Remove" there and in the list, and the badge becomes 1
- **FR-CAT-05** (Could) An unknown product id shows a not-found page.
  - "ITEM NOT FOUND" and "We're sorry, but your call could not be completed as dialled."

## Cart (CRT)

Add, remove, persist and reset items.

- **FR-CRT-01** (Must) Every product’s Add to cart button adds that product, flips to "Remove", and the cart badge always equals the number of items in the cart.
  - Adding all six products gives badge 6
  - Removing one item lowers the badge by 1
  - Adding any single product, including the Bolt T-Shirt, shows "Remove" and badge 1
- **FR-CRT-02** (Should) The cart page lists each item with quantity 1 and its list price and allows removal.
  - Title "Your Cart"; Backpack $29.99 and Fleece Jacket $49.99 each QTY 1
  - Continue Shopping returns to Products with the items still in the cart
- **FR-CRT-03** (Could) The cart persists across page reloads while signed in.
  - After a reload the badge and "Remove" buttons are unchanged
- **FR-CRT-04** (Could) Menu "Reset App State" empties the cart without signing out; About and All Items navigate and the menu closes.
  - After reset, reload shows "Add to cart" on every product
  - About shows title "About"; All Items shows Products

## Checkout (CHK)

Three steps: information, overview, complete.

- **FR-CHK-01** (Must) Your Information requires First Name, Last Name and Postal Code, checked in that order, and accepts a typed value in every field.
  - "Error: First Name is required", then "Error: Last Name is required", then "Error: Postal Code is required"
  - Text typed into each field stays in that field only
  - With all three filled, Continue opens "Checkout: Overview"
- **FR-CHK-02** (Must) The overview shows item total, tax of 8% rounded to the cent, and the total; Finish places the order.
  - Backpack: Item total $29.99, Tax $2.40, Total $32.39
  - Backpack + Bike Light: $39.98, $3.20, $43.18
  - All six items: $129.94, $10.40, $140.34
  - The overview shows "TrailCard #31337" and "Free Pony Express Delivery!"
- **FR-CHK-03** (Must) Finish completes the order for every account: "Checkout: Complete!" with "Thank you for your order!", the cart is emptied and Back Home returns to Products with every item showing "Add to cart".
- **FR-CHK-04** (Should) An empty cart can reach the overview (all amounts $0.00) but the order is refused.
  - Finish shows the inline error "Cart is empty" and stays on the overview
- **FR-CHK-05** (Could) Cancel on Your Information returns to the cart; Cancel on the overview returns to Products; the cart is kept in both cases.

## API (API)

Catalogue, login and order endpoints.

- **FR-API-01** (Should) The catalogue endpoint returns the six products and a single-product lookup; unknown ids return 404.
  - The Backpack has id 4 and price 29.99
  - 404 "Product not found"
- **FR-API-02** (Should) Login maps each outcome to a status and message.
  - Empty: 400 "Oh snap: Username is required"
  - Wrong password: 401 "…do not match any user in this service"
  - Locked account: 403 "Oh snap: Sorry, this user has been locked out."
  - A valid account returns 200
- **FR-API-03** (Should) Orders validate shipping fields and compute totals on the server.
  - Items [4, 0] for Ada Lovelace 10115: 201 with item 39.98, tax 3.2, total 43.18
  - Empty last name: 422 "Error: Last Name is required"
  - Only unknown ids: 422 "Cart is empty" (unknown ids are dropped)

## Non-functional requirements

- **NFR-01** (Must) Money is shown with a $ sign and exactly two decimals, and tax is calculated from the item total (8%, rounded half up to the cent) identically in the UI and the API.

## Glossary and rules

- **Item total** — Sum of list prices of the items in the cart.
- **Tax** — 8% of the item total, rounded to the cent.

## Out of scope

- Real payment and shipping
- Footer social links
