1import{css,html,LitElement}from"https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js";class SkioAddToSubscription extends LitElement{static styles=css` 2 :host { 3 --color-primary: #111827; 4 --color-accent: #fff; 5 --color-bg: #f7f2ee; 6 --color-card-bg: #f5f5f5; 7 --color-border: #e0e0e0; 8 --color-text: #333; 9 --color-muted: #666; 10 --color-strikethrough: #999; 11 --color-badge: #ffcf42; 12 position: relative; 13 display: block; 14 font-family: inherit; 15 font-size: 16px; 16 } 17 .callout { 18 background: var(--color-card-bg); 19 border-radius: 8px; 20 padding: 24px; 21 margin-bottom: 24px; 22 border: 1px solid var(--color-border); 23 position: relative; 24 } 25 .callout h3 { 26 margin: 0; 27 font-size: 1em; 28 font-weight: 700; 29 color: var(--color-text); 30 } 31 .callout small { 32 display: block; 33 margin-bottom: 16px; 34 } 35 .callout p { 36 margin: 0 0 16px 0; 37 color: var(--color-muted); 38 font-size: 0.9em; 39 } 40 .add-to-subscription-button, 41 .primary-button, 42 .secondary-button { 43 --tw-bg-opacity: 1; 44 background-color: rgb(51 51 51 / var(--tw-bg-opacity)); 45 --tw-text-opacity: 1; 46 color: rgb(255 255 255 / var(--tw-text-opacity)); 47 border-radius: 9999px; 48 width: 100%; 49 font-size: 1rem; 50 line-height: 1.5rem; 51 font-weight: 600; 52 cursor: pointer; 53 padding-top: 0.75rem; 54 padding-bottom: 0.75rem; 55 padding-left: 2.5rem; 56 padding-right: 2.5rem; 57 font-family: 58 Proxima Nova, 59 sans-serif; 60 transition-property: color, background-color, border-color, 61 text-decoration-color, fill, stroke; 62 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 63 transition-duration: 0.15s; 64 } 65 .add-to-subscription-button:hover, 66 .primary-button:hover, 67 .secondary-button:hover { 68 --tw-bg-opacity: 1; 69 background-color: rgb(82 82 82 / var(--tw-bg-opacity)); 70 transition-property: color, background-color, border-color, 71 text-decoration-color, fill, stroke; 72 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 73 transition-duration: 0.15s; 74 } 75 dialog { 76 position: fixed; 77 top: 50%; 78 left: 50%; 79 transform: translate(-50%, -50%); 80 margin: 0; 81 z-index: 2147483647; 82 background: var(--color-accent); 83 border-radius: 12px; 84 border: none; 85 width: 100%; 86 max-width: 500px; 87 padding: 0; 88 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18); 89 overflow: hidden; 90 box-sizing: border-box; 91 } 92 dialog:not([open]) { 93 display: none; 94 } 95 dialog::backdrop { 96 background: rgba(0, 0, 0, 0.5); 97 } 98 .modal-content { 99 position: relative; 100 overflow-x: hidden; 101 max-width: 100%; 102 } 103 .modal-header { 104 display: flex; 105 justify-content: space-between; 106 align-items: center; 107 gap: 16px; 108 padding: 16px 32px 16px 32px; 109 border-bottom: 1px solid var(--color-border); 110 } 111 .modal-title { 112 font-size: 1.1em; 113 font-weight: 700; 114 color: var(--color-text); 115 margin: 0; 116 line-height: 1.3; 117 } 118 .modal-title .date-badge { 119 border: 2px solid var(--color-primary); 120 border-radius: 4px; 121 padding: 2px 8px; 122 font-size: 0.8em; 123 color: var(--color-primary); 124 font-weight: 500; 125 background: var(--color-accent); 126 display: inline-block; 127 } 128 .close-button { 129 background: none; 130 border: none; 131 font-size: 2em; 132 color: var(--color-muted); 133 cursor: pointer; 134 padding: 0; 135 margin: 0; 136 width: 2em; 137 height: 2em; 138 display: flex; 139 align-items: center; 140 justify-content: center; 141 line-height: 1; 142 } 143 .close-button:hover { 144 color: var(--color-text); 145 } 146 .modal-body { 147 padding: 16px 32px; 148 overflow-x: hidden; 149 } 150 .modal-body p { 151 margin: 0; 152 color: var(--color-muted); 153 font-size: 0.9em; 154 line-height: 1.5; 155 max-width: 100%; 156 white-space: normal; 157 overflow-wrap: break-word; 158 } 159 form { 160 padding: 24px 32px; 161 display: flex; 162 flex-direction: column; 163 gap: 16px; 164 } 165 .option-label { 166 display: flex; 167 align-items: center; 168 gap: 8px; 169 background: var(--color-card-bg); 170 border: 1px solid var(--color-border); 171 border-radius: 8px; 172 padding: 12px; 173 cursor: pointer; 174 transition: 175 border 0.2s, 176 background 0.2s; 177 position: relative; 178 } 179 .option-label:hover { 180 border-color: var(--color-primary); 181 } 182 .option-label.selected { 183 border: 2px solid var(--color-primary); 184 background: var(--color-accent); 185 } 186 .option-label input[type='radio'] { 187 accent-color: var(--color-primary); 188 width: 1.5em; 189 height: 1.5em; 190 flex-shrink: 0; 191 } 192 .option-content { 193 display: flex; 194 align-items: flex-start; 195 flex-direction: column; 196 gap: 4px; 197 } 198 .option-title { 199 font-size: 1em; 200 font-weight: 700; 201 color: var(--color-text); 202 } 203 .delivery-policy { 204 font-size: 0.8em; 205 color: var(--color-muted); 206 font-weight: 500; 207 text-transform: capitalize; 208 } 209 .discount-badge { 210 background: var(--color-badge); 211 color: var(--color-text); 212 font-weight: 600; 213 border-radius: 50px; 214 padding: 4px 12px; 215 font-size: 0.8em; 216 text-transform: uppercase; 217 letter-spacing: 0.5px; 218 line-height: normal; 219 } 220 .price-container { 221 margin-left: auto; 222 text-align: right; 223 display: flex; 224 flex-direction: column; 225 align-items: flex-end; 226 gap: 2px; 227 } 228 .original-price { 229 text-decoration: line-through; 230 color: var(--color-strikethrough); 231 font-size: 0.9em; 232 font-weight: 500; 233 } 234 .discounted-price { 235 color: var(--color-primary); 236 font-size: 1em; 237 font-weight: 700; 238 }
239 .modal-policy { 240 font-size: 0.8em; 241 color: var(--color-muted); 242 padding: 0px 32px 16px 32px; 243 white-space: initial; 244 } 245 .modal-footer { 246 display: flex; 247 gap: 16px; 248 padding: 24px 32px 32px 32px; 249 border-top: 1px solid var(--color-border); 250 } 251 .secondary-button { 252 background: transparent; 253 color: var(--color-text); 254 border: 2px solid var(--color-border); 255 } 256 .secondary-button:hover { 257 background: var(--color-card-bg); 258 border-color: var(--color-primary); 259 } 260 .success-screen { 261 background: #e8f5e8; 262 border: 2px solid #4caf50; 263 border-radius: 8px; 264 padding: 24px; 265 margin-bottom: 24px; 266 text-align: left; 267 } 268 .success-title { 269 font-size: 1em; 270 font-weight: 700; 271 color: var(--color-text); 272 margin: 0 0 16px 0; 273 text-transform: uppercase; 274 letter-spacing: 0.5px; 275 } 276 .success-message { 277 font-size: 0.9em; 278 color: var(--color-text); 279 margin: 0 0 8px 0; 280 line-height: 1.4; 281 } 282 .success-date { 283 font-weight: 700; 284 color: var(--color-text); 285 } 286 .success-link { 287 color: var(--color-primary); 288 text-decoration: none; 289 font-weight: 700; 290 font-size: 0.9em; 291 margin-top: 16px; 292 display: inline-block; 293 transition: color 0.2s; 294 } 295 .success-link:hover { 296 color: color-mix(in srgb, var(--color-primary) 80%, black); 297 text-decoration: underline; 298 } 299 .sign-in-callout p { 300 margin: 0; 301 } 302 .sign-in-link { 303 color: var(--color-primary); 304 font-weight: 700; 305 text-decoration: underline; 306 } 307 .sign-in-link:hover { 308 color: color-mix(in srgb, var(--color-primary) 80%, black); 309 } 310 .loading-spinner { 311 display: inline-block; 312 width: 16px; 313 height: 16px; 314 border: 2px solid var(--color-accent); 315 border-radius: 50%; 316 border-top-color: transparent; 317 animation: spin 1s linear infinite; 318 margin-right: 8px; 319 } 320 @keyframes spin { 321 to { 322 transform: rotate(360deg); 323 } 324 } 325 .primary-button:disabled { 326 opacity: 0.6; 327 cursor: not-allowed; 328 } 329 .error-message { 330 background: #fee; 331 border: 1px solid #fcc; 332 border-radius: 4px; 333 padding: 12px; 334 margin-bottom: 16px; 335 color: #c33; 336 font-size: 0.9em; 337 text-align: center; 338 } 339 /* Debug / setup mode â matches plan picker's visual language */ 340 .setup-mode { 341 padding: 16px; 342 border: 5px solid transparent; 343 border-image: repeating-linear-gradient( 344 -55deg, 345 #000 0, 346 #000 20px, 347 #ffb101 20px, 348 #ffb101 40px 349 ) 350 10; 351 margin-bottom: 16px; 352 } 353 354 .setup-mode-label { 355 font-size: 0.75em; 356 font-weight: 700; 357 text-transform: uppercase; 358 letter-spacing: 1px; 359 color: #000; 360 background: #ffb101; 361 padding: 2px 8px; 362 margin-bottom: 12px; 363 display: inline-block; 364 } 365 366 .setup-mode-note { 367 font-size: 0.85em; 368 color: #333; 369 margin: 0 0 12px 0; 370 } 371 372 .debug-mode-toggle { 373 display: flex; 374 gap: 8px; 375 margin-bottom: 16px; 376 } 377 378 .debug-mode-toggle button { 379 font-size: 0.8em; 380 font-weight: 700; 381 padding: 4px 12px; 382 border: 2px solid #000; 383 background: transparent; 384 cursor: pointer; 385 text-transform: uppercase; 386 letter-spacing: 0.5px; 387 } 388 389 .debug-mode-toggle button.active { 390 background: #000; 391 color: #fff; 392 } 393 394 .debug-modal-preview { 395 border: 1px solid var(--skio-group-border-color, #e0e0e0); 396 border-radius: 0; 397 max-width: 500px; 398 background: var(--skio-group-background-color, #fff); 399 margin-top: 8px; 400 box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); 401 overflow: hidden; 402 } 403 404 @media (max-width: 500px) { 405 .modal-footer { 406 flex-direction: column; 407 gap: 8px; 408 padding: 12px 16px 16px 16px; 409 } 410 .modal-header { 411 padding: 12px 16px 12px 16px; 412 }
413 .modal-body { 414 padding: 12px 16px; 415 } 416 form { 417 padding: 12px 16px; 418 } 419 .modal-policy { 420 padding: 0px 16px 16px 16px; 421 } 422 } 423 `;static properties={isLoading:{type:Boolean},isAuthenticated:{type:Boolean},productId:{type:String},variantId:{type:String,attribute:"variant-id"},product:{type:Object},selectedVariant:{type:Object},upcomingSubscription:{type:Object},inactiveSubscription:{type:Object},failedSubscription:{type:Object},eligibleForSubscription:{type:Boolean},eligibleForOneTimeUpsell:{type:Boolean},selectedOption:{type:String},selectedSellingPlan:{type:Object},showSuccessScreen:{type:Boolean},isSubmitting:{type:Boolean},submitError:{type:String},debug:{type:Boolean},options:{type:String},debugPreviewMode:{type:String},ready:{type:Boolean},successMode:{type:String},successWasPaused:{type:Boolean}};get renderState(){return this.showSuccessScreen?"success":this.ready&&!this.isAuthenticated?"sign-in":this.mode}get signInUrl(){return SkioAddToSubscription.buildSignInUrl(this._options)}get signInPrompt(){return SkioAddToSubscription.getPrompt("sign-in",this._options)}get paymentFailedPrompt(){return SkioAddToSubscription.getPrompt("failed",this._options)}isVariantInUpcomingSubscription(variant){const variantId=variant?.platformId,lines=this.upcomingSubscription?.SubscriptionLines;return!variantId||!Array.isArray(lines)||!lines.length?!1:lines.some(line=>String(line?.ProductVariant?.platformId)===String(variantId))}get _options(){try{return JSON.parse(this.options||"{}")}catch{return{}}}get mode(){return this.upcomingSubscription&&this.eligibleForSubscription&&this.selectedVariant?"add":this.failedSubscription?"failed":this.inactiveSubscription?"reactivate":null}constructor(){super(),this.isLoading=!1,this.isAuthenticated=!1,this.failedSubscription=null,this.eligibleForSubscription=!1,this.eligibleForOneTimeUpsell=!1,this.selectedOption="subscription",this.showSuccessScreen=!1,this.isSubmitting=!1,this.submitError=null,this.debugPreviewMode="add",this.debug=!1,this.isPreviewMode=!1,this.ready=!1,this.successMode="added",this.successWasPaused=!1,this._handleActionBackdropClick=e=>{e.target.tagName==="DIALOG"&&this.closeModal()}}notifySubscriptionContext(){const detail={renderState:this.renderState,upcomingSubscription:this.upcomingSubscription??null};return this.dispatchEvent(new CustomEvent("skio-component:context-updated",{detail})),detail}async connectedCallback(){if(super.connectedCallback(),this.isPreviewMode=!!window?.Shopify?.designMode,this.debug=this.debug||this.hasAttribute("debug")||!!window.__SKIO_ADD_TO_SUBSCRIPTION_DEBUG__,this.isPreviewMode)return;await this.initialize(),this.isLoading=!1,this.bindFormEvents(),this.ready=!0;const context=this.notifySubscriptionContext();this.dispatchEvent(new CustomEvent("skio-component:ready",{detail:context}))}loadSDK(){return SkioAddToSubscription.loadSdk()}async initialize(){if(await this.loadSDK(),await SkioAddToSubscription.waitForStorefrontUser(),this.debug&&console.log("[Skio AddToSub] initialize:start",{productId:this.productId}),this.isAuthenticated=!!window.Skio?.storefrontUser?.id,!this.isAuthenticated){this.debug&&console.log("[Skio AddToSub] initialize:guest");return}this.upcomingSubscription=await this.fetchSubscription("ACTIVE"),this.upcomingSubscription?(this.product=await this.fetchProduct(),this.applyInitialVariantSelection(),this.debug&&console.log("[Skio AddToSub] initialize:active-subscription",{subscriptionId:this.upcomingSubscription.id,hasProduct:!!this.product,defaultVariantPlatformId:this.selectedVariant?.platformId??null,eligibleForSubscription:this.eligibleForSubscription})):(this.inactiveSubscription=await this.fetchSubscription(["CANCELLED","PAUSED"]),this.inactiveSubscription||(this.failedSubscription=await this.fetchSubscription("FAILED")),this.debug&&console.log("[Skio AddToSub] initialize:inactive-subscription-check",{failedStatus:this.failedSubscription?.status??null,failedSubscriptionId:this.failedSubscription?.id??null,status:this.inactiveSubscription?.status??null,subscriptionId:this.inactiveSubscription?.id??null}),this.inactiveSubscription&&this.productId&&(this.product=await this.fetchProduct(),this.applyInitialVariantSelection())),this.debug&&console.log("[
423Skio AddToSub] initialize:end",{hasUpcoming:!!this.upcomingSubscription,hasInactive:!!this.inactiveSubscription,mode:this.mode})}async fetchSubscription(status){return SkioAddToSubscription.fetchSubscription(status,{includeLines:status==="ACTIVE"})}async fetchProduct(){const query=` 424 query Products($where: Product_bool_exp) { 425 Products(where: $where) { 426 title 427 canUpsell 428 oneTimeUpsellPercentageOff 429 ProductVariants { 430 enableOneTimeUpsell 431 platformId 432 price 433 compareAtPrice 434 SellingPlanGroupResources { 435 SellingPlanGroup { 436 customName 437 SellingPlans { 438 id 439 platformId 440 PricingPolicies { 441 percentageOff 442 fixedPrice 443 } 444 DeliveryPolicy { 445 interval 446 intervalCount 447 } 448 } 449 } 450 } 451 } 452 } 453 } 454 `,variables={where:{platformId:{_eq:`gid://shopify/Product/${this.productId}`}}};try{return(await window.Skio.graphQLRequest({query,variables})).data.Products[0]}catch(error){return console.error("Error fetching product:",error),null}}applyInitialVariantSelection(){this.product?.ProductVariants?.length&&(this.variantId?this.updateSelectedVariant(this.variantId):this.selectedVariant||(this.selectedVariant=this.product.ProductVariants[0]),this.checkVariantEligibility(this.selectedVariant))}bindFormEvents(){if(this.variantId){this.applyInitialVariantSelection(),this.successMode="added",this.showSuccessScreen=this.isVariantInUpcomingSubscription(this.selectedVariant),this.dispatchVariantChanged();return}const variantInput=this.getVariantIdInput();if(!variantInput){this.debug&&console.log("[Skio AddToSub] bindFormEvents:no-variant-input",{selectedVariant:this.selectedVariant?.platformId??null}),this.dispatchVariantChanged();return}this.updateSelectedVariant(variantInput.value),this.checkVariantEligibility(this.selectedVariant),this.successMode="added",this.showSuccessScreen=this.isVariantInUpcomingSubscription(this.selectedVariant),this.debug&&console.log("[Skio AddToSub] bindFormEvents:initial-variant",{variantInputValue:variantInput.value,selectedVariant:this.selectedVariant?.platformId??null,eligibleForSubscription:this.eligibleForSubscription}),this.dispatchVariantChanged(),variantInput.addEventListener("change",e=>{this.updateSelectedVariant(e.target.value),this.checkVariantEligibility(this.selectedVariant),this.successMode="added",this.showSuccessScreen=this.isVariantInUpcomingSubscription(this.selectedVariant),this.submitError=null,this.requestUpdate(),this.dispatchVariantChanged(),this.debug&&console.log("[Skio AddToSub] bindFormEvents:variant-change",{variantInputValue:e.target.value,selectedVariant:this.selectedVariant?.platformId??null,eligibleForSubscription:this.eligibleForSubscription})})}dispatchVariantChanged(){this.dispatchEvent(new CustomEvent("skio-component:variant-changed",{detail:{renderState:this.renderState,selectedVariant:this.selectedVariant??null,eligibleForSubscription:this.eligibleForSubscription,upcomingSubscription:this.upcomingSubscription??null}}))}getVariantIdInput(){const buyboxSelect=(this.closest(".js-buybox")||document).querySelector(".js__buybox-variant-select-element")||document.querySelector(".js__buybox-variant-select-element");if(buyboxSelect)return buyboxSelect;const wrapper=this.closest("[data-product-form-wrapper]"),inColumn=wrapper?.querySelector('[data-product-form] input[name="id"]')||wrapper?.querySelector('input[name="id"]');return inColumn||document.querySelector('[data-product-form] input[name="id"]')||document.querySelector('form[action*="/cart/add"] input[name="id"]')||document.querySelector("[name=id]")}updateSelectedVariant(variantId){const id=variantId!=null?String(variantId).trim():"",variants=this.product?.ProductVariants;if(!variants?.length){this.selectedVariant=void 0;return}if(!id){this.selectedVariant=variants[0];return}this.selectedVariant=variants.find(v=>v.platformId?.includes(id)||v.platformId?.endsWith(id)||String(v.platformId)===id)||variants[0]}checkVariantEligibility(variant){if(!variant)return;const groups=variant.SellingPlanGroupResources,groupCount=Array.isArray(groups)?groups.length:0;
454this.eligibleForOneTimeUpsell=!!(variant.enableOneTimeUpsell&&this.product?.canUpsell),this.eligibleForSubscription=groupCount>0,this.eligibleForOneTimeUpsell||(this.selectedOption="subscription")}isPrepaidSubscription(subscription){return SkioAddToSubscription.isPrepaidSubscription(subscription)}formatDate(timestamp){const date=new Date(timestamp);return`${date.toLocaleString("en-US",{month:"short"})} ${date.getDate()}, ${date.getFullYear()}`}formatMoney(amount){return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD"}).format(amount)}applyPercentageOff(amount,percentageOff){const cents=Math.round(amount*100);return Math.round(cents*(100-percentageOff)/100)/100}getOriginalPrice(){return this.selectedVariant.compareAtPrice?this.formatMoney(this.selectedVariant.compareAtPrice):this.formatMoney(this.selectedVariant.price)}cadenceInDays(deliveryPolicy){const days={DAY:1,WEEK:7,MONTH:30,YEAR:365}[deliveryPolicy?.interval];return days?days*deliveryPolicy.intervalCount:null}getSubscriptionPrice(){const subscriptionPolicy=this.upcomingSubscription.DeliveryPolicy,subscriptionCadence
454=this.cadenceInDays(subscriptionPolicy),plans=this.selectedVariant.SellingPlanGroupResources.flatMap(resource=>resource.SellingPlanGroup.SellingPlans),sellingPlan=plans.find(plan=>plan.DeliveryPolicy.interval===subscriptionPolicy.interval&&plan.DeliveryPolicy.intervalCount===subscriptionPolicy.intervalCount)||plans.find(plan=>subscriptionCadence!==null&&this.cadenceInDays(plan.DeliveryPolicy)===subscriptionCadence);if(!sellingPlan)return{discount:null,price:this.formatMoney(this.selectedVariant.price)};this.selectedSellingPlan=sellingPlan;const{percentageOff,fixedPrice}=sellingPlan.PricingPolicies[0];return percentageOff?{discount:`${percentageOff}%`,price:this.formatMoney(this.applyPercentageOff(this.selectedVariant.price,percentageOff))}:{discount:this.formatMoney(fixedPrice),price:this.formatMoney(this.selectedVariant.price-fixedPrice)}}getOneTimePrice(){const pct=this.product.oneTimeUpsellPercentageOff;if(!pct)return{discount:null,price:this.formatMoney(this.selectedVariant.price)};const percentageOff=pct*100;return{discount:`${percentageOff}%`,price:this.formatMoney(this.applyPercentageOff(this.selectedVariant.price,percentageOff))}}openModal(){const dialog=this.shadowRoot.querySelector("dialog.action-dialog");dialog&&(dialog.showModal(),dialog.addEventListener("click",this._handleActionBackdropClick),document.body.classList.add("skio-modal-open"))}closeModal(){const dialog=this.shadowRoot.querySelector("dialog.action-dialog");dialog&&(dialog.close(),dialog.removeEventListener("click",this._handleActionBackdropClick),document.body.classList.remove("skio-modal-open"))}handleBackdropClick(e){e.target.tagName==="DIALOG"&&this.closeModal()}disconnectedCallback(){super.disconnectedCallback(),document.body.classList.remove("skio-modal-open")}async addToSubscription(){this.submitError=null,this.isSubmitting=!0;const mutation=` 455 mutation storefrontEditSubscriptionLines($input: EditSubscriptionLinesInput!) { 456 editSubscriptionLines(input: $input) { 457 subscriptionId 458 } 459 } 460 `,variables={input:{subscriptionId:this.upcomingSubscription.id,subscriptionLinesToCreate:[{customAttributes:[{key:"add-to-subscription-component",value:"true"}],productVariantPlatformId:this.selectedVariant.platformId,quantity:1,isOneTimeUpsell:this.selectedOption==="one-time"}]}};try{if(await window.Skio.graphQLRequest({query:mutation,variables}),this.closeModal(),this.showSuccessScreen=!0,this.successMode="added",this.upcomingSubscription){const lines=this.upcomingSubscription.SubscriptionLines||[];lines.some(line=>String(line?.ProductVariant?.platformId)===String(this.selectedVariant.platformId))||(this.upcomingSubscription={...this.upcomingSubscription,SubscriptionLines:[...lines,{ProductVariant:{platformId:this.selectedVariant.platformId}}]})}this.dispatchEvent(new CustomEvent("skio:added",{detail:{subscription:this.upcomingSubscription}})),this.notifySubscriptionContext(),this.dispatchVariantChanged()}catch(error){console.error("Error adding to subscription:",error),this.submitError="Error adding to subscription. Please try again."}finally{this.isSubmitting=!1}}async reactivate(){this.submitError=null,this.isSubmitting=!0;const isPaused=this.inactiveSubscription.status==="PAUSED",mutation=isPaused?`mutation storefrontUnpauseSubscription($input: unpauseSubscriptionInput!) { 461 unpauseSubscription(input: $input) { ok } 462 }`:`mutation storefrontReactivateSubscription($input: ReactivateSubscriptionInput!) { 463 reactivateSubscription(input: $input) { ok } 464 }`,variables={input:{subscriptionId:this.inactiveSubscription.id,...isPaused?{}:{newBillingDate:new Date(Date.now()+864e5).toISOString()}}};try{const response=await window.Skio.graphQLRequest({query:mutation,variables});if(response.errors?.length)throw new Error(response.errors[0].message);const reactivated=await this.fetchSubscription("ACTIVE");if(!reactivated)throw new Error("Subscription was reactivated but could not be loaded.");this.upcomingSubscription=reactivated,this.inactiveSubscription=null,this.product||(this.product=await this.fetchProduct());const variantInput=this.getVariantIdInput();this.updateSelectedVariant(variantInput?.value),this.selectedVariant&&this.checkVariantEligibility(this.selectedVariant),this.selectedVariant&&this.eligibleForSubscription&&!this.isVariantInUpcomingSubscription(this.selectedVariant)?await this.addToSubscription():(this.successMode="reactivated",this.successWasPaused=isPaused,this.closeModal(),this.showSuccessScreen=!0),this.notifySubscriptionContext(),this.dispatchVariantChanged()}catch(error){console.error("Error reactivating subscription:",error),this.submitError="Something went wrong. Please try again."}finally{this.isSubmitting=!1}}
464render(){if(this.isPreviewMode)return this.renderDebugMode();if(this.showSuccessScreen)return this.renderSuccessScreen();if(this.ready&&!this.isAuthenticated)return this.renderSignInPrompt();if(!this.mode)return null;if(this.mode==="add")return this.renderAddMode();if(this.mode==="failed")return this.renderPaymentFailedPrompt();if(this.mode==="reactivate")return this.renderReactivateMode()}renderAddMode(){return html` 465 <div class="callout"> 466 <h3>${"Add to Upcoming Subscription"}</h3> 467 <small>${"You can easily add this item to your upcoming subscription order."}</small> 468 <p> 469 Next order on: 470 <b>${this.formatDate(this.upcomingSubscription.nextBillingDate)}</b> 471 </p> 472 <button class="callout-button" @click=${()=>this.openModal()}> 473 ${"Add to next order and save"} 474 </button> 475 </div> 476 477 <dialog class="action-dialog"> 478 <div class="modal-content"> 479 <div class="modal-header"> 480 <h2 class="modal-title"> 481 Add to Subscription 482 <span class="date-badge" 483 >${this.formatDate(this.upcomingSubscription.nextBillingDate)}</span 484 > 485 </h2> 486 <button class="close-button" @click=${this.closeModal}> 487 × 488 </button> 489 </div> 490 <form> 491 ${this.submitError?html`<div class="error-message">${this.submitError}</div>`:null} 492 ${this.eligibleForOneTimeUpsell?this.renderOneTimeOption():null} 493 ${this.eligibleForSubscription?this.renderSubscriptionOption():null} 494 </form> 495 <div class="modal-policy"> 496 ${this.selectedOption==="subscription"?html`<small 497 >By clicking Add to Order, you're adding this item to your 498 subscription. You'll be charged automatically at the listed 499 price and frequency until canceled.</small 500 >`:html`<small 501 >By selecting Add to Order, you agree to a one-time charge for 502 this item, which will be added to your next subscription 503 shipment. This item will not renew or be billed again.</small 504 >`} 505 </div> 506 <div class="modal-footer"> 507 <button 508 class="primary-button" 509 @click=${this.addToSubscription} 510 ?disabled=${this.isSubmitting} 511 > 512 ${this.isSubmitting?html`<span class="loading-spinner"></span>`:"Add to Order"} 513 </button> 514 <button 515 class="secondary-button" 516 @click=${this.closeModal} 517 type="button" 518 ?disabled=${this.isSubmitting} 519 > 520 Cancel 521 </button> 522 </div> 523 </div> 524 </dialog> 525 `}renderReactivateMode(){const isPaused=this.inactiveSubscription.status==="PAUSED",title=isPaused?"Resume Your Subscription":"Reactivate Your Subscription",description=isPaused?"Your subscription is currently paused. Resume it to receive your next order.":"Your subscription has been cancelled. Reactivate it to continue receiving your orders.",buttonLabel=isPaused?"Resume Subscription":"Reactivate Subscription";return html` 526 <div class="callout"> 527 <h3>${title}</h3> 528 <small>${description}</small> 529 <button class="callout-button" @click=${()=>this.openModal()}> 530 ${buttonLabel} 531 </button> 532 </div> 533 534 <dialog class="action-dialog"> 535 <div class="modal-content"> 536 <div class="modal-header"> 537 <h2 class="modal-title">${title}</h2> 538 <button class="close-button" @click=${this.closeModal}> 539 × 540 </button> 541 </div> 542 <div class="modal-body"> 543 ${this.submitError?html`<div class="error-message">${this.submitError}</div>`:null} 544 <p> 545 ${isPaused?"Your subscription will be resumed and your next order will be scheduled automatically.":"Your subscription will be reactivated and your next order will be scheduled automatically."} 546 </p> 547 </div> 548 <div class="modal-footer"> 549 <button 550 class="primary-button" 551 @click=${this.reactivate} 552 ?disabled=${this.isSubmitting} 553 > 554 ${this.isSubmitting?html`<span class="loading-spinner"></span>`:buttonLabel} 555 </button> 556 <button 557 class="secondary-button" 558 @click=${this.closeModal} 559 type="button" 560 ?disabled=${this.isSubmitting} 561 > 562 Cancel 563 </button> 564 </div> 565 </div> 566 </dialog> 567 `}renderOneTimeOption(){const{discount,price}=this.getOneTimePrice(),isSelected=this.selectedOption==="one-time";return html` 568 <label 569 class="option-label ${isSelected?"selected":""}" 570 @click=${()=>this.selectedOption="one-time"} 571 > 572 <input 573 type="radio" 574 name="addType" 575 value="one-time" 576 .checked=${isSelected} 577 @change=${()=>this.selectedOption="one-time"} 578 /> 579 <div class="option-content">
580 <span class="option-title">${"Get One-Time"}</span> 581 </div> 582 ${discount?html`<span class="discount-badge">Save ${discount}</span>`:null} 583 <div class="price-container"> 584 ${this.getOriginalPrice()!==price?html`<span class="original-price" 585 >${this.getOriginalPrice()}</span 586 >`:null} 587 <span class="discounted-price">${price}</span> 588 </div> 589 </label> 590 `}renderSubscriptionOption(){const{discount,price}=this.getSubscriptionPrice(),isSelected=this.selectedOption==="subscription";return html` 591 <label 592 class="option-label ${isSelected?"selected":""}" 593 @click=${()=>this.selectedOption="subscription"} 594 > 595 <input 596 type="radio" 597 name="addType" 598 value="subscription" 599 .checked=${isSelected} 600 @change=${()=>this.selectedOption="subscription"} 601 /> 602 <div class="option-content"> 603 <span class="option-title">${"Subscribe"}</span> 604 <div class="delivery-policy"> 605 Every ${this.upcomingSubscription.DeliveryPolicy.intervalCount} 606 ${this.upcomingSubscription.DeliveryPolicy.interval.toLowerCase()} 607 </div> 608 </div> 609 ${discount?html`<span class="discount-badge">Save ${discount}</span>`:null} 610 <div class="price-container"> 611 ${this.getOriginalPrice()!==price?html`<span class="original-price" 612 >${this.getOriginalPrice()}</span 613 >`:null} 614 <span class="discounted-price">${price}</span> 615 </div> 616 </label> 617 `}renderSignInPrompt(){const{message,linkText,href}=this.signInPrompt;return html` 618 <div class="callout sign-in-callout"> 619 <p> 620 ${message} 621 <a class="sign-in-link" href=${href}>${linkText}</a> 622 </p> 623 </div> 624 `}renderPaymentFailedPrompt(){const{linkText,href,message}=this.paymentFailedPrompt;return html` 625 <div class="callout sign-in-callout"> 626 <p> 627 <a class="sign-in-link" href=${href}>${linkText}</a> 628 ${message} 629 </p> 630 </div> 631 `}renderSuccessScreen(){const isReactivate=this.successMode==="reactivated",isPaused=this.successWasPaused;return isReactivate?html` 632 <div class="success-screen"> 633 <h2 class="success-title"> 634 ${isPaused?"Subscription Resumed":"Subscription Reactivated"} 635 </h2> 636 <p class="success-message"> 637 Your subscription has been ${isPaused?"resumed":"reactivated"}. 638 Your next order will be processed on the next scheduled billing 639 date. 640 </p> 641 <a href="/a/account/subscriptions" class="success-link" 642 >View and manage my subscriptions -></a 643 > 644 </div> 645 `:html` 646 <div class="success-screen"> 647 <h2 class="success-title">Added to Your Next Order</h2> 648 <p class="success-message"> 649 This item will be included in your next subscription order shipment on 650 <span class="success-date" 651 >${this.formatDate(this.upcomingSubscription.nextBillingDate)}</span 652 > 653 </p> 654 <a href="/a/account/subscriptions" class="success-link" 655 >View and manage my subscriptions -></a 656 > 657 </div> 658 `}renderDebugMode(){const mockDate=new Date;mockDate.setDate(mockDate.getDate()+30);const formattedDate=this.formatDate(mockDate);return html` 659 <div class="setup-mode"> 660 <div class="setup-mode-label"> 661 Setup Mode â Skio Add to Subscription 662 </div> 663 <p class="setup-mode-note"> 664 UI preview with mock data. Functionality requires an authenticated 665 customer with an active or inactive subscription. 666 </p> 667 668 <div class="debug-mode-toggle"> 669 <button 670 class="${this.debugPreviewMode==="add"?"active":""}" 671 @click=${()=>this.debugPreviewMode="add"} 672 type="button" 673 > 674 Add Mode 675 </button> 676 <button 677 class="${this.debugPreviewMode==="reactivate"?"active":""}" 678 @click=${()=>this.debugPreviewMode="reactivate"} 679 type="button" 680 > 681 Reactivate Mode 682 </button> 683 </div> 684 685 ${this.debugPreviewMode==="add"?html` 686 <div class="callout" style="margin-bottom: 16px;"> 687 <h3>${"Add to Upcoming Subscription"}</h3> 688 <small>${"You can easily add this item to your upcoming subscription order."}</small> 689 <p>Next order on: <b>${formattedDate}</b></p> 690 <button class="callout-button" type="button"> 691 ${"Add to next order and save"} 692 </button> 693 </div> 694 695 <div class="debug-modal-preview"> 696 <div class="modal-content"> 697 <div class="modal-header"> 698 <h2 class="modal-title"> 699 Add to Subscription
700 <span class="date-badge">${formattedDate}</span> 701 </h2> 702 <button class="close-button" type="button">×</button> 703 </div> 704 <form> 705 <label class="option-label"> 706 <input type="radio" name="addType" value="one-time" /> 707 <div class="option-content"> 708 <span class="option-title">${"Get One-Time"}</span> 709 </div> 710 <span class="discount-badge">Save 10%</span> 711 <div class="price-container"> 712 <span class="original-price">$34.99</span> 713 <span class="discounted-price">$31.49</span> 714 </div> 715 </label> 716 <label class="option-label selected"> 717 <input 718 type="radio" 719 name="addType" 720 value="subscription" 721 checked 722 /> 723 <div class="option-content"> 724 <span class="option-title">${"Subscribe"}</span> 725 <div class="delivery-policy">Every 30 days</div> 726 </div> 727 <span class="discount-badge">Save 15%</span> 728 <div class="price-container"> 729 <span class="original-price">$34.99</span> 730 <span class="discounted-price">$29.74</span> 731 </div> 732 </label> 733 </form> 734 <div class="modal-policy"> 735 <small 736 >By clicking Add to Order, you're adding this item to your 737 subscription. You'll be charged automatically at the 738 listed price and frequency until canceled.</small 739 > 740 </div> 741 <div class="modal-footer"> 742 <button class="primary-button" type="button"> 743 Add to Order 744 </button> 745 <button class="secondary-button" type="button"> 746 Cancel 747 </button> 748 </div> 749 </div> 750 </div> 751 `:html` 752 <div class="callout" style="margin-bottom: 16px;"> 753 <h3>Resume Your Subscription</h3> 754 <small 755 >Your subscription is currently paused. Resume it to receive 756 your next order.</small 757 > 758 <button class="callout-button" type="button"> 759 Resume Subscription 760 </button> 761 </div> 762 763 <div class="debug-modal-preview"> 764 <div class="modal-content"> 765 <div class="modal-header"> 766 <h2 class="modal-title">Resume Your Subscription</h2> 767 <button class="close-button" type="button">×</button> 768 </div> 769 <div class="modal-body"> 770 <p> 771 Your subscription will be resumed and your next order will 772 be scheduled automatically. 773 </p> 774 </div> 775 <div class="modal-footer"> 776 <button class="primary-button" type="button"> 777 RESUME SUBSCRIPTION 778 </button> 779 <button class="secondary-button" type="button"> 780 Cancel 781 </button> 782 </div> 783 </div> 784 </div> 785 `} 786 </div> 787 `}static PROMPT_DEFAULTS={signInMessage:"Want to add this item to your upcoming subscription?",signInLinkText:"Sign in",paymentFailedLinkText:"Visit your account",paymentFailedMessage:"to update your credit card information."};static THEME_PROMPT_LINK_CLASS="font-semibold text-gray-900 underline decoration-1 underline-offset-2";static parseOptions(optionsJson){try{return JSON.parse(optionsJson||"{}")}catch{return{}}}static buildSignInUrl(options={}){const base=options.signInUrl||`${window.Shopify?.routes?.root||"/"}a/account/login`,returnUrl=encodeURIComponent(`${window.location.pathname}${window.location.search}`),separator=base.includes("?")?"&":"?";return`${base}${separator}return_url=${returnUrl}`}static getPrompt(state,options={}){const defaults=SkioAddToSubscription.PROMPT_DEFAULTS;if(state==="sign-in")return{message:options.signInMessage||defaults.sign
787InMessage,linkText:options.signInLinkText||defaults.signInLinkText,href:SkioAddToSubscription.buildSignInUrl(options)};if(state==="failed"){const root=window.Shopify?.routes?.root||"/";return{linkText:options.paymentFailedLinkText||defaults.paymentFailedLinkText,href:options.paymentFailedUrl||`${root}a/account/subscriptions`,message:options.paymentFailedMessage||defaults.paymentFailedMessage}}return null}static renderThemePrompt(container,state,options={}){if(!container)return;const prompt=SkioAddToSubscription.getPrompt(state,options);if(!prompt){container.classList.add("hidden"),container.innerHTML="";return}const linkClass=SkioAddToSubscription.THEME_PROMPT_LINK_CLASS;container.innerHTML=state==="failed"?`<a href="${prompt.href}" class="${linkClass}">${prompt.linkText}</a> ${prompt.message}`:`${prompt.message} <a href="${prompt.href}" class="${linkClass}">${prompt.linkText}</a>`,container.classList.remove("hidden")}static loadSdk(){return window.Skio?Promise.resolve():new Promise((resolve,reject)=>{const script=document.createElement("script");script.src="https://cdn.skio.com/scripts/shopify/sdk/skio-latest.js",script.onload=resolve,script.onerror=reject,document.head.appendChild(script)})}static isLikelyLoggedInShopifyCustomer(){return typeof window.__nuts_external_id__<"u"}static async waitForStorefrontUser(timeout=8e3){if(window.Skio?.storefrontUser?.id)return!0;if(!SkioAddToSubscription.isLikelyLoggedInShopifyCustomer())return!1;const started=Date.now();for(;Date.now()-started<timeout;){if(window.Skio?.storefrontUser?.id)return!0;await new Promise(resolve=>setTimeout(resolve,100))}return!!window.Skio?.storefrontUser?.id}static async fetchSubscription(status,{includeLines=!1}={}){const statusFilter=Array.isArray(status)?{_in:status}:{_eq:status},isActive=status==="ACTIVE",storefrontUserId=window.Skio?.storefrontUser?.id,storefrontUserFilter=storefrontUserId?` 788 storefrontUserId: { _eq: $storefrontUserId }`:"",query=` 789 query storefrontGetSubscription($storefrontUserId: uuid) { 790 Subscriptions(where: { 791 status: ${JSON.stringify(statusFilter).replace(/"([^"]+)":/g,"$1:")} 792 ${storefrontUserFilter} 793 }, order_by: { 794 ${isActive?"nextBillingDate":"updatedAt"}: asc 795 }, limit: 1) { 796 id 797 status 798 nextBillingDate 799 ${isActive&&includeLines?` 800 SubscriptionLines( 801 where: { removedAt: { _is_null: true } } 802 order_by: { createdAt: asc } 803 ) { 804 ProductVariant { 805 platformId 806 } 807 OriginalSellingPlan { 808 PricingPolicies { 809 percentageOff 810 fixedPrice 811 } 812 } 813 }`:""} 814 DeliveryPolicy { 815 interval 816 intervalCount 817 } 818 BillingPolicy { 819 interval 820 intervalCount 821 } 822 } 823 } 824 `,variables=storefrontUserId?{storefrontUserId}:{};try{const subscription=(await window.Skio.graphQLRequest({query,variables}))?.data?.Subscriptions?.[0];return!subscription||isActive&&SkioAddToSubscription.isPrepaidSubscription(subscription)?null:subscription}catch(error){return console.error("Error fetching subscription:",error),null}}static isPrepaidSubscription({DeliveryPolicy,BillingPolicy}){return DeliveryPolicy.interval!==BillingPolicy.interval||DeliveryPolicy.intervalCount!==BillingPolicy.intervalCount}static async resolveSubscriptionContext(){if(await SkioAddToSubscription.loadSdk(),await SkioAddToSubscription.waitForStorefrontUser(),!window.Skio?.storefrontUser?.id)return{renderState:"sign-in",upcomingSubscription:null};const upcomingSubscription=await SkioAddToSubscription.fetchSubscription("ACTIVE",{includeLines:!0});return upcomingSubscription?{renderState:"add",upcomingSubscription}:await SkioAddToSubscription.fetchSubscription(["CANCELLED","PAUSED"])?{renderState:"reactivate",upcomingSubscription:null}:await SkioAddToSubscription.fetchSubscription("FAILED")?{renderState:"failed",upcomingSubscription:null}:{renderState:null,upcomingSubscription:null}}static async addVariantToSubscription({subscriptionId,variantId,quantity=1}){await SkioAddToSubscription.loadSdk();const mutation=`
825 mutation storefrontEditSubscriptionLines($input: EditSubscriptionLinesInput!) { 826 editSubscriptionLines(input: $input) { 827 subscriptionId 828 } 829 } 830 `,variables={input:{subscriptionId,subscriptionLinesToCreate:[{customAttributes:[{key:"add-to-subscription-component",value:"true"},{key:"added-from-cart",value:"true"}],productVariantPlatformId:`gid://shopify/ProductVariant/${variantId}`,quantity,isOneTimeUpsell:!1}]}},response=await window.Skio.graphQLRequest({query:mutation,variables});if(response?.errors?.length)throw new Error(response.errors[0].message)}}customElements.define("skio-add-to-subscription",SkioAddToSubscription); 831//# sourceMappingURL=/cdn/shop/t/3/assets/skio-add-to-subscription.js.map?v=152525021207288618591786133901
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.