PBC Quotes Web Components - Self-Contained Demo

You're viewing 1.3.0

Read the integration guide for 1.3.0 and its component reference. For support status, see the version list.

Loader tags for 1.3.0: pin the exact version, or follow a rollup
EnvironmentExact (1.3.0)Latest 1.3.xLatest 1.x
dev<script src="https://dev-logistics-pbc.awsmanlog10np.manheim.com/v/1.3.0/quotespbc-ui-loader.js"></script> <script src="https://dev-logistics-pbc.awsmanlog10np.manheim.com/v/1.3/quotespbc-ui-loader.js"></script> <script src="https://dev-logistics-pbc.awsmanlog10np.manheim.com/v/1/quotespbc-ui-loader.js"></script>
stage<script src="https://stage-logistics-pbc.awsmanlog10np.manheim.com/v/1.3.0/quotespbc-ui-loader.js"></script> <script src="https://stage-logistics-pbc.awsmanlog10np.manheim.com/v/1.3/quotespbc-ui-loader.js"></script> <script src="https://stage-logistics-pbc.awsmanlog10np.manheim.com/v/1/quotespbc-ui-loader.js"></script>
prod<script src="https://prod-logistics-pbc.awsmanlog10.manheim.com/v/1.3.0/quotespbc-ui-loader.js"></script> <script src="https://prod-logistics-pbc.awsmanlog10.manheim.com/v/1.3/quotespbc-ui-loader.js"></script> <script src="https://prod-logistics-pbc.awsmanlog10.manheim.com/v/1/quotespbc-ui-loader.js"></script>

Release notes: 1.3.0

MINOR: New, backward-compatible features. Safe to adopt within this major.

F325061 (Correlation fields on OrderConfirmed and OrderFailed)

F322342 / US2156061 (CP/CSA delivery address: ZIP and address max length)

The CP/CSA Transportation Quote widget's (<quote-order-component>) delivery address form now limits what can be typed to what the quote service accepts, and says what to fix instead of "Unable to get transportation quote at this time. Please try again later.":

No host-facing contract change → PATCH. No attribute, event or type is added, renamed, removed or retyped. Two host-observable differences: a host-supplied delivery Address Line 1 or 2 over 75 characters now gets prices, instead of the unavailable screen; and a host move with no delivery street address shows "Address Required" straight away. <ready-contact-quote> and <ready-zipcode-quote> are unaffected.

