Skip to content

fix(promocode): preserve ticket type chip labels after validation error#923

Merged
smarcet merged 1 commit into
masterfrom
fix/promocode-ticket-types-undefined-on-validation-error
May 8, 2026
Merged

fix(promocode): preserve ticket type chip labels after validation error#923
smarcet merged 1 commit into
masterfrom
fix/promocode-ticket-types-undefined-on-validation-error

Conversation

@caseylocker
Copy link
Copy Markdown

@caseylocker caseylocker commented May 8, 2026

ref: https://app.clickup.com/t/86b9v01bt
ref: https://jam.dev/c/b1566c62-c802-4f43-b093-9c5c2f5a5fde

Summary

After a server validation error (e.g. POST returning 412 for a missing required code), the Promo Code form's "Ticket Types" chips would revert to displaying the literal string undefined for each previously-selected option.

Two-layer corruption:

  1. PromocodeForm.handleSubmit mutated state.entity.allowed_ticket_types from [{id, name, …}] to [id, …] in place, before calling onSubmit.
  2. savePromocode dispatches UPDATE_PROMOCODE (start action) with that same entity reference; the reducer (promocode-reducer.js:131-133) spreads the now-dehydrated payload into state.entity. On a 412 no PROMOCODE_ADDED re-hydration runs, so the form re-renders with raw IDs. uicore's TicketTypesInput defaults getOptionLabel to `${e.name}` — for a raw number (197).name is undefined, the template literal coerces it to the string "undefined", and that's what every chip displays.

Fix

  • Remove the mutation block in handleSubmit — the form hands onSubmit the unmodified entity.
  • Move the ID extraction into normalizeEntity in the action layer, alongside the existing tags mapping. Only the request body is dehydrated; redux state and the form's state.entity stay hydrated.

This also incidentally cures a latent symptom on the PUT path (existing-code edits): PROMOCODE_UPDATED doesn't re-hydrate from response, so post-fix successful PUTs also keep chips intact.

Test plan

  • Added regression test in promocode-form.integration.test.js asserting onSubmit receives an entity whose allowed_ticket_types retains {id, name} objects. Verified to fail pre-fix (received [197]), pass post-fix.
  • All 31 promocode-form integration tests pass.
  • Manually reproduced the original Jam scenario in dev: created a DOMAIN_AUTHORIZED_PROMO_CODE with the email domains pre-populated and "Early Access Members" added, clicked Save without typing code, dismissed the 412 toast — chip still reads "Early Access Members" (was "undefined" pre-fix).

Refs

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where promotional code ticket type data was being incomplete during form submission. Ticket type information is now properly preserved with all details intact.
  • Tests

    • Added regression test to ensure promotional code forms correctly handle allowed ticket type data.

PromocodeForm.handleSubmit mutated state.entity.allowed_ticket_types
from [{id,name}] to [id] before calling onSubmit. The mutated entity
flowed into redux via UPDATE_PROMOCODE (start action), and on a 412
response no re-hydration happened — the form re-rendered with raw IDs,
and TicketTypesInput's getOptionLabel template literal `${e.name}`
coerced (number).name into the literal string "undefined" for each chip.

Move the ID extraction into normalizeEntity in the action layer so only
the request body is dehydrated; redux and form state stay hydrated.

Adds a regression test asserting onSubmit receives an entity whose
allowed_ticket_types retains {id,name} objects.

Refs ClickUp 86b9v01bt
Jam: https://jam.dev/c/b1566c62-c802-4f43-b093-9c5c2f5a5fde

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f484305-f737-45cb-94db-87cc45f08fe5

📥 Commits

Reviewing files that changed from the base of the PR and between 8994754 and c71af86.

📒 Files selected for processing (3)
  • src/actions/promocode-actions.js
  • src/components/forms/promocode-form/__tests__/promocode-form.integration.test.js
  • src/components/forms/promocode-form/index.js
💤 Files with no reviewable changes (1)
  • src/components/forms/promocode-form/index.js

📝 Walkthrough

Walkthrough

This PR refactors where allowed_ticket_types normalization occurs in promo code submission. The form's handleSubmit no longer converts ticket type objects to bare IDs; instead, the action layer's normalizeEntity function now performs this conversion. A regression test verifies the form preserves object shapes during submission.

Changes

Promo Code Ticket Types Normalization Refactor

Layer / File(s) Summary
Form Submission
src/components/forms/promocode-form/index.js
Removed pre-submit normalization that converted allowed_ticket_types to ID-only values; submission now passes ticket objects as-is.
Action Normalization
src/actions/promocode-actions.js
normalizeEntity now normalizes allowed_ticket_types array entries to identifiers, extracting .id from objects or using primitives directly.
Test Validation
src/components/forms/promocode-form/__tests__/promocode-form.integration.test.js
Added regression test verifying handleSubmit preserves allowed_ticket_types object shape with both id and name fields intact.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A hop and a shuffle, the ticket types dance,
From form to the action, they take their new stance,
Objects preserved with id and with name,
The refactor is tidy, the test claims the fame! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly reflects the main change: preserving ticket type chip labels (the fix) after validation errors (the context), which is the primary problem solved by moving ID extraction from handleSubmit to normalizeEntity.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/promocode-ticket-types-undefined-on-validation-error

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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 and usage tips.

Copy link
Copy Markdown

@smarcet smarcet left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a Promo Code form regression where selected “Ticket Types” chips could display "undefined" after a server-side validation error by preventing in-place dehydration of allowed_ticket_types in component state and moving dehydration to the action normalization layer.

Changes:

  • Removed allowed_ticket_types in-place mutation from PromocodeForm.handleSubmit.
  • Added allowed_ticket_types dehydration to normalizeEntity in promocode-actions.js so only the API request body is normalized.
  • Added an integration regression test to ensure onSubmit receives a hydrated allowed_ticket_types array of {id, name} objects.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/components/forms/promocode-form/index.js Stops mutating state.entity.allowed_ticket_types during submit, preserving hydrated option objects across validation failures.
src/actions/promocode-actions.js Normalizes allowed_ticket_types in the action layer to send IDs to the API without corrupting redux/form state.
src/components/forms/promocode-form/tests/promocode-form.integration.test.js Adds a regression test asserting handleSubmit does not dehydrate allowed_ticket_types before calling onSubmit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


if (Array.isArray(entity.allowed_ticket_types)) {
normalizedEntity.allowed_ticket_types = entity.allowed_ticket_types.map(
(tt) => (typeof tt === "object" && tt !== null ? tt.id : tt)
Copy link
Copy Markdown

@santipalenque santipalenque left a comment

Choose a reason for hiding this comment

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

LGTM

@smarcet smarcet merged commit 6b339c8 into master May 8, 2026
13 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.

4 participants