@getspot/spot-widget 1.1.1 → 1.3.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 +4 -4
- package/CHANGELOG.md +12 -0
- package/dist/index.es.js +399 -204
- package/dist/index.umd.js +3 -3
- package/package.json +1 -1
- package/src/api.js +122 -0
- package/src/index.js +90 -26
- package/src/styles.css +42 -2
- package/src/ui.js +26 -0
- package/src/validateOptions.js +168 -58
package/dist/index.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(_,C){typeof exports=="object"&&typeof module<"u"?module.exports=C():typeof define=="function"&&define.amd?define(C):(_=typeof globalThis<"u"?globalThis:_||self,_.SpotWidget=C())})(this,function(){"use strict";async function _(c,e,o){try{const n=await fetch(c,{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 C(c,e,o){try{const n=c.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 p=new Error((r==null?void 0:r.message)||"Failed to fetch batch quote");throw p.status=t.status,p.responseBody=r,p}return r}catch(n){throw n instanceof Error?n:new Error("Unknown error occurred while fetching batch quote")}}async function I(c,e,o){try{const n={cartId:o.cartInfo.cartId,cartName:o.cartInfo.cartName,currencyCode:o.cartInfo.currencyCode,items:o.items.map((i,u)=>({cartItemId:i.cartItemId||`item-${u+1}`,productPrice:i.productPrice,productType:i.productType,productDuration:i.productDuration,productId:i.productId,productName:i.productName,participantDescription:i.participantDescription,eventType:i.eventType,startDate:i.startDate,endDate:i.endDate}))},t=await C(c,e,n);if(t.status!=="QUOTES_AVAILABLE"&&t.status!=="QUOTE_AVAILABLE")return{status:"NO_MATCHING_QUOTE"};const r=t.quotes.map(i=>{const u=o.items.find(g=>(g.cartItemId||`item-${o.items.indexOf(g)+1}`)===i.cartItemId);return u?u.participantDescription?`${u.productName} - ${u.participantDescription}`:u.productName:`Item ${i.cartItemId}`}),p=Math.round((t.totalSpotPrice||t.spotPrice||0)*100)/100;return{status:"QUOTE_AVAILABLE",data:{id:t.quotes?t.quotes.map(i=>i.id).join(","):t.id,spotPrice:p,currencyCode:t.currencyCode,communication:{...t.communication,yesOptionText:t.communication.yesOptionText.replace(t.totalSpotPrice,p)},payoutSchedule:t.payoutSchedule.map(i=>({...i,amount:i.amount!==void 0?i.amount:0})),coveredItems:r,originalQuotes:t.quotes||[t]},spotPrice:p,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 D(c){const{apiConfig:e={},quoteRequestData:o,callbacks:n={},location:t,theme:r}=c,{environment:p="sandbox",partnerId:i,endpoint:u}=e;if(!i||typeof i!="string")throw new Error("Invalid or missing partnerId in apiConfig");if(!(u||N[p]))throw new Error(`Invalid environment in apiConfig: ${p}`);if(!o||typeof o!="object"&&!Array.isArray(o))throw new Error("quoteRequestData must be a non-null object or array");const h=["startDate","endDate","currencyCode","eventType","productType","productDuration","productPrice","productId","cartId","productName"];function d(s,l=null){const f=l!==null?`quoteRequestData[${l}]`:"quoteRequestData";h.forEach(v=>{if(!Object.prototype.hasOwnProperty.call(s,v)||s[v]===void 0||s[v]===null)throw new Error(`Missing required ${f} field: '${v}'`)});const w=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;if(!w.test(s.startDate))throw new Error(`${f}.startDate must be a valid ISO8601 string`);if(!w.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 E=["Pass","Trip","Registration"];if(!E.includes(s.productType))throw new Error(`${f}.productType must be one of ${E.join(", ")}`);if(typeof s.productDuration!="string")throw new Error(`${f}.productDuration must be a string`);const y=["Daily","Seasonal","Trip","Event"];if(!y.includes(s.productDuration))throw new Error(`${f}.productDuration must be one of ${y.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:l}=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(l)||l.length===0)throw new Error("quoteRequestData.items must be a non-empty array");const w=h.filter(m=>m!=="cartId"&&m!=="currencyCode");l.forEach((m,E)=>{if(!m||typeof m!="object")throw new Error(`quoteRequestData.items[${E}] must be a non-null object`);const y=`quoteRequestData.items[${E}]`;w.forEach(q=>{if(!Object.prototype.hasOwnProperty.call(m,q)||m[q]===void 0||m[q]===null)throw new Error(`Missing required ${y} field: '${q}'`)});const v=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;if(!v.test(m.startDate))throw new Error(`${y}.startDate must be a valid ISO8601 string`);if(!v.test(m.endDate))throw new Error(`${y}.endDate must be a valid ISO8601 string`);if(typeof m.eventType!="string")throw new Error(`${y}.eventType must be a string`);if(typeof m.productType!="string")throw new Error(`${y}.productType must be a string`);const T=["Pass","Trip","Registration"];if(!T.includes(m.productType))throw new Error(`${y}.productType must be one of ${T.join(", ")}`);if(typeof m.productDuration!="string")throw new Error(`${y}.productDuration must be a string`);const x=["Daily","Seasonal","Trip","Event"];if(!x.includes(m.productDuration))throw new Error(`${y}.productDuration must be one of ${x.join(", ")}`);if(typeof m.productPrice!="number"||isNaN(m.productPrice))throw new Error(`${y}.productPrice must be a valid number`);if(typeof m.productId!="string")throw new Error(`${y}.productId must be a string`);if(typeof m.productName!="string")throw new Error(`${y}.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,l)=>{if(!s||typeof s!="object")throw new Error(`quoteRequestData[${l}] must be a non-null object`);d(s,l)})}else d(o);if(["onOptIn","onOptOut","onQuoteRetrieved","onError","noMatchingQuote"].forEach(s=>{const l=n[s];if(l&&typeof l!="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 a(c,{text:e,className:o,parent:n,innerHTML:t}={}){const r=document.createElement(c);return o&&(r.className=o),e!=null&&(r.textContent=e),t!=null&&(r.innerHTML=t),n&&n.appendChild(r),r}function O(c,{name:e,description:o}){a("div",{className:"spot-header__title",text:e,parent:c}),a("div",{className:"spot-header__description",text:o,parent:c})}function P(c,e=[]){const o=a("ul",{className:"spot-benefits__list",parent:c});e.forEach(n=>{const t=a("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>`,a("span",{text:n,parent:t})})}function $(c,e=[]){if(e.length===0)return;const o=a("div",{className:"spot-covered-items__container",parent:c});a("div",{className:"spot-covered-items__title",text:"Items covered in your cart:",parent:o});const n=a("ul",{className:"spot-covered-items__list",parent:o});e.forEach(t=>{const r=a("li",{parent:n});a("span",{text:t,parent:r})})}function R(c,e=[]){const o=a("div",{className:"spot-table__container",parent:c}),n=a("table",{className:"spot-refund__table spot-table--dynamic",parent:o}),t=a("thead",{parent:n}),r=a("tr",{parent:t});a("th",{text:"When you cancel",parent:r}),a("th",{text:"You will receive",parent:r});const p=a("tbody",{parent:n});e.forEach(({text:i,percent:u,amount:g})=>{const h=a("tr",{parent:p});a("td",{text:i,parent:h});const d=u==="Not eligible for refund"?"Not eligible for a refund":`$${g} refund`;a("td",{text:d,parent:h})})}function Q(c,e,o){const n=a("div",{className:"spot-selection__options",parent:c}),t=a("label",{className:`spot-selection__option ${e?"selected":""}`,parent:n}),r=a("input",{parent:t});r.type="radio",r.name="selection",r.value="yes",e&&(r.checked=!0),a("strong",{text:o.yesOptionText,parent:t}),a("span",{className:"spot-selection__recommended-tag",text:"Recommended",parent:t});const p=a("label",{className:"spot-selection__option",parent:n}),i=a("input",{parent:p});return i.type="radio",i.name="selection",i.value="no",a("span",{text:o.noOptionText,parent:p}),n}function S(c,e){var t;const o=(t=e.communication)==null?void 0:t.paymentTerms,n=a("div",{className:"spot-payment-terms",parent:c});return a("div",{className:"spot-payment-terms__header",text:"PAYMENT TERMS",parent:n}),a("div",{className:"spot-payment-terms__body",text:o,parent:n}),n}function H(c,e){const o=a("div",{className:"spot-footer__container",parent:c}),n=a("div",{className:"spot-footer__terms",parent:o});a("span",{innerHTML:e.communication.legalDisclaimer,parent:n}),a("br",{parent:n}),a("a",{href:e.communication.termsAndConditionsUrl,className:"spot-footer__terms-link",text:"Refund Guarantee Terms and Conditions",parent:n});const t=a("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 z{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.options.optInSelected,this.quote.communication);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 z});
|
|
20
|
+
</svg>`,o}const z=":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 M(c){const e=document.createElement("style");e.textContent=c,document.head.appendChild(e)}M(z);const k={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{D(this.options);const{environment:p,partnerId:i}=this.options.apiConfig,g=this.options.apiConfig.customEndpoint||k[p],d=this.options.quoteRequestData.cartInfo&&this.options.quoteRequestData.items?await I(g,i,this.options.quoteRequestData):await _(g,i,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 b={status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id};this.quote.originalQuotes&&this.quote.originalQuotes.length>0&&(b.batchQuoteDetails=this.quote.originalQuotes.map(s=>{var f;const l=(f=this.options.quoteRequestData.items)==null?void 0:f.find(w=>(w.cartItemId||`item-${this.options.quoteRequestData.items.indexOf(w)+1}`)===s.cartItemId);return{quoteId:s.id,productPrice:(l==null?void 0:l.productPrice)||s.spotPrice,cartItemId:s.cartItemId}})),this.options.callbacks.onOptIn(b)}(n=this.options.callbacks)!=null&&n.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote)}catch(p){(t=this.options.callbacks)!=null&&t.onError&&((r=this.options.callbacks)==null||r.onError({message:p.message,status:p.status,responseBody:p.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&&R(e,this.quote.payoutSchedule);const o=Q(e,this.options.optInSelected,this.quote.communication);e.appendChild(o),this.paymentTermsEl=a("div",{className:"spot-payment-terms__wrapper",parent:e}),H(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 i,u,g;const p=r.target.value;if(this.hideSelectionError(),this.currentSelection=p,n.forEach(h=>h.classList.remove("selected")),(i=r.target.closest(".spot-selection__option"))==null||i.classList.add("selected"),this.paymentTermsEl&&(this.paymentTermsEl.innerHTML=""),p==="yes"&&(this.options.quoteRequestData.isPartialPayment&&S(this.paymentTermsEl,this.quote),(u=this.options.callbacks)!=null&&u.onOptIn)){const h={status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id};this.quote.originalQuotes&&this.quote.originalQuotes.length>0&&(h.batchQuoteDetails=this.quote.originalQuotes.map(d=>{var s;const b=(s=this.options.quoteRequestData.items)==null?void 0:s.find(l=>(l.cartItemId||`item-${this.options.quoteRequestData.items.indexOf(l)+1}`)===d.cartItemId);return{quoteId:d.id,productPrice:(b==null?void 0:b.productPrice)||d.spotPrice,cartItemId:d.cartItemId}})),this.options.callbacks.onOptIn(h)}if(p==="no"&&((g=this.options.callbacks)!=null&&g.onOptOut)){const h={status:"QUOTE_DECLINED",quoteId:this.quote.id};this.quote.originalQuotes&&this.quote.originalQuotes.length>0&&(h.batchQuoteDetails=this.quote.originalQuotes.map(d=>{var s;const b=(s=this.options.quoteRequestData.items)==null?void 0:s.find(l=>(l.cartItemId||`item-${this.options.quoteRequestData.items.indexOf(l)+1}`)===d.cartItemId);return{quoteId:d.id,productPrice:(b==null?void 0:b.productPrice)||d.spotPrice,cartItemId:d.cartItemId}})),this.options.callbacks.onOptOut(h)}})})}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};D(r);const{environment:p,partnerId:i,endpoint:u}=this.options.apiConfig,g=u||k[p],d=r.quoteRequestData.cartInfo&&r.quoteRequestData.items?await I(g,i,r.quoteRequestData):await _(g,i,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=>{var i;const p=(i=this.options.quoteRequestData.items)==null?void 0:i.find(u=>(u.cartItemId||`item-${this.options.quoteRequestData.items.indexOf(u)+1}`)===r.cartItemId);return{quoteId:r.id,productPrice:(p==null?void 0:p.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,27 @@ 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
|
+
const originalItem = this.options.quoteRequestData.items?.find(item =>
|
|
93
|
+
(item.cartItemId || `item-${this.options.quoteRequestData.items.indexOf(item) + 1}`) === q.cartItemId
|
|
94
|
+
);
|
|
95
|
+
return {
|
|
96
|
+
quoteId: q.id,
|
|
97
|
+
productPrice: originalItem?.productPrice || q.spotPrice,
|
|
98
|
+
cartItemId: q.cartItemId
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.options.callbacks.onOptIn(optInData);
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
if (this.options.callbacks?.onQuoteRetrieved) {
|
|
@@ -116,6 +133,9 @@ class SpotWidget {
|
|
|
116
133
|
this.container.appendChild(cw);
|
|
117
134
|
|
|
118
135
|
renderBenefits(cw, this.quote.communication.bulletPoints);
|
|
136
|
+
if (this.quote.coveredItems) {
|
|
137
|
+
renderCoveredItems(cw, this.quote.coveredItems);
|
|
138
|
+
}
|
|
119
139
|
if (this.options.showTable) renderTable(cw, this.quote.payoutSchedule);
|
|
120
140
|
const optsEl = renderOptions(
|
|
121
141
|
cw,
|
|
@@ -162,18 +182,50 @@ class SpotWidget {
|
|
|
162
182
|
renderPaymentTerms(this.paymentTermsEl, this.quote);
|
|
163
183
|
}
|
|
164
184
|
if (this.options.callbacks?.onOptIn) {
|
|
165
|
-
|
|
185
|
+
const optInData = {
|
|
166
186
|
status: "QUOTE_ACCEPTED",
|
|
167
187
|
spotPrice: this.quote.spotPrice,
|
|
168
|
-
quoteId: this.quote.id
|
|
169
|
-
}
|
|
188
|
+
quoteId: this.quote.id
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// For batch quotes, include detailed quote information
|
|
192
|
+
if (this.quote.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
193
|
+
optInData.batchQuoteDetails = this.quote.originalQuotes.map(q => {
|
|
194
|
+
const originalItem = this.options.quoteRequestData.items?.find(item =>
|
|
195
|
+
(item.cartItemId || `item-${this.options.quoteRequestData.items.indexOf(item) + 1}`) === q.cartItemId
|
|
196
|
+
);
|
|
197
|
+
return {
|
|
198
|
+
quoteId: q.id,
|
|
199
|
+
productPrice: originalItem?.productPrice || q.spotPrice,
|
|
200
|
+
cartItemId: q.cartItemId
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.options.callbacks.onOptIn(optInData);
|
|
170
206
|
}
|
|
171
207
|
}
|
|
172
208
|
if (val === "no" && this.options.callbacks?.onOptOut) {
|
|
173
|
-
|
|
209
|
+
const optOutData = {
|
|
174
210
|
status: "QUOTE_DECLINED",
|
|
175
|
-
quoteId: this.quote.id
|
|
176
|
-
}
|
|
211
|
+
quoteId: this.quote.id
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// For batch quotes, include detailed quote information
|
|
215
|
+
if (this.quote.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
216
|
+
optOutData.batchQuoteDetails = this.quote.originalQuotes.map(q => {
|
|
217
|
+
const originalItem = this.options.quoteRequestData.items?.find(item =>
|
|
218
|
+
(item.cartItemId || `item-${this.options.quoteRequestData.items.indexOf(item) + 1}`) === q.cartItemId
|
|
219
|
+
);
|
|
220
|
+
return {
|
|
221
|
+
quoteId: q.id,
|
|
222
|
+
productPrice: originalItem?.productPrice || q.spotPrice,
|
|
223
|
+
cartItemId: q.cartItemId
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.options.callbacks.onOptOut(optOutData);
|
|
177
229
|
}
|
|
178
230
|
});
|
|
179
231
|
});
|
|
@@ -221,10 +273,7 @@ class SpotWidget {
|
|
|
221
273
|
try {
|
|
222
274
|
const updatedOptions = {
|
|
223
275
|
...this.options,
|
|
224
|
-
quoteRequestData:
|
|
225
|
-
...this.options.quoteRequestData,
|
|
226
|
-
...newQuoteRequestData,
|
|
227
|
-
},
|
|
276
|
+
quoteRequestData: newQuoteRequestData
|
|
228
277
|
};
|
|
229
278
|
|
|
230
279
|
validateOptions(updatedOptions);
|
|
@@ -237,11 +286,10 @@ class SpotWidget {
|
|
|
237
286
|
|
|
238
287
|
const endpoint = customEndpoint || apiEndpoint[environment];
|
|
239
288
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
partnerId,
|
|
243
|
-
updatedOptions.quoteRequestData
|
|
244
|
-
);
|
|
289
|
+
const isBatchQuote = updatedOptions.quoteRequestData.cartInfo && updatedOptions.quoteRequestData.items;
|
|
290
|
+
const response = isBatchQuote
|
|
291
|
+
? await fetchMultipleQuotes(endpoint, partnerId, updatedOptions.quoteRequestData)
|
|
292
|
+
: await fetchQuote(endpoint, partnerId, updatedOptions.quoteRequestData);
|
|
245
293
|
|
|
246
294
|
if (response.status !== "QUOTE_AVAILABLE") {
|
|
247
295
|
if (
|
|
@@ -282,13 +330,29 @@ class SpotWidget {
|
|
|
282
330
|
getSelection() {
|
|
283
331
|
if (this.currentSelection == null) return null;
|
|
284
332
|
|
|
285
|
-
|
|
333
|
+
const selectionData = {
|
|
286
334
|
selection: this.currentSelection,
|
|
287
335
|
quoteId: this.quote?.id,
|
|
288
336
|
spotPrice: this.quote?.spotPrice,
|
|
289
337
|
status:
|
|
290
|
-
this.currentSelection === "yes" ? "QUOTE_ACCEPTED" : "QUOTE_DECLINED"
|
|
338
|
+
this.currentSelection === "yes" ? "QUOTE_ACCEPTED" : "QUOTE_DECLINED"
|
|
291
339
|
};
|
|
340
|
+
|
|
341
|
+
// For batch quotes, include detailed quote information
|
|
342
|
+
if (this.quote?.originalQuotes && this.quote.originalQuotes.length > 0) {
|
|
343
|
+
selectionData.batchQuoteDetails = this.quote.originalQuotes.map(q => {
|
|
344
|
+
const originalItem = this.options.quoteRequestData.items?.find(item =>
|
|
345
|
+
(item.cartItemId || `item-${this.options.quoteRequestData.items.indexOf(item) + 1}`) === q.cartItemId
|
|
346
|
+
);
|
|
347
|
+
return {
|
|
348
|
+
quoteId: q.id,
|
|
349
|
+
productPrice: originalItem?.productPrice || q.spotPrice,
|
|
350
|
+
cartItemId: q.cartItemId
|
|
351
|
+
};
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return selectionData;
|
|
292
356
|
}
|
|
293
357
|
|
|
294
358
|
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", {
|