Skip to content

Ship the gamified mobile Sweepza marketplace - #102

Merged
AutomatedEmpires merged 6 commits into
mainfrom
codex/gamified-marketplace-mobile
Jul 29, 2026
Merged

AutomatedEmpires merged 6 commits into
mainfrom
codex/gamified-marketplace-mobile

Conversation

@AutomatedEmpires

@AutomatedEmpires AutomatedEmpires commented Jul 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

Ships a production-focused consumer marketplace overhaul using the existing canonical listing, ingestion, lifecycle, reminder, and seeker-state architecture.

  • Makes current sweepstakes the first mobile content, preserves the pinned bottom navigation, and introduces the purple/gold/velvet day-night identity.
  • Uses the founder-supplied full Sweepza logo without cropping or overlays.
  • Makes prize, sponsor, eligibility, entry frequency, rules, deadline, source classification, and official entry action visible on listing cards.
  • Adds owned, category-aware listing photography with explicit representative-photo disclosure and honest alt text while preserving official-media attribution.
  • Expands action-backed streak/badge progress without implying odds, winnings, or Sweepza operation of third-party promotions.
  • Schedules gated ingestion and stale-listing expiration twice daily in the required ingest-then-expire order.

Issue: founder-directed production release; no linked issue.

Lane: C / D / I

Acceptance criteria

  • Public and authenticated mobile surfaces retain safe-area-aware pinned navigation.
  • The rejected tan/red palette is replaced by tokenized purple, gold, violet, and velvet modes.
  • The complete supplied logo is presented without cropping or obstruction.
  • Current listings appear before marketing copy on mobile and render complete trust/relevance facts.
  • Listings without official imagery use owned photos visibly labeled as representative, never official.
  • Official image attribution and source provenance remain intact.
  • Expired listings remain excluded by the existing lifecycle boundary; expiration now runs twice daily.
  • Ingestion runs twice daily only when the existing master switch and per-source compliance gates allow it.
  • Gamification reflects real saved/entered/streak/badge activity and makes no outcome promises.
  • Empty, unavailable, desktop, tablet, and mobile states remain supported.

Canon alignment

  • Matches listing state, trust, provenance, and disclosure rules.
  • Uses the canonical listing object — no parallel listing models.
  • Controlled category and entry-frequency values use existing canonical types/registries.

Security & quality

  • Existing RLS and server authorization paths are unchanged.
  • No secrets or new environment variables are committed.
  • Mobile-first and accessible: safe areas, 44px+ controls, semantic heading order, visible labels, focus behavior, measured contrast.
  • PWA manifest, browser theme colors, icon, and Open Graph palette are synchronized.
  • pnpm test: 97 files / 1,398 tests passed.
  • pnpm typecheck passed.
  • pnpm lint passed with zero warnings.
  • pnpm build passed on Node 24.16.0 / Next 15.5.19.
  • Production-artifact browser QA passed at 390px, 768px, and 1440px; axe reported 0 violations and browser console/errors were empty.

Release boundaries

Merging main deploys sweepza.com. The cron schedule is included, but ingestion still fails closed unless INGESTION_ENABLED=true and at least one source has explicit production/compliance approval. No email, payment, database migration, source approval, or provider activation is included.

Summary by CodeRabbit

  • New Features

    • Added a refreshed homepage experience with the “Daily drop” card and prize lobby messaging.
    • Redesigned the gamification strip with a metrics grid, next-badge progress, and a horizontally scrollable badge shelf.
    • Introduced “Representative photo” disclosure for listing media, including updated winner imagery behavior.
  • Improvements

    • Refreshed mobile navigation and enhanced spacing for devices with bottom insets.
    • Updated listing cards/media visuals, eligibility details, countdown/urgency styling, and official-entry actions.
    • Improved theme-color handling (synced with the active theme) and updated OG visuals to match theme tokens.
  • Documentation

    • Updated scheduled ingestion/expiration times in the environment/job documentation.

Copilot AI review requested due to automatic review settings July 29, 2026 00:19
@vercel

vercel Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sweepza Ready Ready Preview, Comment Jul 29, 2026 1:44am

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AutomatedEmpires, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b9f671d-7ba5-48de-b35d-8d9f02113f5c

📥 Commits

Reviewing files that changed from the base of the PR and between 367c4d8 and 79e908d.

