specs
Table Pos

Table POS — House of Legends

Documented: 2026-05-11


Overview

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


Table PWA Flow

Guest Seated


Scan QR on Table → /table/{tableId}


Menu Displayed (grouped by category)

      ├──► Browse menu
      ├──► Add items to cart
      └──► View cart → Submit order


        Order Sent to Kitchen


        Staff POS Alerts


        Kitchen marks items READY


        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