Lucky Spin — House of Legends

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

Overview

Guests can spin a wheel for a chance to win comp items (free drinks, discounts). One spin per guest per visit.

Spin Flow

Guest visits Photo Wall or Minigames section, sees the “Lucky Spin” prompt, and taps “Spin Now” button. The spin animation plays (using framer-motion), lands on a prize, which is revealed with celebration, and the comp item is added to the order or discount is applied.

Tables

spinPrizes: defineTable({
  label: v.string(),
  prizeType: v.union(v.literal("MENU_ITEM"), v.literal("DISCOUNT")),
  menuItemId: v.optional(v.id("menuItems")),
  discountPercent: v.optional(v.number()),
  weight: v.number(), // Higher = more likely
  enabled: v.boolean(),
  createdAt: v.number(),
  updatedAt: v.number(),
}).index("by_enabled", ["enabled"]);

spinResults: defineTable({
  profileId: v.id("guestProfiles"),
  orderId: v.id("orders"),
  tableId: v.id("tables"),
  prizeId: v.id("spinPrizes"),
  displayText: v.string(),
  showDate: v.string(),
  createdAt: v.number(),
})
  .index("by_show_date", ["showDate"])
  .index("by_table_show", ["tableId", "showDate"])
  .index("by_profile", ["profileId"]);

Prize Types

TypeDescriptionExample
MENU_ITEMFree menu itemFree Cocktail
DISCOUNTPercentage off bill10% off

Weight System

Higher weight = more likely to land on that prize. Example config:
PrizeTypeWeight
Free CocktailMENU_ITEM30
5% OffDISCOUNT25
10% OffDISCOUNT20
Free DessertMENU_ITEM15
15% OffDISCOUNT10

Components

ComponentPurpose
spin-wheel.tsxAnimated wheel (framer-motion)
spin-trigger.tsx”Spin Now” button
spin-result.tsxPrize reveal modal
spin-history.tsxGuest’s spin results

Backend Functions

FunctionPurpose
minigames.spinProcess spin, select prize
minigames.getSpinResultGet guest’s spin result
minigames.getPrizesGet available prizes
minigames.applySpinPrizeApply comp to order

Animation

Wheel spin uses framer-motion:
  • animate-spin with custom easing
  • Random duration 3-5 seconds
  • Lands on prize based on server-side selection (not client random)