Skip to content

feat(card): real FOMO door tally on rejection screen#2307

Merged
Hugo0 merged 3 commits into
mainfrom
feat/door-tally-real-counts
Jun 29, 2026
Merged

feat(card): real FOMO door tally on rejection screen#2307
Hugo0 merged 3 commits into
mainfrom
feat/door-tally-real-counts

Conversation

@Hugo0

@Hugo0 Hugo0 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What

The Berghain "not tonight" card-rejection screen showed a hardcoded door tally — CardRejectionScreen defaulted applicants = 213, admitted = 7 and card/page.tsx never passed real values, so the static defaults always rendered.

Now it's real / semi-real:

  • "tried" = the real waitlist size (/cardwaitlistTotal) inflated for FOMOmax(213, round(waitlistTotal × 5)). Mirrors how /shhhhh's ScarcityCounter already fakes scarcity ("only 20 a week").
  • "got in" = the real number admitted (/cardadmittedTotal), shown verbatim.

The inflation lives in a new pure helper src/components/Card/doorTally.utils.ts (inflateApplicants / computeDoorTally), so it's deterministic per render (no Math.random, no jitter). A 213 / 7 fallback covers the still-loading window. Hardcoded 213/7 defaults removed from the component (props are now waitlistTotal / admittedTotal, both optional so existing mocks + the BE-deploys-first rollout tolerate undefined).

/dev/rejection-builder updated: sliders now drive the real backend counts and the panel previews the inflated "renders as: N tried · M got in".

Design (3 lines)

  • Counts come from GET /cardwaitlistTotal (all who joined the queue) + admittedTotal (all released/granted).
  • Exaggeration: tried = max(213, round(waitlistTotal × 5)); got in = admittedTotal (verbatim). ×5 (not ×3) so the real count clears the 213 floor at the current prod waitlist size (~55 → 275 > 213); below that the floor would mask it and the tally would look frozen at 213.
  • Deterministic: pure fn of the API counts — same input, same output every render.

Cross-repo — BE must deploy first

⚠️ Depends on peanut-api-ts PR #1085 (feat/door-tally-real-counts), which adds waitlistTotal / admittedTotal to GET /card. Merge + deploy that first. Until then the FE reads undefined and renders the 213 / 7 fallback — safe (no crash, just the old static-looking copy), but not the real numbers.

Tests

  • src/components/Card/__tests__/doorTally.utils.test.ts: ×5 multiplier, floor clamp, "clears the floor at ~55" case, rounding, fallbacks (undefined/NaN/0/negative/Infinity), determinism, admitted=0 is real (not the fallback). Full tsc --noEmit + full npm test green; prettier clean.

eslint note

