doc-site
Developer guide

Booking form

How the React camp booking form is mounted, configured, validated and submitted, the requests it makes, and its known issues.

The booking form is a React 18 app built from src/ into theme/assets/camp-booking.bundle.js, with its dependencies in vendors-booking.bundle.js. It runs only on pages using layout/theme.product.liquid.

The Book camp button on the product page opens the booking form. The form calls Google Places for school search, shopify-zoho-integration for club search and asc-api-membership-pass for saved children, then adds one cart line per participant and sends the customer to checkout.

Start-up

  1. theme.product.liquid renders an empty #camp-booking-root and writes window.theme.campBookingConfig from the ASC - Camp Booking theme settings.

  2. src/js/camp-booking.js defines window.startCampBooking.

  3. The Book Camp button, .book-camp-cta in theme/snippets/product-atc.liquid, calls it through theme/assets/main-product.js. A start_booking query parameter on the URL calls it on load.

  4. startCampBooking loads the Google Maps JavaScript API with the Places library, reads the participant count from #qty, and renders <CampBooking> into the root with:

    {
      ...window.theme.campBookingConfig,
      api: window.theme.api,
      customer: window.Shopify.customer,
    }
  5. After 100 milliseconds it adds the active class to the root, which fades the form in.

#qty offers 1 to 10 participants, capped at the variant's stock when inventory is tracked. It is rendered only when the variant is available and In Season is not false.

Components

CampBooking                  src/components/CampBooking.tsx
└─ ParticipantsContext       src/components/ParticipantsContext.tsx
   ├─ StickyTitle            desktop header, close button, Google reviews badge
   └─ BookingBody            hero image
      ├─ MobileTitle
      └─ one per participant, only the current one visible
         ├─ SavedChildrenDropdown   logged-in customers with saved children
         └─ ParticipantDetailsForm  src/components/ParticipantDetails.tsx
            ├─ SchoolAutocomplete   Google Places
            ├─ ClubsAutocomplete    shopify-zoho-integration
            └─ PreviousParticipantButton

Fields and validation

Validation uses react-hook-form with the Zod schema from src/components/models/ParticipantDetailsSchema.tsx, built from the theme settings.

Field keyInputRule
First Name, Last NameText, 255 charactersRequired
Middle NameTextOptional
Date of BirthDate input on mobile, day, month and year on desktopAge between date_of_birth_min_age and date_of_birth_max_age, defaults 4 and 16
GenderMale, Female, OtherRequired
Has Confidential Gender Info, Confidential Gender InfoSwitch and text areaOptional, 255 characters
SchoolGoogle Places searchRequired
Grade0 (Prep) to 12Defaults to 0
Club or Team NameClub search, or free textRequired
_ClubIdHiddenZoho club id when a listed club is chosen
Playing ExperienceBeginner, Intermediate, AccomplishedRequired
Has Friends Attending, Friends AttendingSwitch and text areaOptional
Has Medical Conditions, Medical ConditionsSwitch and text areaOptional

The field key is also the cart line property name. Zoho mapping in asc-api-src-zoho-integration reads those names, so renaming a key is a change in both repositories.

Date of Birth defaults to today, so it fails validation until the parent changes it.

Submitting

  1. Each participant step calls updateParticipantDetails. The last step sets readyToSubmit.
  2. finalSubmit in CampBooking.tsx builds one cart item per participant, with quantity 1 and the fields as properties. It formats Date of Birth as dd MMMM yyyy and drops the Has … switches, keeping the related text only when its switch was on.
  3. If the children were typed rather than chosen from saved children, it starts saving each child to asc-api-membership-pass. The chain of saves is not awaited.
  4. It posts the items to {Shopify.routes.root}cart/add.js and sets window.location.href to the cart.

The exact properties are listed in Booking line item properties.

Requests

RequestWhenService
Google Maps JavaScript API with PlacesForm opensGoogle
GET {ZOHO_INTEGRATION_API_ENDPOINT}/clubs/search?q=…&limit=10&offset=0Typing in club search, debounced 500 msshopify-zoho-integration
GET {theme.api.root}v1/customers/{id}/childrenForm opens, logged inasc-api-membership-pass
POST {theme.api.root}v1/customers/{id}/children/saveSubmit, new children onlyasc-api-membership-pass
POST {Shopify.routes.root}cart/add.jsSubmitShopify

Saved-children requests send text/plain bodies containing the JSON encoded twice with btoa.

ZOHO_INTEGRATION_API_ENDPOINT and GOOGLE_PLACES_API_KEY are inlined at build time from src/.env. src/js/camp-booking.js also contains the Maps key as a literal in its loader, so a key change needs both updated and a rebuild.

Saved children

The service stores each child as a child metaobject and lists them in the customer metafield children.saved_details. GET …/children returns each child's fields with title-cased names, which SavedChildrenDropdown.tsx maps back to form keys.

Returned fieldForm key
Date Of BirthDate of Birth
Club Team NameClub or Team Name
Has Confidential Gender InformationHas Confidential Gender Info
Confidential Gender InformationConfidential Gender Info
OthersSame name

A logged-in customer can choose between "Choose from kids" and "Add new kids" for each participant. loadedFromSavedChildren is a single flag for the whole booking that reflects the last choice made. In a booking that mixes saved and new children, either none of the new children are saved, or the saved children are saved again as duplicates, depending on which was chosen last.

Known issues

These were found by reading the code and have not been confirmed against production.

IssueEffectWhere
The client sends Has Confidential Gender Info. The service reads Has Confidential Gender InformationConfidential gender information is never saved with a childCampBooking.tsx, service children.service.ts
The service does not store Friends Attending or _ClubIdThose values are blank when a saved child is chosenService children.service.ts
Saves are not awaited before the redirect to the cartLater saves in a multi-child booking may be cancelled by the page changeCampBooking.tsx, finalSubmit
btoa only accepts characters up to U+00FFA name containing, for example, an emoji or "ł" makes that save fail silentlyCampBooking.tsx
Text areas call replace(',', '\r\n') on each changeTyped commas become line breaks. In pasted text only the first comma is replacedParticipantDetails.tsx
A second schema, participantDetailsSchema, and validateInputs in CampBooking.tsx are never calledNone. Dead code that can be mistaken for the live rulesCampBooking.tsx
lodash is imported but not declared in src/package.jsonBuild breaks if misc/ is removedClubsAutocomplete.tsx
Babel strips types without checking themType errors never fail the buildsrc/babel.config.json

Changing the form

ChangeFiles
A label or placeholderNo code. Theme settings, ASC - Camp Booking
A new settingtheme/config/settings_schema.json, theme/layout/theme.product.liquid config object, src/components/types.d.ts
Options for Gender, Grade or Playing Experiencesrc/components/ParticipantDetails.tsx
Validationsrc/components/models/ParticipantDetailsSchema.tsx
A new fieldSchema, ParticipantDetails.tsx, CampBooking.tsx property building, then the Zoho mapping in asc-api-src-zoho-integration and, if it should be remembered, asc-api-membership-pass and SavedChildrenDropdown.tsx

After any change, run the build and commit theme/assets/camp-booking.bundle.js and vendors-booking.bundle.js in the theme repository. See Releasing a change.

On this page