Table POS — House of Legends

Documented: 2026-05-11 Doc Status: Excellent | ✓ All 6 checks passed

Overview

Guests order food and drinks via a table PWA. Staff fulfill orders via kitchen/reception POS views.

Table PWA Flow

Guest is seated, scans QR on table to open /table/{tableId}, then browses menu, adds items to cart, and views cart to submit order. The order is sent to kitchen, staff POS alerts, kitchen marks items READY, and reception marks items SERVED.

Tables

// tables
tables: defineTable({
  name: v.string(),         // "A1", "VIP-1"
  zone: v.union(v.literal("VIP"), v.literal("PREMIUM"), v.literal("STANDARD")),
  capacity: v.number(),
  status: v.union(v.literal("ACTIVE"), v.literal("INACTIVE")),
  floorPlanX: v.number(),
  floorPlanY: v.number(),
})
  .index("by_zone", ["zone"])
  .index("by_status", ["status"]);

// menuItems
menuItems: defineTable({
  name: v.string(),
  description: v.string(),
  price: v.number(),
  category: v.union(...),
  station: v.union(v.literal("KITCHEN"), v.literal("BAR")),
  available: v.boolean(),
})
  .index("by_category", ["category"])
  .index("by_available", ["available"]);

// orders
orders: defineTable({
  tableId: v.id("tables"),
  reservationId: v.optional(v.id("reservations")),
  status: v.union(
    v.literal("OPEN"),
    v.literal("SUBMITTED"),
    v.literal("COMPLETED"),
    v.literal("CANCELLED"),
  ),
  totalAmount: v.number(),
})
  .index("by_table", ["tableId"])
  .index("by_status", ["status"]);

// orderItems
orderItems: defineTable({
  orderId: v.id("orders"),
  menuItemId: v.id("menuItems"),
  quantity: v.number(),
  unitPrice: v.number(),
  status: v.union(
    v.literal("PENDING"),
    v.literal("SUBMITTED"),
    v.literal("PREPARING"),
    v.literal("READY"),
    v.literal("SERVED"),
    v.literal("CANCELLED"),
  ),
  station: v.union(v.literal("KITCHEN"), v.literal("BAR")),
  isComp: v.boolean(),
  compSource: v.optional(
    v.union(v.literal("SPIN"), v.literal("PHOTO_WIN"), v.literal("GOOGLE_REVIEW")),
  ),
})
  .index("by_order", ["orderId"])
  .index("by_station_status", ["station", "status"]);

POS Views

Kitchen Display (KDS)

Located at /dashboard/pos/kitchen:
  • Orders grouped by station (KITCHEN vs BAR)
  • Shows: table name, item names, quantity, notes
  • Color-coded status: PENDING (yellow), PREPARING (blue), READY (green)
  • Audio ping on new orders
  • “Mark Preparing” / “Mark Ready” actions

Reception View

Located at /dashboard/pos/reception:
  • Table grid layout with status indicators
  • Shows: table name, open order total, order status
  • Click table — see order details
  • “Mark Served” action for items

Staff POS

Located at /dashboard/pos/staff:
  • Per-table order summary
  • Add items to open orders
  • Apply comp items (from spin/photo/review wins)
  • Tab accumulation across multiple orders

Comp Item Flow

When guest wins a challenge:
  1. spinResults / challengeSubmissions created with isComp: true
  2. Staff adds comp item in POS
  3. Item appears with isComp: true flag and source
  4. Comp items don’t count toward revenue

Components

ComponentPurpose
table-menu.tsxMenu grid with categories
table-cart.tsxCart sidebar with items
table-order-confirm.tsxOrder confirmation modal
kitchen-display.tsxKDS grid view
reception-display.tsxTable status grid
order-item-row.tsxSingle item in order list

Backend Functions

FunctionPurpose
payments.createOrderCreate new order
payments.addItemToOrderAdd item to open order
payments.updateItemStatusMark item as preparing/ready/served
payments.applyCompApply comp item to order
payments.getTableOrdersGet orders for table
payments.getKitchenQueueGet all pending items by station