specs
Review Challenge

Review Challenge — House of Legends

Documented: 2026-05-11


Overview

Guests are encouraged to leave Google reviews in exchange for comp rewards. They submit a screenshot of their review, staff approves it, and the guest receives a reward.


Challenge Flow

Guest enjoys experience


Open "Review Challenge" in app


See challenge steps:
  1. Go to Google Maps
  2. Leave a review
  3. Take screenshot
  4. Submit screenshot


Submit screenshot


Staff reviews in admin dashboard

      ├──► APPROVED → Guest gets comp reward
      └──► REJECTED → Guest notified, can resubmit

Tables

challengeSubmissions: defineTable({
  profileId: v.id("guestProfiles"),
  orderId: v.id("orders"),
  tableId: v.id("tables"),
  challengeType: v.literal("GOOGLE_REVIEW"),
  screenshotUrl: v.string(),
  status: v.union(
    v.literal("PENDING"),
    v.literal("APPROVED"),
    v.literal("REJECTED"),
  ),
  rewardMenuItemId: v.optional(v.id("menuItems")),
  reviewedBy: v.optional(v.id("users")),
  reviewedAt: v.optional(v.number()),
  notes: v.optional(v.string()),
  showDate: v.string(),
  createdAt: v.number(),
})
  .index("by_status", ["status"])
  .index("by_show_date", ["showDate"])
  .index("by_table_show", ["tableId", "showDate"]);

Challenge Config

challengeConfig: defineTable({
  challengeType: v.union(
    v.literal("PHOTO_WALL"),
    v.literal("LUCKY_SPIN"),
    v.literal("GOOGLE_REVIEW"),
  ),
  enabled: v.boolean(),
  maxValue: v.optional(v.number()),
  prizeDescription: v.optional(v.string()),
  steps: v.array(
    v.object({
      order: v.number(),
      text: v.string(),
      imageUrl: v.optional(v.string()),
    }),
  ),
  activeForDates: v.array(v.string()),
  createdAt: v.number(),
  updatedAt: v.number(),
}).index("by_type", ["challengeType"]);

Example steps for Google Review:

  1. "Search for House of Legends on Google Maps"
  2. "Tap 'Write a review'"
  3. "Add stars and your thoughts"
  4. "Take a screenshot of your review"
  5. "Submit it here"

Admin Review

Staff sees pending submissions in /dashboard/challenges:

  • Guest name + profile
  • Screenshot image
  • Submitted at
  • Approve / Reject buttons

On approve:

  • statusAPPROVED
  • reviewedBy → staff user ID
  • reviewedAt → timestamp
  • Comp item added to guest's order

Components

ComponentPurpose
challenge-card.tsxDisplay challenge info
screenshot-upload.tsxUpload review screenshot
challenge-status.tsxShow pending/approved/rejected
challenge-steps.tsxStep-by-step instructions

Backend Functions

FunctionPurpose
minigames.submitChallengeSubmit screenshot
minigames.approveChallengeStaff approval
minigames.rejectChallengeStaff rejection
minigames.getChallengeStatusCheck submission status