Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/alex-pizza-assistant/llms.txt

Use this file to discover all available pages before exploring further.

Every entry in the steps[] array carries two properties that make the form self-enforcing: a validate() function and an error string. When the user submits an answer, handleSubmit() calls step.validate(rawValue) before doing anything else. If the validator returns false, the error string is written to the #validationMessage element and the current step does not advance — the user must correct their input and try again. Only a passing validation clears the message, saves the answer, and moves the conversation forward.

Validation Rules Per Step

The table below lists every step ID, the exact validation logic from app.js, and the error message the user sees on failure.
Step IDValidation RuleError Message
userNamevalue.length > 0Name cannot be blank.
sizeMust be small, medium, or large (case-insensitive)Please enter small, medium, or large.
toppingsvalue.length > 0Toppings cannot be blank. If you do not want any toppings, simply enter cheese.
crustTypevalue.length > 0Crust type cannot be blank.
quantityMust match /^\d+$/ and be > 0Please enter a numeric value greater than 0.
addAnotherMust be yes, y, no, or n (case-insensitive)Please enter yes or no.
methodMust be carry out, carryout, pickup, pick up, or deliveryPlease enter carry out or delivery.
paymentMethodMust be cash, card, credit, debit, credit card, or debit cardPlease enter cash or card.
cardNumber13–19 digits after stripping spaces and hyphens — /^\d{13,19}$/Please enter a card number using 13 to 19 digits.
cardExpirationMM/YY format — /^(0[1-9]|1[0-2])\/\d{2}$/Please enter the expiration date as MM/YY.
cardCvv3 or 4 digits — /^\d{3,4}$/Please enter a 3 or 4 digit security code.
tipValid money value matching /^\$?\d+(\.\d{1,2})?$/ via isValidMoney()Please enter a valid tip amount, like 3, 3.50, or 0.
The toppings and crustType steps accept any non-blank string. There is no fixed list of valid toppings or crust names — these are free-form text fields. Users can enter anything as long as the field is not empty.

The handleSubmit() Validation Path

The entire validation gate lives in the handleSubmit() function. Raw input is tested first; only after it passes is the value normalized, saved, and acted on.
function handleSubmit(event) {
  event.preventDefault();

  const step = steps[currentStep];
  const rawValue = chatInput.value.trim();

  // Validation gate — block here if the answer is invalid
  if (!step.validate(rawValue)) {
    validation.textContent = step.error;
    return;                          // stop — do not advance the step
  }

  // Clear any previous error message
  validation.textContent = "";

  // Normalize, save, display, respond, recalculate, then advance
  const value = normalizeValue(step.id, rawValue);
  saveAnswer(step.id, value);

  addBubble(rawValue, "user");
  respondToAnswer(step.id, value);
  calculateTotals();
  updateReceipt();

  advanceStep(step.id, value);
}
The #validationMessage element carries role="alert" in the HTML, so screen readers announce validation errors automatically without any extra JavaScript.

Normalization

Raw values that pass validation are immediately passed to normalizeValue() before being stored. This coerces the many acceptable input variants into the single canonical form the rest of the app depends on.
function normalizeValue(id, value) {
  const normalized = value.toLowerCase().trim();

  if (id === "size") return normalized;
  // "carryout", "pickup", "pick up" → "carry out"
  if (id === "method") {
    if (["carryout", "pickup", "pick up"].includes(normalized)) return "carry out";
    return normalized;
  }
  // "3" → 3  (number, not string)
  if (id === "quantity") return Number(value);
  // "y" → "yes", "n" → "no"
  if (id === "addAnother") return ["yes", "y"].includes(normalized) ? "yes" : "no";
  // "credit", "debit", "credit card", "debit card" → "card"
  if (id === "paymentMethod")
    return normalized.includes("card") || ["credit", "debit"].includes(normalized) ? "card" : "cash";
  // Strip spaces and hyphens from card number
  if (id === "cardNumber") return value.replace(/[\s-]/g, "");
  // "$3.50" → 3.5  (number)
  if (id === "tip") return Number(value.replace("$", ""));
  // Everything else (userName, toppings, crustType, etc.) is returned as-is
  return value;
}
Key normalizations to know:
  • methodcarryout, pickup, and pick up all resolve to the canonical form "carry out".
  • addAnother"y" becomes "yes"; "n" becomes "no". The branching logic in advanceStep() only checks for "yes" and "no".
  • paymentMethod"credit", "debit", "credit card", and "debit card" all resolve to "card". Only "cash" stays "cash".
  • quantity — stored as a Number, not a string, so arithmetic in calculateTotals() works without further conversion.
  • tip — the leading $ sign is stripped and the value is cast to Number.

Error Display

The #validationMessage element is a <p> tag rendered below the submit button in the chat form. Its textContent is set to step.error when validation fails and cleared to an empty string when validation passes.
<p id="validationMessage" class="validation-message" role="alert"></p>
Because the element is empty by default, no message is visible at the start of each step. The message only appears the moment a bad answer is submitted, and it disappears the instant the user provides a valid answer.

Build docs developers (and LLMs) love