@getspot/spot-widget 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +12 -0
- package/dist/index.es.js +378 -207
- package/dist/index.umd.js +3 -3
- package/package.json +1 -1
- package/src/api.js +122 -0
- package/src/index.js +72 -28
- package/src/styles.css +42 -2
- package/src/ui.js +29 -3
- package/src/validateOptions.js +168 -58
package/dist/index.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(w,v){typeof exports=="object"&&typeof module<"u"?module.exports=v():typeof define=="function"&&define.amd?define(v):(w=typeof globalThis<"u"?globalThis:w||self,w.SpotWidget=v())})(this,function(){"use strict";async function w(p,e,o){try{const n=await fetch(p,{method:"POST",headers:{"Content-Type":"application/json","X-Spot-Partner-Id":e},body:JSON.stringify(o)}),t=await n.json();if(!n.ok){const r=new Error((t==null?void 0:t.message)||"Failed to fetch quote");throw r.status=n.status,r.responseBody=t,r}return t}catch(n){throw n instanceof Error?n:new Error("Unknown error occurred while fetching quote")}}async function v(p,e,o){try{const n=p.replace("/quote","/quote/batch"),t=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","X-Spot-Partner-Id":e},body:JSON.stringify(o)}),r=await t.json();if(!t.ok){const c=new Error((r==null?void 0:r.message)||"Failed to fetch batch quote");throw c.status=t.status,c.responseBody=r,c}return r}catch(n){throw n instanceof Error?n:new Error("Unknown error occurred while fetching batch quote")}}async function I(p,e,o){try{const n={cartId:o.cartInfo.cartId,cartName:o.cartInfo.cartName,currencyCode:o.cartInfo.currencyCode,items:o.items.map((a,u)=>({cartItemId:a.cartItemId||`item-${u+1}`,productPrice:a.productPrice,productType:a.productType,productDuration:a.productDuration,productId:a.productId,productName:a.productName,participantDescription:a.participantDescription,eventType:a.eventType,startDate:a.startDate,endDate:a.endDate}))},t=await v(p,e,n);if(t.status!=="QUOTES_AVAILABLE"&&t.status!=="QUOTE_AVAILABLE")return{status:"NO_MATCHING_QUOTE"};const r=t.quotes.map(a=>{const u=o.items.find(h=>(h.cartItemId||`item-${o.items.indexOf(h)+1}`)===a.cartItemId);return u?u.participantDescription?`${u.productName} - ${u.participantDescription}`:u.productName:`Item ${a.cartItemId}`}),c=Math.round((t.totalSpotPrice||t.spotPrice||0)*100)/100;return{status:"QUOTE_AVAILABLE",data:{id:t.quotes?t.quotes.map(a=>a.id).join(","):t.id,spotPrice:c,currencyCode:t.currencyCode,communication:{...t.communication,yesOptionText:t.communication.yesOptionText.replace(t.totalSpotPrice,c)},payoutSchedule:t.payoutSchedule.map(a=>({...a,amount:a.amount!==void 0?a.amount:0})),coveredItems:r,originalQuotes:t.quotes||[t]},spotPrice:c,coveredItems:r}}catch(n){throw n instanceof Error?n:new Error("Unknown error occurred while fetching multiple quotes")}}const N={sandbox:"https://api.sandbox.getspot.com/v1/quote",production:"https://api.getspot.com/v1/quote",local:"http://localhost:3999/api/v1/quote"};function k(p){const{apiConfig:e={},quoteRequestData:o,callbacks:n={},location:t,theme:r}=p,{environment:c="sandbox",partnerId:a,endpoint:u}=e;if(!a||typeof a!="string")throw new Error("Invalid or missing partnerId in apiConfig");if(!(u||N[c]))throw new Error(`Invalid environment in apiConfig: ${c}`);if(!o||typeof o!="object"&&!Array.isArray(o))throw new Error("quoteRequestData must be a non-null object or array");const m=["startDate","endDate","currencyCode","eventType","productType","productDuration","productPrice","productId","cartId","productName"];function d(s,y=null){const f=y!==null?`quoteRequestData[${y}]`:"quoteRequestData";m.forEach(b=>{if(!Object.prototype.hasOwnProperty.call(s,b)||s[b]===void 0||s[b]===null)throw new Error(`Missing required ${f} field: '${b}'`)});const C=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;if(!C.test(s.startDate))throw new Error(`${f}.startDate must be a valid ISO8601 string`);if(!C.test(s.endDate))throw new Error(`${f}.endDate must be a valid ISO8601 string`);if(typeof s.currencyCode!="string")throw new Error(`${f}.currencyCode must be a string`);if(!["USD","CAD","AUD"].includes(s.currencyCode))throw new Error(`Invalid ${f}.currencyCode: ${s.currencyCode}`);if(typeof s.eventType!="string")throw new Error(`${f}.eventType must be a string`);if(typeof s.productType!="string")throw new Error(`${f}.productType must be a string`);const _=["Pass","Trip","Registration"];if(!_.includes(s.productType))throw new Error(`${f}.productType must be one of ${_.join(", ")}`);if(typeof s.productDuration!="string")throw new Error(`${f}.productDuration must be a string`);const g=["Daily","Seasonal","Trip","Event"];if(!g.includes(s.productDuration))throw new Error(`${f}.productDuration must be one of ${g.join(", ")}`);if(typeof s.productPrice!="number"||isNaN(s.productPrice))throw new Error(`${f}.productPrice must be a valid number`);if(typeof s.productId!="string")throw new Error(`${f}.productId must be a string`);if(typeof s.cartId!="string")throw new Error(`${f}.cartId must be a string`);if(typeof s.productName!="string")throw new Error(`${f}.productName must be a string`)}if(o.cartInfo&&o.items){const{cartInfo:s,items:y}=o;if(!s||typeof s!="object")throw new Error("quoteRequestData.cartInfo must be a non-null object");if(!s.cartId||typeof s.cartId!="string")throw new Error("quoteRequestData.cartInfo.cartId must be a string");if(!s.cartName||typeof s.cartName!="string")throw new Error("quoteRequestData.cartInfo.cartName must be a string");if(!s.currencyCode||typeof s.currencyCode!="string")throw new Error("quoteRequestData.cartInfo.currencyCode must be a string");if(!["USD","CAD","AUD"].includes(s.currencyCode))throw new Error(`Invalid quoteRequestData.cartInfo.currencyCode: ${s.currencyCode}`);if(!Array.isArray(y)||y.length===0)throw new Error("quoteRequestData.items must be a non-empty array");const C=m.filter(l=>l!=="cartId"&&l!=="currencyCode");y.forEach((l,_)=>{if(!l||typeof l!="object")throw new Error(`quoteRequestData.items[${_}] must be a non-null object`);const g=`quoteRequestData.items[${_}]`;C.forEach(E=>{if(!Object.prototype.hasOwnProperty.call(l,E)||l[E]===void 0||l[E]===null)throw new Error(`Missing required ${g} field: '${E}'`)});const b=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;if(!b.test(l.startDate))throw new Error(`${g}.startDate must be a valid ISO8601 string`);if(!b.test(l.endDate))throw new Error(`${g}.endDate must be a valid ISO8601 string`);if(typeof l.eventType!="string")throw new Error(`${g}.eventType must be a string`);if(typeof l.productType!="string")throw new Error(`${g}.productType must be a string`);const T=["Pass","Trip","Registration"];if(!T.includes(l.productType))throw new Error(`${g}.productType must be one of ${T.join(", ")}`);if(typeof l.productDuration!="string")throw new Error(`${g}.productDuration must be a string`);const x=["Daily","Seasonal","Trip","Event"];if(!x.includes(l.productDuration))throw new Error(`${g}.productDuration must be one of ${x.join(", ")}`);if(typeof l.productPrice!="number"||isNaN(l.productPrice))throw new Error(`${g}.productPrice must be a valid number`);if(typeof l.productId!="string")throw new Error(`${g}.productId must be a string`);if(typeof l.productName!="string")throw new Error(`${g}.productName must be a string`)})}else if(Array.isArray(o)){if(o.length===0)throw new Error("quoteRequestData array cannot be empty");o.forEach((s,y)=>{if(!s||typeof s!="object")throw new Error(`quoteRequestData[${y}] must be a non-null object`);d(s,y)})}else d(o);if(["onOptIn","onOptOut","onQuoteRetrieved","onError","noMatchingQuote"].forEach(s=>{const y=n[s];if(y&&typeof y!="function")throw new Error(`Callback '${s}' must be a function.`)}),typeof t=="string"&&!document.querySelector(t))throw new Error(`Invalid location selector: '${t}'`);if(r&&typeof r!="object")throw new Error("Theme must be an object with CSS variables, do not include the '--' prefix")}function i(p,{text:e,className:o,parent:n,innerHTML:t}={}){const r=document.createElement(p);return o&&(r.className=o),e!=null&&(r.textContent=e),t!=null&&(r.innerHTML=t),n&&n.appendChild(r),r}function O(p,{name:e,description:o}){i("div",{className:"spot-header__title",text:e,parent:p}),i("div",{className:"spot-header__description",text:o,parent:p})}function P(p,e=[]){const o=i("ul",{className:"spot-benefits__list",parent:p});e.forEach(n=>{const t=i("li",{parent:o});t.innerHTML=`
|
|
2
2
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
3
3
|
<path d="M11.6666 3.5L5.24998 9.91667L2.33331 7"
|
|
4
4
|
stroke="#2E2E2E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
-
</svg>`,
|
|
5
|
+
</svg>`,i("span",{text:n,parent:t})})}function $(p,e=[]){if(e.length===0)return;const o=i("div",{className:"spot-covered-items__container",parent:p});i("div",{className:"spot-covered-items__title",text:"Items covered in your cart:",parent:o});const n=i("ul",{className:"spot-covered-items__list",parent:o});e.forEach(t=>{const r=i("li",{parent:n});i("span",{text:t,parent:r})})}function Q(p,e=[]){const o=i("div",{className:"spot-table__container",parent:p}),n=i("table",{className:"spot-refund__table spot-table--dynamic",parent:o}),t=i("thead",{parent:n}),r=i("tr",{parent:t});i("th",{text:"When you cancel",parent:r}),i("th",{text:"You will receive",parent:r});const c=i("tbody",{parent:n});e.forEach(({text:a,percent:u,amount:h})=>{const m=i("tr",{parent:c});i("td",{text:a,parent:m});const d=u==="Not eligible for refund"?"Not eligible for a refund":`$${h} refund`;i("td",{text:d,parent:m})})}function S(p,e,o){const n=i("div",{className:"spot-selection__options",parent:p}),t=i("label",{className:`spot-selection__option ${e?"selected":""}`,parent:n}),r=i("input",{parent:t});r.type="radio",r.name="selection",r.value="yes",e&&(r.checked=!0),i("strong",{text:o.yesOptionText,parent:t}),i("span",{className:"spot-selection__recommended-tag",text:"Recommended",parent:t});const c=i("label",{className:"spot-selection__option",parent:n}),a=i("input",{parent:c});return a.type="radio",a.name="selection",a.value="no",i("span",{text:o.noOptionText,parent:c}),n}function H(p,e){var t;const o=(t=e.communication)==null?void 0:t.paymentTerms,n=i("div",{className:"spot-payment-terms",parent:p});return i("div",{className:"spot-payment-terms__header",text:"PAYMENT TERMS",parent:n}),i("div",{className:"spot-payment-terms__body",text:o,parent:n}),n}function z(p,e){const o=i("div",{className:"spot-footer__container",parent:p}),n=i("div",{className:"spot-footer__terms",parent:o});i("span",{innerHTML:e.communication.legalDisclaimer,parent:n}),i("br",{parent:n}),i("a",{href:e.communication.termsAndConditionsUrl,className:"spot-footer__terms-link",text:"Refund Guarantee Terms and Conditions",parent:n});const t=i("p",{className:"spot-footer__powered-by",parent:o});return t.innerHTML=`
|
|
6
6
|
<svg width="145" height="28" viewBox="0 0 145 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
7
7
|
<rect width="145" height="28"/>
|
|
8
8
|
<rect x="-655" y="-270" width="819" height="325" rx="10"/>
|
|
@@ -17,4 +17,4 @@
|
|
|
17
17
|
<rect width="45.405" height="14.8867" fill="white" transform="translate(87 8)"/>
|
|
18
18
|
</clipPath>
|
|
19
19
|
</defs>
|
|
20
|
-
</svg>`,t}const T=":root{--spot-font-family: Arial;--spot-padding: 1.25rem;--spot-background-color: #ffffff;--spot-font-color: #000000;--spot-border-radius: .5rem;--spot-title-font-size: 1.25rem;--spot-title-font-weight: 700;--spot-title-padding: 0 0 1.25rem 0;--spot-description-font-size: .875rem;--spot-description-font-weight: 400;--spot-description-padding: 0 0 .5rem 0;--spot-bullets-font-size: .875rem;--spot-bullets-font-weight: 400;--spot-bullets-padding: .3125rem;--spot-table-border-radius: .625rem;--spot-table-header-font-size: .875rem;--spot-table-header-font-weight: 700;--spot-table-header-padding: 0 .5rem .625rem;--spot-table-cell-font-size: .815rem;--spot-table-cell-font-weight: 400;--spot-table-cell-padding: 0 .625rem;--spot-radio-border: #000000;--spot-radio-border-radius: .625rem;--spot-radio-checked-background: #000000;--spot-radio-text-font-size: .875rem;--spot-radio-text-font-weight: 400;--spot-radio-text-padding: .625rem;--spot-radio-selection-background: #f4f4f4;--spot-radio-selection-border-radius: .625rem;--spot-radio-selection-padding: .625rem;--spot-recommended-tag-background: #000000;--spot-recommended-tag-font-color: #ffffff;--spot-recommended-tag-font-size: .875rem;--spot-recommended-tag-font-weight: 700;--spot-recommended-tag-padding: .25rem .5rem;--spot-recommended-tag-border-radius: .5rem;--spot-selection-error-font-color: #ff0000;--spot-selection-error-font-size: .875rem;--spot-selection-error-padding: .5rem;--spot-payment-terms-background: #f4f4f4;--spot-payment-terms-border-radius: .625rem;--spot-payment-terms-padding: 1rem;--spot-payment-terms-font-color: #636569;--spot-payment-terms-font-size: .75rem;--spot-payment-terms-header-font-weight: 700;--spot-payment-terms-header-font-size: .875rem;--spot-payment-terms-header-margin-bottom: .5rem;--spot-payment-terms-header-border-color: #c2c2c2;--spot-payment-terms-header-padding: 0 0 .5rem 0;--spot-terms-font-size: .75rem;--spot-terms-font-weight: 400;--spot-terms-font-color: #636569;--spot-terms-padding: 0;--spot-terms-link-text-decoration: underline;--spot-terms-link-font-size: .75rem;--spot-terms-link-font-weight: 400;--spot-terms-link-font-color: #636569;--spot-terms-link-padding: 0}.spot-refund-guarantee{font-family:var(--spot-font-family);padding:var(--spot-padding);background-color:var(--spot-background-color);color:var(--spot-font-color);border:.0625rem solid #e0e0e0;border-radius:var(--spot-border-radius);max-width:51rem;margin:1rem}.spot-refund-guarantee *{color:inherit}.spot-header__title{font-size:var(--spot-title-font-size);font-weight:var(--spot-title-font-weight);padding:var(--spot-title-padding);color:var(--spot-title-font-color);font-family:var(--spot-title-font-family);line-height:120%;letter-spacing:-.03125rem}.spot-header__description{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:var(--spot-description-padding);line-height:125%;letter-spacing:-.025rem}.spot-content__wrapper{display:flex;flex-direction:column}@media (min-width: 48rem){.spot-content__wrapper.desktop-layout{display:grid;grid-template-columns:1fr 20.3125rem;align-items:start;gap:1rem}.desktop-layout .spot-benefits__list{grid-row:1}.desktop-layout .spot-selection__options{grid-row:2}.desktop-layout .spot-table__container{grid-row:1 / span 2}}@media (max-width: 52.438rem){.spot-selection__recommended-tag{display:inline-block;margin-left:0}}@media (max-width: 47.938rem){.spot-selection__recommended-tag{margin-top:0rem}}@media (max-width: 32.125rem){.spot-selection__recommended-tag{margin-top:.5rem}}@media (max-width: 47.9375rem){.spot-table__container{display:flex;justify-content:center}.spot-selection__recommended-tag{display:inline-block;margin-left:0}.spot-footer__container{flex-direction:column;margin-top:.5rem}.spot-refund__table{width:100%;table-layout:auto}.spot-refund__table th{padding:0rem}}.spot-benefits__list{list-style-type:none;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-benefits__list li{margin-bottom:.5rem;display:flex;align-items:flex-start;gap:.5rem}.spot-benefits__list li svg{flex-shrink:0;position:relative;top:.125rem}.spot-table__container{width:100%}.spot-refund__table{max-width:22rem;border-radius:var(--spot-table-border-radius);overflow:hidden;border:.09375rem solid #636569;table-layout:fixed;margin-bottom:1.5rem;margin-top:.25rem;padding:.625rem}.spot-refund__table--dynamic{height:auto!important;min-height:7.5rem}.spot-refund__table td,.spot-refund__table th{padding:.375rem .625rem;text-align:left}.spot-refund__table th{text-align:left;font-size:var(--spot-table-header-font-size);font-weight:var(--spot-table-header-font-weight);color:var(--spot-table-header-font-color);font-family:var(--spot-table-header-font-family);padding:var(--spot-table-header-padding)}.spot-refund__table td{text-align:left;font-size:var(--spot-table-cell-font-size);font-weight:var(--spot-table-cell-font-weight);color:var(--spot-table-cell-font-color);font-family:var(--spot-table-cell-font-family);padding:var(--spot-table-cell-padding)}input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:.75rem;height:.75rem;border:.0625rem solid var(--spot-radio-border);border-radius:var(--spot-radio-border-radius);margin-right:.5rem;position:relative;vertical-align:middle;top:-.0625rem;cursor:pointer}input[type=radio]:checked{background:var(--spot-radio-checked-background);box-shadow:inset 0 0 0 .0625rem #fff}.spot-selection__options{display:flex;flex-direction:column;gap:.5rem}.spot-selection__option{display:block;position:relative;transition:background .2s;cursor:pointer;font-size:var(--spot-radio-text-font-size);font-weight:var(--spot-radio-text-font-weight);color:var(--spot-radio-text-font-color);font-family:var(--spot-radio-text-font-family);padding:var(--spot-radio-text-padding);margin-right:.5rem;gap:.5rem;flex:1 0 0;align-self:stretch}.spot-selection__option.selected{background:var(--spot-radio-selection-background);border-radius:var(--spot-radio-selection-border-radius);padding:var(--spot-radio-selection-padding)}.spot-selection__recommended-tag{background:var(--spot-recommended-tag-background);color:var(--spot-recommended-tag-font-color);font-size:var(--spot-recommended-tag-font-size);font-weight:var(--spot-recommended-tag-font-weight);padding:var(--spot-recommended-tag-padding);border-radius:var(--spot-recommended-tag-border-radius);margin-left:1.5rem;white-space:nowrap}.spot-selection__error{color:var(--spot-selection-error-font-color);font-size:var(--spot-selection-error-font-size);padding:var(--spot-selection-error-padding);display:none}.spot-payment-terms__wrapper{margin-top:1rem}.spot-payment-terms__header{font-weight:var(--spot-payment-terms-header-font-weight);margin-bottom:.5rem;padding:var(--spot-payment-terms-header-padding);font-size:var(--spot-payment-terms-header-font-size);border-bottom:1px solid var(--spot-payment-terms-header-border-color)}.spot-payment-terms{background-color:var(--spot-payment-terms-background);border-radius:var(--spot-payment-terms-border-radius);padding:var(--spot-payment-terms-padding);margin-right:.5rem;color:var(--spot-payment-terms-font-color);font-size:var(--spot-payment-terms-font-size)}.spot-footer__terms{margin-top:.625rem;margin-right:.25rem;font-size:var(--spot-terms-font-size);font-weight:var(--spot-terms-font-weight);color:var(--spot-terms-font-color);font-family:var(--spot-terms-font-family);padding:var(--spot-terms-padding)}.spot-footer__terms-link{text-decoration:var(--spot-terms-link-text-decoration);font-size:var(--spot-terms-link-font-size);font-weight:var(--spot-terms-link-font-weight);color:var(--spot-terms-link-font-color);font-family:var(--spot-terms-link-font-family);padding:var(--spot-terms-link-padding)}.spot-footer__container{display:flex;justify-content:space-between;align-items:center}.spot-footer__powered-by{margin-top:1.5rem}";function H(i){const e=document.createElement("style");e.textContent=i,document.head.appendChild(e)}H(T);const b={sandbox:"https://api.sandbox.getspot.com/api/v1/quote",production:"https://api.getspot.com/api/v1/quote"};class N{constructor(e={}){this.options={location:"body",showTable:!0,optInSelected:!1,apiConfig:{environment:"production",partnerId:""},quoteRequestData:{},callbacks:{},...e},this._onResize=this._updateLayout.bind(this),this.root=typeof this.options.location=="string"?document.querySelector(this.options.location):this.options.location,this.currentSelection=this.options.optInSelected?"yes":null,this._init()}async _init(){var e,t,o,n,r;try{g(this.options);const{environment:a,partnerId:p}=this.options.apiConfig,l=this.options.apiConfig.customEndpoint||b[a],d=await u(l,p,this.options.quoteRequestData);if(d.status!=="QUOTE_AVAILABLE"){d.status==="NO_MATCHING_QUOTE"&&((e=this.options.callbacks)!=null&&e.noMatchingQuote)&&this.options.callbacks.noMatchingQuote({status:"NO_MATCHING_QUOTE",data:this.options.quoteRequestData});return}this.quote=d.data,this._renderWidget(),this.options.optInSelected&&((t=this.options.callbacks)!=null&&t.onOptIn)&&this.options.callbacks.onOptIn({status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id}),(o=this.options.callbacks)!=null&&o.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote)}catch(a){(n=this.options.callbacks)!=null&&n.onError&&((r=this.options.callbacks)==null||r.onError({message:a.message,status:a.status,responseBody:a.responseBody}))}}_renderWidget(){this.container=document.createElement("div"),this.container.className="spot-refund-guarantee",this.root.appendChild(this.container),Object.entries(this.options.theme||{}).forEach(([o,n])=>{const r=`--${o}`;this.container.style.setProperty(r,n)}),w(this.container,this.quote.communication);const e=document.createElement("div");e.className="spot-content__wrapper",this.container.appendChild(e),C(e,this.quote.communication.bulletPoints),this.options.showTable&&E(e,this.quote.payoutSchedule);const t=k(e,this.quote.spotPrice,this.options.optInSelected);e.appendChild(t),this.paymentTermsEl=s("div",{className:"spot-payment-terms__wrapper",parent:e}),q(this.container,this.quote),window.addEventListener("resize",this._onResize),this._updateLayout(),this._setupOptionListeners(t)}_updateLayout(){const e=window.matchMedia("(min-width: 768px)").matches;this.container.querySelector(".spot-content__wrapper").classList.toggle("desktop-layout",e&&this.options.showTable)}_setupOptionListeners(e){const t=e.querySelectorAll('input[type="radio"]'),o=e.querySelectorAll(".spot-selection__option");t.forEach(n=>{n.addEventListener("change",r=>{var p,c,l;const a=r.target.value;this.hideSelectionError(),this.currentSelection=a,o.forEach(d=>d.classList.remove("selected")),(p=r.target.closest(".spot-selection__option"))==null||p.classList.add("selected"),this.paymentTermsEl&&(this.paymentTermsEl.innerHTML=""),a==="yes"&&(this.options.quoteRequestData.isPartialPayment&&x(this.paymentTermsEl,this.quote),(c=this.options.callbacks)!=null&&c.onOptIn&&this.options.callbacks.onOptIn({status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id})),a==="no"&&((l=this.options.callbacks)!=null&&l.onOptOut)&&this.options.callbacks.onOptOut({status:"QUOTE_DECLINED",quoteId:this.quote.id})})})}showSelectionError(){var e;if(!this.errorEl){this.errorEl=document.createElement("div"),this.errorEl.className="spot-selection__error",this.errorEl.textContent="Please make a selection";const t=(e=this.container)==null?void 0:e.querySelector(".spot-selection__options");t&&t.insertAdjacentElement("afterend",this.errorEl)}this.errorEl.style.display="block"}hideSelectionError(){this.errorEl&&(this.errorEl.style.display="none")}validateSelection(){if(!this.container)return!1;const e=!!this.container.querySelector('input[name="selection"]:checked');return e?this.hideSelectionError():this.showSelectionError(),e}async updateQuote(e){var t,o,n;try{const r={...this.options,quoteRequestData:{...this.options.quoteRequestData,...e}};g(r);const{environment:a,partnerId:p,endpoint:c}=this.options.apiConfig,l=c||b[a],d=await u(l,p,r.quoteRequestData);return d.status!=="QUOTE_AVAILABLE"?(d.status==="NO_MATCHING_QUOTE"&&((t=this.options.callbacks)!=null&&t.noMatchingQuote)&&this.options.callbacks.noMatchingQuote({status:"NO_MATCHING_QUOTE",data:r.quoteRequestData}),!1):(this.options.quoteRequestData=r.quoteRequestData,this.quote=d.data,this.currentSelection=null,this.destroy(),this._renderWidget(),(o=this.options.callbacks)!=null&&o.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote),!0)}catch(r){return(n=this.options.callbacks)==null||n.onError({message:r.message,status:r.status,responseBody:r.responseBody}),!1}}getSelection(){var e,t;return this.currentSelection==null?null:{selection:this.currentSelection,quoteId:(e=this.quote)==null?void 0:e.id,spotPrice:(t=this.quote)==null?void 0:t.spotPrice,status:this.currentSelection==="yes"?"QUOTE_ACCEPTED":"QUOTE_DECLINED"}}destroy(){window.removeEventListener("resize",this._onResize),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}}return N});
|
|
20
|
+
</svg>`,o}const M=":root{--spot-font-family: Arial;--spot-padding: 1.25rem;--spot-background-color: #ffffff;--spot-font-color: #000000;--spot-border-radius: .5rem;--spot-title-font-size: 1.25rem;--spot-title-font-weight: 700;--spot-title-padding: 0 0 1.25rem 0;--spot-description-font-size: .875rem;--spot-description-font-weight: 400;--spot-description-padding: 0 0 .5rem 0;--spot-bullets-font-size: .875rem;--spot-bullets-font-weight: 400;--spot-bullets-padding: .3125rem;--spot-table-border-radius: .625rem;--spot-table-header-font-size: .875rem;--spot-table-header-font-weight: 700;--spot-table-header-padding: 0 .5rem .625rem;--spot-table-cell-font-size: .815rem;--spot-table-cell-font-weight: 400;--spot-table-cell-padding: 0 .625rem;--spot-radio-border: #000000;--spot-radio-border-radius: .625rem;--spot-radio-checked-background: #000000;--spot-radio-text-font-size: .875rem;--spot-radio-text-font-weight: 400;--spot-radio-text-padding: .625rem;--spot-radio-selection-background: #f4f4f4;--spot-radio-selection-border-radius: .625rem;--spot-radio-selection-padding: .625rem;--spot-recommended-tag-background: #000000;--spot-recommended-tag-font-color: #ffffff;--spot-recommended-tag-font-size: .875rem;--spot-recommended-tag-font-weight: 700;--spot-recommended-tag-padding: .25rem .5rem;--spot-recommended-tag-border-radius: .5rem;--spot-selection-error-font-color: #ff0000;--spot-selection-error-font-size: .875rem;--spot-selection-error-padding: .5rem;--spot-payment-terms-background: #f4f4f4;--spot-payment-terms-border-radius: .625rem;--spot-payment-terms-padding: 1rem;--spot-payment-terms-font-color: #636569;--spot-payment-terms-font-size: .75rem;--spot-payment-terms-header-font-weight: 700;--spot-payment-terms-header-font-size: .875rem;--spot-payment-terms-header-margin-bottom: .5rem;--spot-payment-terms-header-border-color: #c2c2c2;--spot-payment-terms-header-padding: 0 0 .5rem 0;--spot-terms-font-size: .75rem;--spot-terms-font-weight: 400;--spot-terms-font-color: #636569;--spot-terms-padding: 0;--spot-terms-link-text-decoration: underline;--spot-terms-link-font-size: .75rem;--spot-terms-link-font-weight: 400;--spot-terms-link-font-color: #636569;--spot-terms-link-padding: 0}.spot-refund-guarantee{font-family:var(--spot-font-family);padding:var(--spot-padding);background-color:var(--spot-background-color);color:var(--spot-font-color);border:.0625rem solid #e0e0e0;border-radius:var(--spot-border-radius);max-width:51rem;margin:1rem}.spot-refund-guarantee *{color:inherit}.spot-header__title{font-size:var(--spot-title-font-size);font-weight:var(--spot-title-font-weight);padding:var(--spot-title-padding);color:var(--spot-title-font-color);font-family:var(--spot-title-font-family);line-height:120%;letter-spacing:-.03125rem}.spot-header__description{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:var(--spot-description-padding);line-height:125%;letter-spacing:-.025rem}.spot-content__wrapper{display:flex;flex-direction:column}@media (min-width: 48rem){.spot-content__wrapper.desktop-layout{display:grid;grid-template-columns:1fr 20.3125rem;align-items:start;gap:1rem}.desktop-layout .spot-benefits__list{grid-row:1}.desktop-layout .spot-covered-items__container{grid-row:2;grid-column:1}.desktop-layout .spot-selection__options{grid-row:3}.desktop-layout .spot-table__container{grid-row:1 / span 3}}@media (max-width: 52.438rem){.spot-selection__recommended-tag{display:inline-block;margin-left:0}}@media (max-width: 47.938rem){.spot-selection__recommended-tag{margin-top:0rem}}@media (max-width: 32.125rem){.spot-selection__recommended-tag{margin-top:.5rem}}@media (max-width: 47.9375rem){.spot-table__container{display:flex;justify-content:center}.spot-selection__recommended-tag{display:inline-block;margin-left:0}.spot-footer__container{flex-direction:column;margin-top:.5rem}.spot-refund__table{width:100%;table-layout:auto}.spot-refund__table th{padding:0rem}}.spot-benefits__list{list-style-type:none;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-benefits__list li{margin-bottom:.5rem;display:flex;align-items:flex-start;gap:.5rem}.spot-benefits__list li svg{flex-shrink:0;position:relative;top:.125rem}.spot-covered-items__container{margin-top:0;margin-bottom:1rem}.spot-covered-items__title{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:0 .3125rem .25rem;line-height:125%;margin:0}.spot-covered-items__list{list-style-type:disc;list-style-position:inside;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-covered-items__list li{margin-bottom:.3rem;text-align:left}.spot-table__container{width:100%}.spot-refund__table{max-width:22rem;border-radius:var(--spot-table-border-radius);overflow:hidden;border:.09375rem solid #636569;table-layout:fixed;margin-bottom:1.5rem;margin-top:.25rem;padding:.625rem}.spot-refund__table--dynamic{height:auto!important;min-height:7.5rem}.spot-refund__table td,.spot-refund__table th{padding:.375rem .625rem;text-align:left}.spot-refund__table th{text-align:left;font-size:var(--spot-table-header-font-size);font-weight:var(--spot-table-header-font-weight);color:var(--spot-table-header-font-color);font-family:var(--spot-table-header-font-family);padding:var(--spot-table-header-padding)}.spot-refund__table td{text-align:left;font-size:var(--spot-table-cell-font-size);font-weight:var(--spot-table-cell-font-weight);color:var(--spot-table-cell-font-color);font-family:var(--spot-table-cell-font-family);padding:var(--spot-table-cell-padding)}input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:.75rem;height:.75rem;border:.0625rem solid var(--spot-radio-border);border-radius:var(--spot-radio-border-radius);margin-right:.5rem;position:relative;vertical-align:middle;top:-.0625rem;cursor:pointer}input[type=radio]:checked{background:var(--spot-radio-checked-background);box-shadow:inset 0 0 0 .0625rem #fff}.spot-selection__options{display:flex;flex-direction:column;gap:.5rem}.spot-selection__option{display:block;position:relative;transition:background .2s;cursor:pointer;font-size:var(--spot-radio-text-font-size);font-weight:var(--spot-radio-text-font-weight);color:var(--spot-radio-text-font-color);font-family:var(--spot-radio-text-font-family);padding:var(--spot-radio-text-padding);margin-right:.5rem;gap:.5rem;flex:1 0 0;align-self:stretch}.spot-selection__option.selected{background:var(--spot-radio-selection-background);border-radius:var(--spot-radio-selection-border-radius);padding:var(--spot-radio-selection-padding)}.spot-selection__recommended-tag{background:var(--spot-recommended-tag-background);color:var(--spot-recommended-tag-font-color);font-size:var(--spot-recommended-tag-font-size);font-weight:var(--spot-recommended-tag-font-weight);padding:var(--spot-recommended-tag-padding);border-radius:var(--spot-recommended-tag-border-radius);margin-left:1.5rem;white-space:nowrap}.spot-selection__error{color:var(--spot-selection-error-font-color);font-size:var(--spot-selection-error-font-size);padding:var(--spot-selection-error-padding);display:none}.spot-payment-terms__wrapper{margin-top:1rem}.spot-payment-terms__header{font-weight:var(--spot-payment-terms-header-font-weight);margin-bottom:.5rem;padding:var(--spot-payment-terms-header-padding);font-size:var(--spot-payment-terms-header-font-size);border-bottom:1px solid var(--spot-payment-terms-header-border-color)}.spot-payment-terms{background-color:var(--spot-payment-terms-background);border-radius:var(--spot-payment-terms-border-radius);padding:var(--spot-payment-terms-padding);margin-right:.5rem;color:var(--spot-payment-terms-font-color);font-size:var(--spot-payment-terms-font-size)}.spot-footer__terms{margin-top:.625rem;margin-right:.25rem;font-size:var(--spot-terms-font-size);font-weight:var(--spot-terms-font-weight);color:var(--spot-terms-font-color);font-family:var(--spot-terms-font-family);padding:var(--spot-terms-padding)}.spot-footer__terms-link{text-decoration:var(--spot-terms-link-text-decoration);font-size:var(--spot-terms-link-font-size);font-weight:var(--spot-terms-link-font-weight);color:var(--spot-terms-link-font-color);font-family:var(--spot-terms-link-font-family);padding:var(--spot-terms-link-padding)}.spot-footer__container{display:flex;justify-content:space-between;align-items:center}.spot-footer__powered-by{margin-top:1.5rem}";function R(p){const e=document.createElement("style");e.textContent=p,document.head.appendChild(e)}R(M);const D={sandbox:"https://api.sandbox.getspot.com/api/v1/quote",production:"https://api.getspot.com/api/v1/quote",local:"http://localhost:3999/api/v1/quote"};class L{constructor(e={}){this.options={location:"body",showTable:!0,optInSelected:!1,apiConfig:{environment:"production",partnerId:""},quoteRequestData:{},callbacks:{},...e},this._onResize=this._updateLayout.bind(this),this.root=typeof this.options.location=="string"?document.querySelector(this.options.location):this.options.location,this.currentSelection=this.options.optInSelected?"yes":null,this._init()}async _init(){var e,o,n,t,r;try{k(this.options);const{environment:c,partnerId:a}=this.options.apiConfig,h=this.options.apiConfig.customEndpoint||D[c],d=this.options.quoteRequestData.cartInfo&&this.options.quoteRequestData.items?await I(h,a,this.options.quoteRequestData):await w(h,a,this.options.quoteRequestData);if(d.status!=="QUOTE_AVAILABLE"){d.status==="NO_MATCHING_QUOTE"&&((e=this.options.callbacks)!=null&&e.noMatchingQuote)&&this.options.callbacks.noMatchingQuote({status:"NO_MATCHING_QUOTE",data:this.options.quoteRequestData});return}if(this.quote=d.data,this._renderWidget(),this.options.optInSelected&&((o=this.options.callbacks)!=null&&o.onOptIn)){const q={status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id};this.quote.originalQuotes&&this.quote.originalQuotes.length>0&&(q.batchQuoteDetails=this.quote.originalQuotes.map(s=>({quoteId:s.id,productPrice:s.spotPrice,cartItemId:s.cartItemId}))),this.options.callbacks.onOptIn(q)}(n=this.options.callbacks)!=null&&n.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote)}catch(c){(t=this.options.callbacks)!=null&&t.onError&&((r=this.options.callbacks)==null||r.onError({message:c.message,status:c.status,responseBody:c.responseBody}))}}_renderWidget(){this.container=document.createElement("div"),this.container.className="spot-refund-guarantee",this.root.appendChild(this.container),Object.entries(this.options.theme||{}).forEach(([n,t])=>{const r=`--${n}`;this.container.style.setProperty(r,t)}),O(this.container,this.quote.communication);const e=document.createElement("div");e.className="spot-content__wrapper",this.container.appendChild(e),P(e,this.quote.communication.bulletPoints),this.quote.coveredItems&&$(e,this.quote.coveredItems),this.options.showTable&&Q(e,this.quote.payoutSchedule);const o=S(e,this.options.optInSelected,this.quote.communication);e.appendChild(o),this.paymentTermsEl=i("div",{className:"spot-payment-terms__wrapper",parent:e}),z(this.container,this.quote),window.addEventListener("resize",this._onResize),this._updateLayout(),this._setupOptionListeners(o)}_updateLayout(){const e=window.matchMedia("(min-width: 768px)").matches;this.container.querySelector(".spot-content__wrapper").classList.toggle("desktop-layout",e&&this.options.showTable)}_setupOptionListeners(e){const o=e.querySelectorAll('input[type="radio"]'),n=e.querySelectorAll(".spot-selection__option");o.forEach(t=>{t.addEventListener("change",r=>{var a,u,h;const c=r.target.value;if(this.hideSelectionError(),this.currentSelection=c,n.forEach(m=>m.classList.remove("selected")),(a=r.target.closest(".spot-selection__option"))==null||a.classList.add("selected"),this.paymentTermsEl&&(this.paymentTermsEl.innerHTML=""),c==="yes"&&(this.options.quoteRequestData.isPartialPayment&&H(this.paymentTermsEl,this.quote),(u=this.options.callbacks)!=null&&u.onOptIn)){const m={status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id};this.quote.originalQuotes&&this.quote.originalQuotes.length>0&&(m.batchQuoteDetails=this.quote.originalQuotes.map(d=>({quoteId:d.id,productPrice:d.spotPrice,cartItemId:d.cartItemId}))),this.options.callbacks.onOptIn(m)}if(c==="no"&&((h=this.options.callbacks)!=null&&h.onOptOut)){const m={status:"QUOTE_DECLINED",quoteId:this.quote.id};this.quote.originalQuotes&&this.quote.originalQuotes.length>0&&(m.batchQuoteDetails=this.quote.originalQuotes.map(d=>({quoteId:d.id,productPrice:d.spotPrice,cartItemId:d.cartItemId}))),this.options.callbacks.onOptOut(m)}})})}showSelectionError(){var e;if(!this.errorEl){this.errorEl=document.createElement("div"),this.errorEl.className="spot-selection__error",this.errorEl.textContent="Please make a selection";const o=(e=this.container)==null?void 0:e.querySelector(".spot-selection__options");o&&o.insertAdjacentElement("afterend",this.errorEl)}this.errorEl.style.display="block"}hideSelectionError(){this.errorEl&&(this.errorEl.style.display="none")}validateSelection(){if(!this.container)return!1;const e=!!this.container.querySelector('input[name="selection"]:checked');return e?this.hideSelectionError():this.showSelectionError(),e}async updateQuote(e){var o,n,t;try{const r={...this.options,quoteRequestData:e};k(r);const{environment:c,partnerId:a,endpoint:u}=this.options.apiConfig,h=u||D[c],d=r.quoteRequestData.cartInfo&&r.quoteRequestData.items?await I(h,a,r.quoteRequestData):await w(h,a,r.quoteRequestData);return d.status!=="QUOTE_AVAILABLE"?(d.status==="NO_MATCHING_QUOTE"&&((o=this.options.callbacks)!=null&&o.noMatchingQuote)&&this.options.callbacks.noMatchingQuote({status:"NO_MATCHING_QUOTE",data:r.quoteRequestData}),!1):(this.options.quoteRequestData=r.quoteRequestData,this.quote=d.data,this.currentSelection=null,this.destroy(),this._renderWidget(),(n=this.options.callbacks)!=null&&n.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote),!0)}catch(r){return(t=this.options.callbacks)==null||t.onError({message:r.message,status:r.status,responseBody:r.responseBody}),!1}}getSelection(){var o,n,t;if(this.currentSelection==null)return null;const e={selection:this.currentSelection,quoteId:(o=this.quote)==null?void 0:o.id,spotPrice:(n=this.quote)==null?void 0:n.spotPrice,status:this.currentSelection==="yes"?"QUOTE_ACCEPTED":"QUOTE_DECLINED"};return(t=this.quote)!=null&&t.originalQuotes&&this.quote.originalQuotes.length>0&&(e.batchQuoteDetails=this.quote.originalQuotes.map(r=>({quoteId:r.id,productPrice:r.spotPrice,cartItemId:r.cartItemId}))),e}destroy(){window.removeEventListener("resize",this._onResize),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}}return L});
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -30,3 +30,125 @@ export async function fetchQuote(endpoint, partnerId, payload) {
|
|
|
30
30
|
: new Error("Unknown error occurred while fetching quote");
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Retrieve batch quote from Spot API
|
|
36
|
+
* @param {string} endpoint – Spot API URL
|
|
37
|
+
* @param {string} partnerId – partner UUID
|
|
38
|
+
* @param {object} batchPayload – batch request data with items array
|
|
39
|
+
* @returns {Promise<{ status: string, data?: object }>}
|
|
40
|
+
*/
|
|
41
|
+
export async function fetchBatchQuote(endpoint, partnerId, batchPayload) {
|
|
42
|
+
try {
|
|
43
|
+
const batchEndpoint = endpoint.replace('/quote', '/quote/batch');
|
|
44
|
+
const res = await fetch(batchEndpoint, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
"X-Spot-Partner-Id": partnerId,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(batchPayload),
|
|
51
|
+
});
|
|
52
|
+
const body = await res.json();
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const error = new Error(body?.message || "Failed to fetch batch quote");
|
|
55
|
+
error.status = res.status;
|
|
56
|
+
error.responseBody = body;
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return body;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
throw err instanceof Error
|
|
63
|
+
? err
|
|
64
|
+
: new Error("Unknown error occurred while fetching batch quote");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Retrieve multiple quotes in parallel from Spot API
|
|
70
|
+
* @param {string} endpoint – Spot API URL
|
|
71
|
+
* @param {string} partnerId – partner UUID
|
|
72
|
+
* @param {object} batchData – batch quote data with cartInfo and items
|
|
73
|
+
* @param {object} batchData.cartInfo – cart-level information
|
|
74
|
+
* @param {string} batchData.cartInfo.cartId – cart ID
|
|
75
|
+
* @param {string} batchData.cartInfo.cartName – cart name
|
|
76
|
+
* @param {string} batchData.cartInfo.currencyCode – currency code
|
|
77
|
+
* @param {object[]} batchData.items – array of item data
|
|
78
|
+
* @returns {Promise<{ status: string, data?: object, spotPrice?: number, coveredItems?: string[] }>}
|
|
79
|
+
*/
|
|
80
|
+
export async function fetchMultipleQuotes(endpoint, partnerId, batchData) {
|
|
81
|
+
try {
|
|
82
|
+
// Transform into batch format for API
|
|
83
|
+
const batchPayload = {
|
|
84
|
+
cartId: batchData.cartInfo.cartId,
|
|
85
|
+
cartName: batchData.cartInfo.cartName,
|
|
86
|
+
currencyCode: batchData.cartInfo.currencyCode,
|
|
87
|
+
items: batchData.items.map((item, index) => ({
|
|
88
|
+
cartItemId: item.cartItemId || `item-${index + 1}`,
|
|
89
|
+
productPrice: item.productPrice,
|
|
90
|
+
productType: item.productType,
|
|
91
|
+
productDuration: item.productDuration,
|
|
92
|
+
productId: item.productId,
|
|
93
|
+
productName: item.productName,
|
|
94
|
+
participantDescription: item.participantDescription,
|
|
95
|
+
eventType: item.eventType,
|
|
96
|
+
startDate: item.startDate,
|
|
97
|
+
endDate: item.endDate
|
|
98
|
+
}))
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const response = await fetchBatchQuote(endpoint, partnerId, batchPayload);
|
|
102
|
+
|
|
103
|
+
// Check for various success statuses
|
|
104
|
+
if (response.status !== "QUOTES_AVAILABLE" && response.status !== "QUOTE_AVAILABLE") {
|
|
105
|
+
return { status: "NO_MATCHING_QUOTE" };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Extract covered items only for successful quotes
|
|
109
|
+
const coveredItems = response.quotes.map(quote => {
|
|
110
|
+
// Find the corresponding item from the original request using cartItemId
|
|
111
|
+
const originalItem = batchData.items.find(item =>
|
|
112
|
+
(item.cartItemId || `item-${batchData.items.indexOf(item) + 1}`) === quote.cartItemId
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (originalItem) {
|
|
116
|
+
return originalItem.participantDescription
|
|
117
|
+
? `${originalItem.productName} - ${originalItem.participantDescription}`
|
|
118
|
+
: originalItem.productName;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Fallback if we can't find the original item
|
|
122
|
+
return `Item ${quote.cartItemId}`;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Fix floating point precision issue
|
|
126
|
+
const totalSpotPrice = Math.round((response.totalSpotPrice || response.spotPrice || 0) * 100) / 100;
|
|
127
|
+
|
|
128
|
+
// Build response in expected format
|
|
129
|
+
return {
|
|
130
|
+
status: "QUOTE_AVAILABLE",
|
|
131
|
+
data: {
|
|
132
|
+
id: response.quotes ? response.quotes.map(q => q.id).join(',') : response.id,
|
|
133
|
+
spotPrice: totalSpotPrice,
|
|
134
|
+
currencyCode: response.currencyCode,
|
|
135
|
+
communication: {
|
|
136
|
+
...response.communication,
|
|
137
|
+
yesOptionText: response.communication.yesOptionText.replace(response.totalSpotPrice, totalSpotPrice)
|
|
138
|
+
},
|
|
139
|
+
payoutSchedule: response.payoutSchedule.map(item => ({
|
|
140
|
+
...item,
|
|
141
|
+
amount: item.amount !== undefined ? item.amount : 0
|
|
142
|
+
})),
|
|
143
|
+
coveredItems: coveredItems,
|
|
144
|
+
originalQuotes: response.quotes || [response]
|
|
145
|
+
},
|
|
146
|
+
spotPrice: totalSpotPrice,
|
|
147
|
+
coveredItems
|
|
148
|
+
};
|
|
149
|
+
} catch (err) {
|
|
150
|
+
throw err instanceof Error
|
|
151
|
+
? err
|
|
152
|
+
: new Error("Unknown error occurred while fetching multiple quotes");
|
|
153
|
+
}
|
|
154
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { fetchQuote } from "./api.js";
|
|
1
|
+
import { fetchQuote, fetchMultipleQuotes } from "./api.js";
|
|
2
2
|
import { validateOptions } from "./validateOptions.js";
|
|
3
3
|
import {
|
|
4
4
|
renderHeader,
|
|
5
5
|
renderBenefits,
|
|
6
|
+
renderCoveredItems,
|
|
6
7
|
renderTable,
|
|
7
8
|
renderOptions,
|
|
8
9
|
renderFooter,
|
|
@@ -23,6 +24,7 @@ injectStyles(styles);
|
|
|
23
24
|
const apiEndpoint = {
|
|
24
25
|
sandbox: "https://api.sandbox.getspot.com/api/v1/quote",
|
|
25
26
|
production: "https://api.getspot.com/api/v1/quote",
|
|
27
|
+
local: "http://localhost:3999/api/v1/quote"
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
class SpotWidget {
|
|
@@ -56,11 +58,10 @@ class SpotWidget {
|
|
|
56
58
|
|
|
57
59
|
const endpoint = customEndpoint || apiEndpoint[environment];
|
|
58
60
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
partnerId,
|
|
62
|
-
this.options.quoteRequestData
|
|
63
|
-
);
|
|
61
|
+
const isBatchQuote = this.options.quoteRequestData.cartInfo && this.options.quoteRequestData.items;
|
|
62
|
+
const response = isBatchQuote
|
|
63
|
+
? await fetchMultipleQuotes(endpoint, partnerId, this.options.quoteRequestData)
|
|
64
|
+
: await fetchQuote(endpoint, partnerId, this.options.quoteRequestData);
|
|
64
65
|
|
|
65
66
|
if (response.status !== "QUOTE_AVAILABLE") {
|
|
66
67
|
if (
|
|
@@ -79,11 +80,22 @@ class SpotWidget {
|
|
|
79
80
|
this._renderWidget();
|
|
80
81
|
|
|
81
82
|
if (this.options.optInSelected && this.options.callbacks?.onOptIn) {
|
|
82
|
-
|
|
83
|
+
const optInData = {
|
|
83
84
|
status: "QUOTE_ACCEPTED",
|
|
84
85
|
spotPrice: this.quote.spotPrice,
|
|
85
|
-
quoteId: this.quote.id
|
|
86
|
-
}
|
|
86
|
+
quoteId: this.quote.id
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// For batch quotes, include detailed quote information
|
|
90
|
+
if (this.quote.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
91
|
+
optInData.batchQuoteDetails = this.quote.originalQuotes.map(q => ({
|
|
92
|
+
quoteId: q.id,
|
|
93
|
+
productPrice: q.spotPrice,
|
|
94
|
+
cartItemId: q.cartItemId
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.options.callbacks.onOptIn(optInData);
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
if (this.options.callbacks?.onQuoteRetrieved) {
|
|
@@ -116,11 +128,14 @@ class SpotWidget {
|
|
|
116
128
|
this.container.appendChild(cw);
|
|
117
129
|
|
|
118
130
|
renderBenefits(cw, this.quote.communication.bulletPoints);
|
|
131
|
+
if (this.quote.coveredItems) {
|
|
132
|
+
renderCoveredItems(cw, this.quote.coveredItems);
|
|
133
|
+
}
|
|
119
134
|
if (this.options.showTable) renderTable(cw, this.quote.payoutSchedule);
|
|
120
135
|
const optsEl = renderOptions(
|
|
121
136
|
cw,
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
137
|
+
this.options.optInSelected,
|
|
138
|
+
this.quote.communication
|
|
124
139
|
);
|
|
125
140
|
cw.appendChild(optsEl);
|
|
126
141
|
this.paymentTermsEl = makeEl("div", {
|
|
@@ -162,18 +177,40 @@ class SpotWidget {
|
|
|
162
177
|
renderPaymentTerms(this.paymentTermsEl, this.quote);
|
|
163
178
|
}
|
|
164
179
|
if (this.options.callbacks?.onOptIn) {
|
|
165
|
-
|
|
180
|
+
const optInData = {
|
|
166
181
|
status: "QUOTE_ACCEPTED",
|
|
167
182
|
spotPrice: this.quote.spotPrice,
|
|
168
|
-
quoteId: this.quote.id
|
|
169
|
-
}
|
|
183
|
+
quoteId: this.quote.id
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// For batch quotes, include detailed quote information
|
|
187
|
+
if (this.quote.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
188
|
+
optInData.batchQuoteDetails = this.quote.originalQuotes.map(q => ({
|
|
189
|
+
quoteId: q.id,
|
|
190
|
+
productPrice: q.spotPrice,
|
|
191
|
+
cartItemId: q.cartItemId
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.options.callbacks.onOptIn(optInData);
|
|
170
196
|
}
|
|
171
197
|
}
|
|
172
198
|
if (val === "no" && this.options.callbacks?.onOptOut) {
|
|
173
|
-
|
|
199
|
+
const optOutData = {
|
|
174
200
|
status: "QUOTE_DECLINED",
|
|
175
|
-
quoteId: this.quote.id
|
|
176
|
-
}
|
|
201
|
+
quoteId: this.quote.id
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// For batch quotes, include detailed quote information
|
|
205
|
+
if (this.quote.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
206
|
+
optOutData.batchQuoteDetails = this.quote.originalQuotes.map(q => ({
|
|
207
|
+
quoteId: q.id,
|
|
208
|
+
productPrice: q.spotPrice,
|
|
209
|
+
cartItemId: q.cartItemId
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.options.callbacks.onOptOut(optOutData);
|
|
177
214
|
}
|
|
178
215
|
});
|
|
179
216
|
});
|
|
@@ -221,10 +258,7 @@ class SpotWidget {
|
|
|
221
258
|
try {
|
|
222
259
|
const updatedOptions = {
|
|
223
260
|
...this.options,
|
|
224
|
-
quoteRequestData:
|
|
225
|
-
...this.options.quoteRequestData,
|
|
226
|
-
...newQuoteRequestData,
|
|
227
|
-
},
|
|
261
|
+
quoteRequestData: newQuoteRequestData
|
|
228
262
|
};
|
|
229
263
|
|
|
230
264
|
validateOptions(updatedOptions);
|
|
@@ -237,11 +271,10 @@ class SpotWidget {
|
|
|
237
271
|
|
|
238
272
|
const endpoint = customEndpoint || apiEndpoint[environment];
|
|
239
273
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
partnerId,
|
|
243
|
-
updatedOptions.quoteRequestData
|
|
244
|
-
);
|
|
274
|
+
const isBatchQuote = updatedOptions.quoteRequestData.cartInfo && updatedOptions.quoteRequestData.items;
|
|
275
|
+
const response = isBatchQuote
|
|
276
|
+
? await fetchMultipleQuotes(endpoint, partnerId, updatedOptions.quoteRequestData)
|
|
277
|
+
: await fetchQuote(endpoint, partnerId, updatedOptions.quoteRequestData);
|
|
245
278
|
|
|
246
279
|
if (response.status !== "QUOTE_AVAILABLE") {
|
|
247
280
|
if (
|
|
@@ -282,13 +315,24 @@ class SpotWidget {
|
|
|
282
315
|
getSelection() {
|
|
283
316
|
if (this.currentSelection == null) return null;
|
|
284
317
|
|
|
285
|
-
|
|
318
|
+
const selectionData = {
|
|
286
319
|
selection: this.currentSelection,
|
|
287
320
|
quoteId: this.quote?.id,
|
|
288
321
|
spotPrice: this.quote?.spotPrice,
|
|
289
322
|
status:
|
|
290
|
-
this.currentSelection === "yes" ? "QUOTE_ACCEPTED" : "QUOTE_DECLINED"
|
|
323
|
+
this.currentSelection === "yes" ? "QUOTE_ACCEPTED" : "QUOTE_DECLINED"
|
|
291
324
|
};
|
|
325
|
+
|
|
326
|
+
// For batch quotes, include detailed quote information
|
|
327
|
+
if (this.quote?.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
328
|
+
selectionData.batchQuoteDetails = this.quote.originalQuotes.map(q => ({
|
|
329
|
+
quoteId: q.id,
|
|
330
|
+
productPrice: q.spotPrice,
|
|
331
|
+
cartItemId: q.cartItemId
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return selectionData;
|
|
292
336
|
}
|
|
293
337
|
|
|
294
338
|
destroy() {
|
package/src/styles.css
CHANGED
|
@@ -127,12 +127,17 @@
|
|
|
127
127
|
grid-row: 1;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
.desktop-layout .spot-
|
|
130
|
+
.desktop-layout .spot-covered-items__container {
|
|
131
131
|
grid-row: 2;
|
|
132
|
+
grid-column: 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.desktop-layout .spot-selection__options {
|
|
136
|
+
grid-row: 3;
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
.desktop-layout .spot-table__container {
|
|
135
|
-
grid-row: 1 / span
|
|
140
|
+
grid-row: 1 / span 3;
|
|
136
141
|
}
|
|
137
142
|
}
|
|
138
143
|
|
|
@@ -207,6 +212,41 @@
|
|
|
207
212
|
top: 0.125rem;
|
|
208
213
|
}
|
|
209
214
|
|
|
215
|
+
/* Covered Items List */
|
|
216
|
+
.spot-covered-items__container {
|
|
217
|
+
margin-top: 0;
|
|
218
|
+
margin-bottom: 1rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.spot-covered-items__title {
|
|
222
|
+
font-size: var(--spot-description-font-size);
|
|
223
|
+
font-weight: var(--spot-description-font-weight);
|
|
224
|
+
color: var(--spot-description-font-color);
|
|
225
|
+
font-family: var(--spot-description-font-family);
|
|
226
|
+
padding: 0 0.3125rem 0.25rem 0.3125rem;
|
|
227
|
+
line-height: 125%;
|
|
228
|
+
margin: 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.spot-covered-items__list {
|
|
232
|
+
list-style-type: disc;
|
|
233
|
+
list-style-position: inside;
|
|
234
|
+
line-height: 125%;
|
|
235
|
+
gap: 0.5625rem;
|
|
236
|
+
font-size: var(--spot-bullets-font-size);
|
|
237
|
+
font-weight: var(--spot-bullets-font-weight);
|
|
238
|
+
color: var(--spot-bullets-font-color);
|
|
239
|
+
font-family: var(--spot-bullets-font-family);
|
|
240
|
+
padding: var(--spot-bullets-padding);
|
|
241
|
+
margin-block-start: 0rem;
|
|
242
|
+
margin-block-end: 0rem;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.spot-covered-items__list li {
|
|
246
|
+
margin-bottom: 0.3rem;
|
|
247
|
+
text-align: left;
|
|
248
|
+
}
|
|
249
|
+
|
|
210
250
|
/* Payout Table */
|
|
211
251
|
.spot-table__container {
|
|
212
252
|
width: 100%;
|
package/src/ui.js
CHANGED
|
@@ -38,6 +38,32 @@ export function renderBenefits(container, bullets = []) {
|
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// covered items for multi-quote
|
|
42
|
+
export function renderCoveredItems(container, coveredItems = []) {
|
|
43
|
+
if (coveredItems.length === 0) return;
|
|
44
|
+
|
|
45
|
+
const wrapper = makeEl("div", {
|
|
46
|
+
className: "spot-covered-items__container",
|
|
47
|
+
parent: container,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
makeEl("div", {
|
|
51
|
+
className: "spot-covered-items__title",
|
|
52
|
+
text: "Items covered in your cart:",
|
|
53
|
+
parent: wrapper,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const ul = makeEl("ul", {
|
|
57
|
+
className: "spot-covered-items__list",
|
|
58
|
+
parent: wrapper,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
coveredItems.forEach((item) => {
|
|
62
|
+
const li = makeEl("li", { parent: ul });
|
|
63
|
+
makeEl("span", { text: item, parent: li });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
// payout table
|
|
42
68
|
export function renderTable(container, schedule = []) {
|
|
43
69
|
const wrapper = makeEl("div", {
|
|
@@ -67,7 +93,7 @@ export function renderTable(container, schedule = []) {
|
|
|
67
93
|
}
|
|
68
94
|
|
|
69
95
|
// radio buttons
|
|
70
|
-
export function renderOptions(container,
|
|
96
|
+
export function renderOptions(container, optInSelected, communication) {
|
|
71
97
|
const optionsWrapper = makeEl("div", {
|
|
72
98
|
className: "spot-selection__options",
|
|
73
99
|
parent: container,
|
|
@@ -85,7 +111,7 @@ export function renderOptions(container, spotPrice, optInSelected) {
|
|
|
85
111
|
if (optInSelected) yesInput.checked = true;
|
|
86
112
|
|
|
87
113
|
makeEl("strong", {
|
|
88
|
-
text:
|
|
114
|
+
text: communication.yesOptionText,
|
|
89
115
|
parent: yesOption,
|
|
90
116
|
});
|
|
91
117
|
|
|
@@ -105,7 +131,7 @@ export function renderOptions(container, spotPrice, optInSelected) {
|
|
|
105
131
|
noInput.name = "selection";
|
|
106
132
|
noInput.value = "no";
|
|
107
133
|
|
|
108
|
-
makeEl("span", { text:
|
|
134
|
+
makeEl("span", { text: communication.noOptionText, parent: noOption });
|
|
109
135
|
|
|
110
136
|
return optionsWrapper;
|
|
111
137
|
}
|