US2174643 (Integration guide and host-facing help on each version's example page)

Each version now publishes its own integration guide at /v/X.Y.Z/integration-guide.html, and the version list links to it. The guide has been corrected: every example uses the attributes the widgets actually take (api-config and quote-request; the old token, pbc-url and partnerid attributes never existed), every loader URL names the version you're reading about, and a new section explains when to pin /v/X.Y.Z/ and when to use the /v/M.m/ and /v/M/ rollups. It ends with a property and event reference generated from the components' code.

Each version's example page now opens with "You're viewing X.Y.Z" and copy-ready loader tags for dev, stage and prod. On a deployed page the forms point at that environment's BFF, so pasting a token is enough to try a widget. A table explains the "currently unavailable" and "Contact Ready Logistics" states, each form can copy its setup as HTML, React or Angular, and each event log shows the message types it can receive. On the production page, a warning beside the quote/order form says orders placed there are real.

No host-facing contract change → MINOR (a new capability). No tag, attribute, event or loader path changes. Versions 1.0.x–1.2.x have no guide page.

US2167551 (Version index page and per-version release notes)

The PBC's address now shows a list of its published versions, newest first. Each one says whether it's Current, Supported, Superseded or Unsupported, and links to that version's example page. From 1.3.0 on, each version's example page also carries its own release notes and changelog, so you can see what changed before you move a pin. Versions 1.0.x–1.2.x still load exactly as before, but they aren't on the list.

No host-facing contract change → MINOR (a new capability). No tag, attribute, event or loader path changes. Pinned /v/X.Y.Z/ URLs and the /v/M/ and /v/M.m/ rollups behave as before.

Changelog: 1.3.0

F325061 (Correlation fields on OrderConfirmed and OrderFailed)

OrderConfirmed and OrderFailed now say which order they are about. quote_bus is shared by the whole page, so every tab or modal hears every order outcome; until now a <quote-order-component> host couldn't tell whether a confirmation was for its own request.

Additive host-facing change → MINOR. Only new optional fields are added. Nothing is removed, renamed or retyped, and OrderSubmitted is unchanged.

Added — for hosts (<quote-order-component>)

For maintainers

F322342 / US2156061 (CP/CSA delivery address: ZIP and address max length)

Changed — behaviour (<quote-order-component> only)

Internal

US2174643 (Integration guide and host-facing help on each version's example page)

For hosts

For maintainers

US2167551 (Version index page and per-version release notes)

For hosts

For maintainers

What you see, and why

What you see Why
"This transportation quote is currently unavailable. Please try again later." The token in api-config is missing, expired or invalid, or quote-request is missing the move details: at least two stops and one vehicle. Fix your configuration; the shipper cannot resolve this by calling.
"Unable to retrieve transportation pricing at this time." with a Contact Ready Logistics link No transport option could be priced for this move, or the pricing service failed. The configuration is accepted; the shipper can try again or call.
A message under a field (ZIP code, phone, email) That input is invalid. The shipper corrects it and continues; nothing is sent until it is valid.

Ready contact Quote Component Test Form

Test all props: Update the values below and click "Update Component" to test different prop values

The widget shows "This transportation quote is currently unavailable" until a token is entered.

LIVE COMPONENT PREVIEW:

Ready Contact Quote — Event Log

Message types and payloads
Example Event Payload

Quote Created Event:

### Example Event Payload

// Example of event.data when a quote is successfully created
{
  "eventType": "QuoteCreated",
  "eventBody": {
    "currency": "USD",
    "discount": 0,
    "discountCurrency": "USD",
    "distance": 11,
    "distanceLabel": "MI",
    "expirationDate": "2025-09-22T18:31:21Z",
    "maxDaysToDeliver": 3,
    "minDaysToDeliver": 2,
    "price": 93,
    "quoteId": "f2d1cce1-e1fe-4105-8070-7c6617f6dc11",
    "quoteRef": "202509001623111",
    "quoteSubType": "",
    "quoteType": "Standard",
    "stops": [
      {
        "distance": 0,
        "address1": "",
        "address2": "",
        "city": "Phoenix",
        "country": "US",
        "refId": "S1",
        "state": "AZ",
        "zip": "85044"
      },
      {
        "distance": 11,
        "distanceLabel": "MI",
        "address1": "",
        "address2": "",
        "city": "Phoenix",
        "country": "US",
        "refId": "S2",
        "state": "AZ",
        "zip": "85040"
      }
    ],
    "vehicles": [
      {
        "currency": "USD",
        "description": "1991/Acura/Legend/L",
        "descriptionDetails": {
          "make": "Acura",
          "model": "Legend",
          "subModel": "L",
          "year": "1991"
        },
        "discount": 0,
        "discountCurrency": "USD",
        "predictedBasePrice": 0,
        "price": 93,
        "priceLineItems": [
          {
            "currency": "USD",
            "description": "FixedRate",
            "price": 93
          }
        ],
        "vehicleSize": "S",
        "weight": "3300",
        "weightUnit": "pounds",
        "dropOffId": "S2",
        "isEnclosed": false,
        "isOperable": true,
        "pickUpId": "S1",
        "vin": "JH4KA7561MC010714"
      }
    ]
  }
}

Quote Error Event:

// Example of event.data when an error occurs during quote processing
{
  "eventType": "QuoteError",
  "eventBody": {
    "errors": [
      {
        "code": "quotes_api.bad_request",
        "message": "An error occurred while calculating distance: Route at index 0: Stop at index 1: No matching postal code found Problem: [N/A] 11111 , ErrorId:fc26f1b3-5226-43b2-8837-269cc14b8c2d"
      }
    ]
  }
}

Ready Zipcode Quote Component Test Form

Test zipcode props: Update the values below and click "Update Zipcode Component" to test different prop values

The widget shows "This transportation quote is currently unavailable" until a token is entered.

Parent-supplied ZIP (VDP mode — F306320 AC-4 / AC-5)

Sets quote-request on the live component, as an OVE VDP would. Tick VDP Mode above and press Update Zipcode Component first, then change the ZIP here — the quote refreshes with no remount and no GO button. Clear it and the blue box stays put, showing “Enter a zip code to receive a delivery estimate.” in place of the price; enter a partial ZIP (e.g. 3032) and it does the same, since nothing was quoted.

LIVE ZIPCODE COMPONENT PREVIEW:

Ready Zipcode Quote — Event Log

Message types and payloads
Example Event Payload

Quote Created Event:

### Example Event Payload

// Example of event.data when a quote is successfully created
{
  "eventType": "QuoteCreated",
  "eventBody": {
    "currency": "USD",
    "discount": 0,
    "discountCurrency": "USD",
    "distance": 11,
    "distanceLabel": "MI",
    "expirationDate": "2025-09-22T18:31:21Z",
    "maxDaysToDeliver": 3,
    "minDaysToDeliver": 2,
    "price": 93,
    "quoteId": "f2d1cce1-e1fe-4105-8070-7c6617f6dc11",
    "quoteRef": "202509001623111",
    "quoteSubType": "",
    "quoteType": "Standard",
    "stops": [
      {
        "distance": 0,
        "address1": "",
        "address2": "",
        "city": "Phoenix",
        "country": "US",
        "refId": "S1",
        "state": "AZ",
        "zip": "85044"
      },
      {
        "distance": 11,
        "distanceLabel": "MI",
        "address1": "",
        "address2": "",
        "city": "Phoenix",
        "country": "US",
        "refId": "S2",
        "state": "AZ",
        "zip": "85040"
      }
    ],
    "vehicles": [
      {
        "currency": "USD",
        "description": "1991/Acura/Legend/L",
        "descriptionDetails": {
          "make": "Acura",
          "model": "Legend",
          "subModel": "L",
          "year": "1991"
        },
        "discount": 0,
        "discountCurrency": "USD",
        "predictedBasePrice": 0,
        "price": 93,
        "priceLineItems": [
          {
            "currency": "USD",
            "description": "FixedRate",
            "price": 93
          }
        ],
        "vehicleSize": "S",
        "weight": "3300",
        "weightUnit": "pounds",
        "dropOffId": "S2",
        "isEnclosed": false,
        "isOperable": true,
        "pickUpId": "S1",
        "vin": "JH4KA7561MC010714"
      }
    ]
  }
}

Quote Error Event:

// Example of event.data when an error occurs during quote processing
{
  "eventType": "QuoteError",
  "eventBody": {
    "errors": [
      {
        "code": "quotes_api.bad_request",
        "message": "An error occurred while calculating distance: Route at index 0: Stop at index 1: No matching postal code found Problem: [N/A] 11111 , ErrorId:fc26f1b3-5226-43b2-8837-269cc14b8c2d"
      }
    ]
  }
}

Responsive Layout Test (F306320)

The widget stacks the quote under the ZIP entry when its container is squarish, and collapses to a single ~60px strip — quote to the right of the ZIP entry — at 800px and above. The switch is the component's wide-breakpoint prop (default 800), measured against the component's own width, so resizing the browser window proves nothing on its own: drag the container's bottom-right corner or use the presets.

One threshold, and the whole quote on a single line from it — logo, price, delivery estimate, vertical rule, disclaimer. The card is a flat 60px at every width from 800px up. That fit is bought with smaller type in the strip only (delivery 11px, disclaimer 8px, price 16px, against 16/10/24 in the stacked card), leaving comfortable slack at 800px. The logo is deliberately not scaled: at 30px it is what sets the strip's height, so shrinking it would take the card under 60px.

Container width:

RESIZABLE CONTAINER — drag the bottom-right corner

Quote / Order Component Test Form (F255314)

Test all props: Update the values below and click "Update Component" to render the component with a different configuration. The BFF URL defaults to this host's own BFF (https://localhost:7100 when served locally under Aspire).

This is the production page. Orders placed from this form are real orders.

The widget shows "This transportation quote is currently unavailable" until a token is entered.

LIVE COMPONENT PREVIEW:

Quote / Order — Event Log

Message types and payloads
eventType eventBody When
pbc:ready (none) Loaded and configured. Also fires again after each re-quote
pbc:error { reason: 'unavailable' } Config/token rejected, or no option could be priced
MultiQuoteCreated MultiQuoteResponse Quotes returned
QuoteError { errors: [{ code, message }] } Quote request failed — carries the reason
OrderSubmitted { order } Each submission attempt
OrderConfirmed OrderResponse Order placed
OrderFailed { status, error, orderId?, confirmationNumber?, quoteId?, itemRef?, shipperId?, vehicle?, order } Order rejected, or could not be sent
Four things to know before you write the handler

1. OrderSubmitted fires once per attempt, not per order. Submission is not idempotent, so a shipper retrying after a failure produces a second event for the same order. Deduplicate on quoteId if you record attempts.

2. OrderFailed can arrive with NO preceding OrderSubmitted. Two cases reject the order before anything is sent — an invalid contact, and a widget that has lost its api-config. If your handler marks an order "pending" on OrderSubmitted and resolves it on OrderConfirmed/OrderFailed, it will receive a resolution for something it never marked pending. Key your state off the order itself, not off having seen OrderSubmitted.

3. pbc:error tells you the widget is unavailable; QuoteError tells you why. They are different signals and both can fire for one failure. Listen for QuoteError if you want to distinguish a bad postal code from an expired token from an outage.

4. Match an outcome to your request with its correlation fields. quote_bus is shared by the whole page, so every tab or modal hears every OrderConfirmed and OrderFailed. Both carry quoteId, itemRef, shipperId and vehicle at the top level, copied from the order that was submitted — the same values OrderSubmitted carries in order. Compare them with what you supplied and ignore events that aren't yours. Values are echoed exactly: a field you left out of quote-request is absent, and one you sent as '' comes back as ''. itemRef, shipperId and vehicle.vin are your own values, so they are the ones to match on. quoteId is the quote the shipper picked, which you supplied nothing for; it is useful only if you captured it from that widget's OrderSubmitted.

A business rejection from the order service passes its whole response through, so an OrderFailed may also carry orderId and confirmationNumber. Treat both as optional on either event — don't write a type guard that rejects an OrderFailed that has them.

error.code values you may see on OrderFailed
Code Meaning Was anything sent?
invalid_contact Phone or email failed validation No
not_configured Widget lost its api-config before submitting No
service_unavailable The order service could not be reached Yes, outcome unknown
(from the BFF) Business rejection — code and message come from the service Yes, rejected

service_unavailable is the only one where the order may have reached the service, so treat it as indeterminate rather than as a clean failure.

Privacy note. OrderSubmitted and OrderFailed carry the full order, which includes the contact name, phone and email — deliberately, since you own the contact and need it to fulfil the order. Once you set user-email, that address rides along too. Treat those events as containing personal data when deciding what to log.

Response shapes
MultiQuoteResponse {
  quotes: {                          // always contains all three keys
    open?: TransportPriceOption,
    enclosed?: TransportPriceOption,
    expedited?: TransportPriceOption,
  },
  isHighValueVehicle: boolean,
  status: 'Successful' | 'Partial' | 'Failed',
}

TransportPriceOption {
  price: number | null,              // null when this option could not be priced
  currency: string | null,
  minDaysToDeliver: number | null,
  maxDaysToDeliver: number | null,
  expirationDate: string | null,     // UTC
  quoteId: string | null,
  error: { code, message } | null,
}

OrderResponse {
  orderId?: string,
  confirmationNumber?: string,
  status: 'Unknown' | 'Confirmed' | 'Failed',
  error?: { code, message },
  // Correlation fields, echoed from the submitted order:
  quoteId?: string,
  itemRef?: string,
  shipperId?: string,
  vehicle?: { vin?, isOperable, description?: { year?, make?, model?, subModel? } },
}

A Partial status is normal, not an error — one option can price while another fails. The widget handles the display; you only need status if you record the outcome.

Maintainer tools
✅ Interstate Design System Integrated!
This page includes NO Interstate dependencies in the HTML. All Interstate styling and components are automatically loaded by your web components.

Checking Interstate CSS...
Checking Interstate Components...
Checking Cache Busting...
Key Benefits:
  • Host websites don't need to include Interstate CSS or JavaScript
  • Components work consistently across different websites
  • Automatic fallback styling if Interstate CDN is unavailable
  • Progressive enhancement with full Interstate features when available

Cache Busting Test Center

Test automatic cache busting: Monitor how cache parameters ensure fresh component loading

Current Cache Status

Loading cache version...
Scanning for cache parameters...

Test Actions


How to Test Cache Busting:

  1. Open browser DevTools → Network tab
  2. Click "Check Cache Parameters" to see current version
  3. Run npm run build in terminal to generate new version
  4. Refresh this page and check for new version numbers
  5. Look for ?v=timestamp.hash parameters on JS files

Cache Test Log

Ready for cache busting tests...
For maintainers: how releases, release notes and the changelog are made

What MAJOR, MINOR and PATCH mean for a host

  • MAJOR: Breaking changes. Upgrade deliberately and read the changelog first.
  • MINOR: New, backward-compatible features. Safe to adopt within this major.
  • PATCH: Fixes only. No contract change.

Labelling pull requests

Label a pull request to main semver:minor when it adds to the host-facing contract, or semver:major when it breaks it. An unlabelled pull request counts as a patch. Prepare release takes the highest label merged since the last release.

Writing a changelog entry

Add a section at the top of docs/changelog.md headed ## [Unreleased] — <story> (<title>). Start it with a ### Release notes subsection written for hosts: what changed for them, and what they need to do, if anything. The rest of the section is the changelog. A section without release notes shows "No release notes" on this page, and Prepare release warns about it.

Cutting a release

  1. Run the Prepare release workflow with dry_run on. Its summary shows the version, the pull requests it counts, and the release notes it would write, with a warning for each section that has none.
  2. Run it again with dry_run off. It opens a release pull request that sets the version, converts the changelog headings, and writes this page's release notes for that version.
  3. Review the release pull request, including the release notes on this page: they are what hosts read.
  4. Merge it, and merge nothing else to main until it has merged. The Main Pipeline then creates the release branch, after checking that this page's notes name the version.

Hotfixes

Follow the hotfix steps in the README. On the hotfix branch, also update this page's release notes by hand: render the hotfix's changelog sections, replace what is between the release-notes markers, take out relative links and links to GitHub, and set the start marker to the hotfix version. The reviewer checks the marker. Hotfixes of 1.0.x to 1.2.x skip this, because those versions are not listed. The forward-port pull request to main leaves out the change to this page, as it leaves out npm version.

Where the process is maintained

README.md (Branches, labels and releases), docs/release-process.md and .github/workflows/prepare-release.yml. When the process changes, update this section too.

✅ Interstate Integration Active