📒 Files selected for processing (5)
  • app/layout.tsx
  • components/winner-card.tsx
  • lib/__tests__/mobile-navigation-theme.test.ts
  • lib/__tests__/winner-card-media.test.tsx
  • lib/theme.tsx
📝 Walkthrough

Walkthrough

The pull request replaces generated listing fallback images with owned static assets, adds tokenized theme synchronization, refreshes mobile navigation and theme handling, redesigns homepage and listing surfaces, restructures gamification, and expands cron schedules with configuration tests.

Changes

Product experience updates

Layer / File(s) Summary
Owned listing fallback assets
lib/listing-media.ts, app/api/images/listing-fallback/..., lib/__tests__/listing-media-presentation.test.ts, public/images/listing-fallbacks/*, lib/listing-seo.ts
Fallback categories resolve to owned WebP assets with deterministic alt text; the route returns a cached 307 redirect, presentation URLs use owned assets, representative media is disclosed, and SEO excludes category fallback art from primary images.
Tokenized theme pipeline
app/tokens.css, scripts/theme-colors-lib.mjs, scripts/sync-theme-colors.mjs, app/layout.tsx, lib/theme.tsx, app/manifest.ts, lib/og-theme.tsx, app/api/og/..., package.json, scripts/__tests__/theme-colors.test.ts
Theme primitives and shadows are revised, generated theme colors are validated and synchronized, and runtime, manifest, and Open Graph colors consume the generated tokens.
Mobile navigation and shell layout
components/bottom-nav.tsx, components/public-shell.tsx, components/mobile-shell.tsx, components/public-header.tsx, lib/__tests__/mobile-navigation-theme.test.ts
Bottom navigation styling and wiring, safe-area spacing, responsive shell widths, and the public mobile authentication/profile link are updated and tested.
Homepage and listing presentation
app/page.tsx, components/gamification-strip.tsx, components/listing-media.tsx, components/listing-card.tsx, components/listing-detail.tsx, components/winner-card.tsx, lib/__tests__/*media*.test.*, public/brand/*
The homepage hero, daily-drop card, gamification board, representative-photo disclosure, listing-card layout, winner media, and primary entry action are revised.
Cron scheduling and ingestion guard
vercel.json, README.md, app/api/cron/ingest/__tests__/route.test.ts
Ingestion and stale-expiry jobs run twice daily, documentation mirrors the schedules, and tests verify the schedules and disabled-ingestion response.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ListingFallbackRoute
  participant listingFallbackAssetUrl
  participant OwnedFallbackAsset
  Client->>ListingFallbackRoute: Request category fallback
  ListingFallbackRoute->>listingFallbackAssetUrl: Normalize category and resolve asset
  listingFallbackAssetUrl-->>ListingFallbackRoute: Return owned WebP URL
  ListingFallbackRoute-->>Client: Return 307 redirect with cache headers
  Client->>OwnedFallbackAsset: Fetch static WebP asset
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the main mobile marketplace and gamification release.
Description check ✅ Passed The description is mostly complete and matches the template, but it omits the required Closes # issue reference.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/gamified-marketplace-mobile

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Overhauls Sweepza’s mobile marketplace presentation, listing trust details, representative media, gamification, and scheduled ingestion lifecycle.

Changes:

  • Introduces jewel-themed responsive navigation and marketplace surfaces.
  • Adds owned fallback photography with explicit disclosure.
  • Schedules ingestion and expiration twice daily.

Validation: Static review and focused source tracing completed; commands were not rerun.

Blocked: Official attribution overlap, omitted eligibility facets, and stale cron documentation. Author should address these before independent review.

Reviewed changes

Copilot reviewed 19 out of 25 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
vercel.json Adds twice-daily ingest and expiration schedules.
lib/og-theme.tsx Synchronizes Open Graph colors.
lib/listing-media.ts Maps categories to owned fallback assets.
lib/__tests__/mobile-navigation-theme.test.ts Checks navigation and theme integration.
lib/__tests__/listing-media-presentation.test.ts Tests fallback mapping and redirects.
lib/__tests__/listing-media-component.test.tsx Tests media disclosure rendering.
components/public-shell.tsx Adds pinned public bottom navigation.
components/public-header.tsx Simplifies the mobile header.
components/mobile-shell.tsx Expands responsive widths and safe-area spacing.
components/listing-media.tsx Adds representative-photo fallback behavior.
components/listing-card.tsx Expands listing facts and card presentation.
components/gamification-strip.tsx Redesigns activity and badge progress.
components/bottom-nav.tsx Restyles responsive pinned navigation.
app/tokens.css Defines purple, gold, and velvet tokens.
app/page.tsx Reorders and redesigns marketplace content.
app/manifest.ts Updates PWA colors.
app/layout.tsx Updates browser theme colors.
app/icon.svg Applies the new icon palette.
app/api/images/listing-fallback/[category]/route.tsx Redirects generated fallbacks to static assets.
app/api/cron/ingest/__tests__/route.test.ts Verifies cron ordering and gating.

Comment thread components/listing-card.tsx
Comment thread components/listing-card.tsx Outdated
Comment thread vercel.json

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad1787ca04

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/page.tsx Outdated
Comment thread app/layout.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/listing-card.tsx (1)

468-509: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Attribution badge and new countdown/context chip can visually collide.

ListingMedia's own attribution badge renders at absolute bottom-2 right-2 (visible whenever a real, non-representative image has listing.imageAttribution set). The new context/countdown row added here sits at absolute inset-x-3 bottom-3 ... justify-between, placing the countdown chip in the same bottom-right corner. Neither element sets an explicit z-index, so whichever paints later in DOM order (the countdown row, added after ListingMedia) will overlap/obscure the attribution text for any listing that has both a real attributed image and an active countdown. Since "preserved official-media attribution" is an explicit goal for this cohort, this collision should be fixed (e.g., reposition the attribution badge — perhaps by extending the representativeLabelPosition-style prop pattern already added to ListingMedia — so it doesn't share screen space with the new bottom row).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/listing-card.tsx` around lines 468 - 509, Prevent the bottom-right
attribution badge rendered by ListingMedia from colliding with the
countdown/context row in the listing card. Update the ListingMedia positioning
API and its usage in the listing card, reusing the existing
representativeLabelPosition pattern or equivalent, so attributed real images
place their badge outside the bottom row while preserving attribution
visibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@components/listing-card.tsx`:
- Around line 468-509: Prevent the bottom-right attribution badge rendered by
ListingMedia from colliding with the countdown/context row in the listing card.
Update the ListingMedia positioning API and its usage in the listing card,
reusing the existing representativeLabelPosition pattern or equivalent, so
attributed real images place their badge outside the bottom row while preserving
attribution visibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 56a09eb9-3fdd-4ef8-98fc-08764ce78093

📥 Commits

Reviewing files that changed from the base of the PR and between 5dfe364 and ad1787c.

⛔ Files ignored due to path filters (1)
  • app/icon.svg is excluded by !**/*.svg