The repo-wide eslint check is red on pre-existing violations in files this PR does not touch (add-money/*, dev/debug/page.tsx). None of this PR's files appear in the eslint output, and the required ci-success aggregate passes regardless (eslint is advisory per the ruleset).

The 'not tonight' Berghain screen rendered a hardcoded '213 tried · 7 got in'
(props defaulted to 213/7 and the page never passed real values). Now /card
returns waitlistTotal + admittedTotal; the screen inflates 'tried' for FOMO
(real waitlist size x3, floored at 213 — mirrors the /shhhhh ScarcityCounter
fake-scarcity flex) and shows the real 'got in'. Inflation is a pure,
deterministic fn so it never jitters between renders; a sane 213/7 fallback
covers the still-loading window. /dev/rejection-builder now drives the real
counts and previews the inflated result.

Requires the BE PR (waitlistTotal/admittedTotal on /card) deployed first.
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jun 29, 2026 2:48pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 9 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6e157deb-cd21-416b-bf5e-0048ad2af465

📥 Commits

Reviewing files that changed from the base of the PR and between 2ec8b5d and 09345b9.

📒 Files selected for processing (6)
  • src/app/(mobile-ui)/card/page.tsx
  • src/app/(mobile-ui)/dev/rejection-builder/page.tsx
  • src/components/Card/CardRejectionScreen.tsx
  • src/components/Card/__tests__/doorTally.utils.test.ts
  • src/components/Card/doorTally.utils.ts
  • src/services/card.ts

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

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5843.8 → 5850.46 (+6.66)
Findings: +1 net (+14 new, -13 resolved)

🆕 New findings (14)

  • critical complexity — src/app/(mobile-ui)/card/page.tsx — CC 94, MI 57.49, SLOC 374
  • high hotspot — src/app/(mobile-ui)/card/page.tsx — 38 commits, +1057/-465 lines since 6 months ago
  • medium high-mdd — src/app/(mobile-ui)/card/page.tsx:52 — CardPage: MDD 101.5 (uses across many lines from declarations)
  • medium high-mdd — src/app/(mobile-ui)/dev/rejection-builder/page.tsx:26 — RejectionBuilderPage: MDD 75.0 (uses across many lines from declarations)
  • medium structural-dup — app/(mobile-ui)/dev/rejection-builder/page.tsx:179 — 38 duplicate lines / 192 tokens with app/(mobile-ui)/dev/share-builder/page.tsx:407
  • medium high-mdd — src/components/Card/CardRejectionScreen.tsx:53 — CardRejectionScreen: MDD 37.2 (uses across many lines from declarations)
  • medium complexity — src/app/(mobile-ui)/dev/rejection-builder/page.tsx — CC 23, MI 72.63, SLOC 70
  • medium complexity — src/components/Card/CardRejectionScreen.tsx — CC 21, MI 56.97, SLOC 102
  • low high-dlt — src/components/Card/CardRejectionScreen.tsx:53 — CardRejectionScreen: DLT 24 (calls 24 distinct functions — high context load)
  • low high-dlt — src/components/Card/CardRejectionScreen.tsx:102 — handleAppeal: DLT 17 (calls 17 distinct functions — high context load)
  • low high-mdd — src/components/Card/CardRejectionScreen.tsx:102 — handleAppeal: MDD 14.1 (uses across many lines from declarations)
  • low structural-dup — app/(mobile-ui)/dev/rejection-builder/page.tsx:45 — 11 duplicate lines / 59 tokens with app/(mobile-ui)/dev/share-builder/page.tsx:156
  • low structural-dup — services/card.ts:68 — 8 duplicate lines / 58 tokens with services/card.ts:97
  • low missing-return-type — src/app/(mobile-ui)/dev/rejection-builder/page.tsx:26 — RejectionBuilderPage: exported fn missing return type annotation

✅ Resolved (13)

  • src/app/(mobile-ui)/card/page.tsx — CC 94, MI 57.5, SLOC 374
  • src/app/(mobile-ui)/card/page.tsx — 37 commits, +1055/-465 lines since 6 months ago
  • src/app/(mobile-ui)/card/page.tsx:52 — CardPage: MDD 101.2 (uses across many lines from declarations)
  • app/(mobile-ui)/dev/rejection-builder/page.tsx:156 — 38 duplicate lines / 192 tokens with app/(mobile-ui)/dev/share-builder/page.tsx:407
  • src/components/Card/CardRejectionScreen.tsx:49 — CardRejectionScreen: MDD 37.2 (uses across many lines from declarations)
  • src/app/(mobile-ui)/dev/rejection-builder/page.tsx — CC 22, MI 73.06, SLOC 64
  • src/components/Card/CardRejectionScreen.tsx — CC 21, MI 57.4, SLOC 98
  • src/components/Card/CardRejectionScreen.tsx:49 — CardRejectionScreen: DLT 23 (calls 23 distinct functions — high context load)
  • src/components/Card/CardRejectionScreen.tsx:95 — handleAppeal: DLT 17 (calls 17 distinct functions — high context load)
  • src/components/Card/CardRejectionScreen.tsx:95 — handleAppeal: MDD 14.1 (uses across many lines from declarations)
  • app/(mobile-ui)/dev/rejection-builder/page.tsx:40 — 11 duplicate lines / 59 tokens with app/(mobile-ui)/dev/share-builder/page.tsx:156
  • services/card.ts:58 — 8 duplicate lines / 58 tokens with services/card.ts:87
  • src/app/(mobile-ui)/dev/rejection-builder/page.tsx:25 — RejectionBuilderPage: exported fn missing return type annotation

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Card/doorTally.utils.ts 0.0 5.3 +5.3
src/app/(mobile-ui)/dev/rejection-builder/page.tsx 5.0 5.8 +0.7

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1618 ran, 0 failed, 0 skipped, 24.7s

📊 Coverage (unit)

metric %
statements 54.5%
branches 37.1%
functions 42.3%
lines 54.4%
⏱ 10 slowest test cases
time test
3.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
0.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.4s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in updateUserById
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.1s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Manteca PIX form ready shows merchant card + amount input + pay button
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle too long for US account
0.1s src/services/__tests__/resolveClaimLink.test.ts › restores the pristine password after a redirect mangles the fragment
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid US account with spaces
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid Italian IBAN
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

CI typecheck caught existing CardInfoResponse mocks (e.g. cardState.utils
.test.ts) breaking because the new fields were required. Optional is also the
correct contract: the BE deploys first, but the FE must tolerate undefined
during the rollout window (and for any older API) — computeDoorTally already
falls back to 213/7. Full `tsc --noEmit` + `npm test` now green.
At x3 the real number is masked by the 213 floor (prod waitlist ~55 -> 55x3
= 165 < 213, so it just renders the hardcoded-looking 213). x5 = 275 clears
the floor, so the real inflated count actually shows. Counts source +
floor + fallback unchanged.
@Hugo0 Hugo0 merged commit 2793c87 into main Jun 29, 2026
20 of 23 checks passed
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.

1 participant