specs
Photo Wall

Photo Wall — House of Legends

Documented: 2026-05-11


Overview

Guests can submit photos during their visit. Photos appear on the live "wall" display and other guests can like them. Winning photos are featured.


Photo Submission Flow

Guest at Table


Open Photo Wall in App


Take Photo or Select from Gallery


Add Caption (optional)


Submit → Upload to R2


Photo appears on Wall Display

Tables

photoSubmissions: defineTable({
  profileId: v.id("guestProfiles"),
  orderId: v.id("orders"),
  tableId: v.id("tables"),
  imageUrl: v.string(),
  caption: v.optional(v.string()),
  likeCount: v.number(),
  status: v.union(v.literal("ACTIVE"), v.literal("HIDDEN")),
  winner: v.boolean(),
  showDate: v.string(),
  createdAt: v.number(),
})
  .index("by_show_date", ["showDate"])
  .index("by_profile", ["profileId"])
  .index("by_status", ["status"])
  .index("by_likes", ["likeCount", "status"]);
 
photoLikes: defineTable({
  submissionId: v.id("photoSubmissions"),
  profileId: v.id("guestProfiles"),
  createdAt: v.number(),
})
  .index("by_submission", ["submissionId"])
  .index("by_profile_submission", ["profileId", "submissionId"]);

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"]);

Components

ComponentPurpose
photo-submission-form.tsxUpload form with camera/gallery
photo-card.tsxIndividual photo with likes
photo-wall-grid.tsxMasonry grid of photos
photo-likes.tsxLike button + count
wall-display.tsxFull-screen wall for venue display

Like Flow

  1. Guest taps heart on photo
  2. photoLikes record created
  3. photoSubmissions.likeCount incremented
  4. Heart fills in UI

Winner Selection

Admin selects winning photos:

  • Set winner: true on selected photos
  • Winners featured at top of wall
  • Winners eligible for prize (comp items, discounts)

Backend Functions

FunctionPurpose
minigames.submitPhotoUpload + create submission
minigames.likePhotoAdd like
minigames.getWallPhotosGet photos for date
minigames.selectWinnerMark photo as winner