📒 Files selected for processing (24)
  • app/api/cron/ingest/__tests__/route.test.ts
  • app/api/images/listing-fallback/[category]/route.tsx
  • app/layout.tsx
  • app/manifest.ts
  • app/page.tsx
  • app/tokens.css
  • components/bottom-nav.tsx
  • components/gamification-strip.tsx
  • components/listing-card.tsx
  • components/listing-media.tsx
  • components/mobile-shell.tsx
  • components/public-header.tsx
  • components/public-shell.tsx
  • lib/__tests__/listing-media-component.test.tsx
  • lib/__tests__/listing-media-presentation.test.ts
  • lib/__tests__/mobile-navigation-theme.test.ts
  • lib/listing-media.ts
  • lib/og-theme.tsx
  • public/brand/sweepza-logo-mobile.webp
  • public/images/listing-fallbacks/cash-gift-card.webp
  • public/images/listing-fallbacks/electronics.webp
  • public/images/listing-fallbacks/general-prize.webp
  • public/images/listing-fallbacks/travel.webp
  • vercel.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 31 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

app/layout.tsx:54

  • This still selects browser chrome from the OS color-scheme, but the app selects its actual theme from the stored preference/local clock (lib/theme.tsx:34-53). A user who pins dark mode—or uses auto mode at night with a light OS—therefore gets sunrise browser chrome around a midnight UI, despite the synchronization acceptance criterion. Update the theme-color meta when resolved changes (and during the anti-flash bootstrap) rather than relying only on these media queries.
    {
      media: "(prefers-color-scheme: light)",
      color: THEME_COLORS.sunrise.paper,
    },
    {
      media: "(prefers-color-scheme: dark)",
      color: THEME_COLORS.midnight.paper,

app/page.tsx:300

  • The preview subtree is emitted before the page <h1>, and ListingCard itself emits an <h3> (the empty state emits another <h2>). CSS order changes only visual placement, so screen-reader/document order begins at level 2/3 before level 1. Keep the card visually first on mobile, but move the h1-containing grid child first in the markup and use the existing order classes for visual ordering.
            <h1 className="mt-5 max-w-[15ch] font-display text-[40px] leading-[0.98] tracking-[-0.055em] text-paper sm:text-[54px] lg:text-[62px]">
              Real sweeps. One bold daily run.
            </h1>

app/api/images/listing-fallback/[category]/route.tsx:16

  • This redirect removes the disclosure that was previously baked into the generated fallback image for consumers that do not render through ListingMedia. In particular, components/winner-card.tsx:23-40 uses categoryFallbackImageUrl directly and will now show the representative photo with the listing alt text—or even “Photo shared by …”—and no representative label. Preserve disclosure in the asset itself or update every direct fallback consumer to detect it, label it, and use representative alt text before redirecting the legacy route.
  const location = new URL(listingFallbackAssetUrl(category), request.url);

  return new Response(null, {
    status: 307,
    headers: {
      Location: location.toString(),
      "Cache-Control": "public, max-age=86400, s-maxage=31536000, immutable",

lib/listing-media.ts:100

  • Unknown categories matching inherited object keys do not fall back safely. For example, normalizeFallbackCategory("constructor") returns "constructor" because THEMES[category] sees Object.prototype.constructor; this lookup then selects the inherited function and produces an invalid /images/listing-fallbacks/function ... URL. Use an own-property check before indexing the asset map so every arbitrary route category resolves to the general asset.
  const normalizedCategory = normalizeFallbackCategory(category);
  const assetName =
    ASSET_BY_CATEGORY[normalizedCategory] ?? ASSET_BY_CATEGORY.other;

Comment thread app/layout.tsx Fixed
Comment thread app/layout.tsx Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/winner-card.tsx`:
- Around line 46-54: Update the ListingMedia invocation in the Winner Wall card
to pass the listing’s image attribution via the existing attribution prop,
matching ListingDetail’s use of listing.imageAttribution while preserving the
current representativePhoto behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b37c299c-96c4-44dd-aac3-b210c91be790

📥 Commits

Reviewing files that changed from the base of the PR and between ad1787c and 367c4d8.

⛔ Files ignored due to path filters (1)
  • lib/generated/theme-colors.ts is excluded by !**/generated/**
📒 Files selected for processing (23)
  • README.md
  • app/api/cron/ingest/__tests__/route.test.ts
  • app/api/og/sweeps/[slug]/route.tsx
  • app/layout.tsx
  • app/manifest.ts
  • app/page.tsx
  • components/listing-card.tsx
  • components/listing-detail.tsx
  • components/listing-media.tsx
  • components/winner-card.tsx
  • lib/__tests__/listing-media-component.test.tsx
  • lib/__tests__/listing-media-presentation.test.ts
  • lib/__tests__/listing-seo.test.ts
  • lib/__tests__/mobile-navigation-theme.test.ts
  • lib/__tests__/winner-card-media.test.tsx
  • lib/listing-media.ts
  • lib/listing-seo.ts
  • lib/og-theme.tsx
  • lib/theme.tsx
  • package.json
  • scripts/__tests__/theme-colors.test.ts
  • scripts/sync-theme-colors.mjs
  • scripts/theme-colors-lib.mjs
🚧 Files skipped from review as they are similar to previous changes (6)
  • app/manifest.ts
  • app/api/cron/ingest/tests/route.test.ts
  • lib/tests/listing-media-presentation.test.ts
  • lib/listing-media.ts
  • lib/tests/mobile-navigation-theme.test.ts
  • components/listing-card.tsx

Comment thread components/winner-card.tsx
@AutomatedEmpires
AutomatedEmpires merged commit a6351f4 into main Jul 29, 2026
9 checks passed
@AutomatedEmpires
AutomatedEmpires deleted the codex/gamified-marketplace-mobile branch July 29, 2026 01:46

This branch was successfully deployed

1 active deployment
Preview — 79e908da Deployed Jul 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants