JezK
Edit File: sc-subscription-switch.js.map
{"file":"sc-subscription-switch.js","mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,MAAM,uBAAuB,GAAG,4PAA4P,CAAC;AAC7R,mCAAe,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCCiEf,UAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+OAsHZ,UAAE,gLAKF,UAAE,+KAKF,UAAE,kLAKF,UAAE,oLAKF,UAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oIA0EU,UAAE,qYASJ,UAAE;;;;;;;uBAcV,UAAE;;;uBAEF,UAAE;;;;;uBAMF,UAAE;;;uBAEF,UAAE;;;eAGN,UAAE;;;;;;;;;;;;;;;;;;;gEAsBF,UAAE;;oEAMuC,UAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","names":[],"sources":["src/components/controllers/dashboard/subscription-switch/sc-subscription-switch.scss?tag=sc-subscription-switch&encapsulation=shadow","src/components/controllers/dashboard/subscription-switch/sc-subscription-switch.tsx"],"sourcesContent":[":host {\n display: block;\n position: relative;\n}\n\n[hidden] {\n display: none !important;\n}\n\n\n.subscriptions-switch {\n display: grid;\n gap: 0.5em;\n\n &__switcher {\n background: rgba(0,0,0,0.035);\n padding: 2px;\n line-height: 1;\n border-radius: var(--sc-border-radius-small);\n }\n}\n\n","import { Component, Element, Fragment, h, Prop, State, Watch } from '@stencil/core';\nimport { __ } from '@wordpress/i18n';\nimport { addQueryArgs } from '@wordpress/url';\n\nimport apiFetch from '../../../../functions/fetch';\nimport { onFirstVisible } from '../../../../functions/lazy';\nimport { intervalString } from '../../../../functions/price';\nimport { Price, Product, ProductGroup, Subscription } from '../../../../types';\n\n@Component({\n tag: 'sc-subscription-switch',\n styleUrl: 'sc-subscription-switch.scss',\n shadow: true,\n})\nexport class ScSubscriptionSwitch {\n @Element() el: HTMLScSubscriptionsListElement;\n /** Customer id to fetch subscriptions */\n @Prop() query: object;\n @Prop() heading: string;\n @Prop() productGroupId: ProductGroup;\n @Prop() productId: string;\n @Prop() subscription: Subscription;\n @Prop() filterAbove: number = 4;\n @Prop() successUrl: string = window.location.href;\n\n /** The currently selected price. */\n @State() selectedPrice: Price;\n\n /** Holds the products */\n @State() products: Array<Product> = [];\n\n /** Holds the prices */\n @State() prices: Array<Price>;\n\n /** Filter state */\n @State() filter: 'month' | 'week' | 'year' | 'never' | 'split' = 'month';\n\n @State() hasFilters: {\n split: boolean;\n month: boolean;\n week: boolean;\n year: boolean;\n never: boolean;\n };\n\n @State() showFilters: boolean;\n\n /** Loading state */\n @State() loading: boolean;\n\n /** Busy state */\n @State() busy: boolean;\n\n /** Error message */\n @State() error: string;\n\n componentWillLoad() {\n onFirstVisible(this.el, async () => {\n try {\n this.loading = true;\n await Promise.all([this.getGroup(), this.getProductPrices()]);\n } catch (e) {\n console.error(e);\n if (e?.message) {\n this.error = e.message;\n } else {\n this.error = __('Something went wrong', 'surecart');\n }\n } finally {\n this.loading = false;\n }\n });\n this.handleSubscriptionChange();\n }\n\n @Watch('products')\n handleProductsChange() {\n this.prices = this.products\n .map(product => (product as Product)?.prices?.data)\n .flat()\n .filter((v, i, a) => a.findIndex(t => t.id === v.id) === i) // remove duplicates.\n .filter(price => !price?.archived) // remove archived\n .filter(price => price.portal_subscription_update_enabled); // only show prices that can be upgraded to.\n\n this.showFilters = this.prices?.length > this.filterAbove;\n }\n\n @Watch('prices')\n handlePricesChange(val, prev) {\n if (!prev?.length && val?.length) {\n this.selectedPrice = val.find(price => price.id === (this.subscription?.price as Price)?.id);\n }\n\n this.hasFilters = {\n ...this.hasFilters,\n split: this.prices.some(price => !!price?.recurring_period_count && !price?.archived),\n month: this.prices.some(price => price.recurring_interval === 'month' && !price?.recurring_period_count && !price?.archived),\n year: this.prices.some(price => price.recurring_interval === 'year' && !price?.recurring_period_count && !price?.archived),\n never: this.prices.some(price => (price.recurring_interval === 'never' || !price.recurring_interval) && !price?.archived),\n };\n }\n\n @Watch('subscription')\n handleSubscriptionChange() {\n this.filter = (this.subscription?.price as Price)?.recurring_interval || 'month';\n }\n\n hasMultipleFilters() {\n return Object.values(this.hasFilters || {}).filter(v => !!v).length > 1;\n }\n\n /** Get all subscriptions */\n async getGroup() {\n if (!this.productGroupId) return;\n const products = (await await apiFetch({\n path: addQueryArgs(`surecart/v1/products/`, {\n product_group_ids: [this.productGroupId],\n expand: ['prices'],\n ...this.query,\n }),\n })) as Product[];\n this.products = [...this.products, ...products];\n }\n\n /** Get the product's prices. */\n async getProductPrices() {\n if (!this.productId) return;\n const product = (await await apiFetch({\n path: addQueryArgs(`surecart/v1/products/${this.productId}`, {\n expand: ['prices'],\n }),\n })) as Product;\n this.products = [...this.products, ...[product]];\n }\n\n async handleSubmit(e) {\n const { plan } = await e.target.getFormJson();\n const price = this.prices.find(p => p.id === plan);\n const currentPlan = this.subscription?.price as Price;\n if (price?.id === currentPlan.id && !price?.ad_hoc && !this.subscription?.variant_options?.length) return;\n\n // confirm product variation.\n if (this.subscription?.variant_options?.length) {\n this.busy = true;\n return window.location.assign(\n addQueryArgs(this.successUrl, {\n action: 'confirm_variation',\n price_id: plan,\n ...(this.subscription?.live_mode === false ? { live_mode: false } : {}),\n }),\n );\n }\n\n // confirm ad_hoc amount.\n if (price?.ad_hoc) {\n this.busy = true;\n return window.location.assign(\n addQueryArgs(this.successUrl, {\n action: 'confirm_amount',\n price_id: plan,\n ...(this.subscription?.live_mode === false ? { live_mode: false } : {}),\n }),\n );\n }\n\n // confirm plan.\n this.busy = true;\n window.location.assign(\n addQueryArgs(this.successUrl, {\n action: 'confirm',\n price_id: plan,\n ...(this.subscription?.live_mode === false ? { live_mode: false } : {}),\n }),\n );\n }\n\n renderSwitcher() { \n if (!this.hasMultipleFilters()) return;\n if (!this.showFilters) return;\n\n return (\n <sc-flex slot=\"end\" class=\"subscriptions-switch__switcher\">\n {this.hasFilters.month && (\n <sc-button onClick={() => (this.filter = 'month')} size=\"small\" type={this.filter === 'month' ? 'default' : 'text'}>\n {__('Monthly', 'surecart')}\n </sc-button>\n )}\n {this.hasFilters.week && (\n <sc-button onClick={() => (this.filter = 'week')} size=\"small\" type={this.filter === 'week' ? 'default' : 'text'}>\n {__('Weekly', 'surecart')}\n </sc-button>\n )}\n {this.hasFilters.year && (\n <sc-button onClick={() => (this.filter = 'year')} size=\"small\" type={this.filter === 'year' ? 'default' : 'text'}>\n {__('Yearly', 'surecart')}\n </sc-button>\n )}\n {this.hasFilters.never && (\n <sc-button onClick={() => (this.filter = 'never')} size=\"small\" type={this.filter === 'never' ? 'default' : 'text'}>\n {__('Lifetime', 'surecart')}\n </sc-button>\n )}\n {this.hasFilters.split && (\n <sc-button onClick={() => (this.filter = 'split')} size=\"small\" type={this.filter === 'split' ? 'default' : 'text'}>\n {__('Payment Plan', 'surecart')}\n </sc-button>\n )}\n </sc-flex>\n );\n }\n\n renderLoading() {\n return (\n <sc-choice name=\"loading\" disabled>\n <sc-skeleton style={{ width: '60px', display: 'inline-block' }}></sc-skeleton>\n <sc-skeleton style={{ width: '80px', display: 'inline-block' }} slot=\"price\"></sc-skeleton>\n <sc-skeleton style={{ width: '120px', display: 'inline-block' }} slot=\"description\"></sc-skeleton>\n </sc-choice>\n );\n }\n\n /** Is the price hidden or not */\n isHidden(price: Price) {\n // don't hide if no filters or has no multiple filters available.\n if (!this.showFilters || !this.hasMultipleFilters()) return false;\n\n // hide if the filter does not match the recurring interval.\n let hidden = this.filter !== price.recurring_interval;\n\n // if filter is never, show prices with non-recurring interval.\n if (this.filter === 'never' && !price?.recurring_interval) {\n hidden = false;\n }\n\n // if filter is split, show prices with a recurring_period_count.\n if (this.filter === 'split' && price?.recurring_period_count) {\n hidden = false;\n }\n\n return hidden;\n }\n\n renderContent() {\n if (this.loading) {\n return this.renderLoading();\n }\n\n return (\n <sc-choices required>\n <div>\n {(this.prices || [])\n .filter(price => !price.archived)\n .filter(price => price?.currency === this.subscription?.currency)\n .sort((a, b) => a.amount - b.amount)\n .map(price => {\n const currentPlan = (this.subscription?.price as Price)?.id === price?.id;\n const product = this.products.find(product => product.id === price?.product);\n\n return (\n <sc-choice\n key={price?.id}\n checked={currentPlan}\n name=\"plan\"\n value={price?.id}\n hidden={this.isHidden(price)}\n onScChange={e => {\n if (e.detail) {\n this.selectedPrice = this.prices.find(p => p.id === price?.id);\n }\n }}\n >\n <div>\n <strong>\n {product?.name} {price?.name && <Fragment> — {price?.name}</Fragment>}\n </strong>\n </div>\n <div slot=\"description\">\n {price?.ad_hoc ? (\n `${__('Custom amount', 'surecart')} ${intervalString(price)}`\n ) : (\n <Fragment>\n <sc-format-number type=\"currency\" currency={price?.currency || 'usd'} value={price?.amount}></sc-format-number> {intervalString(price, { showOnce: true })}\n </Fragment>\n )}\n </div>\n {currentPlan && (\n <sc-tag type=\"warning\" slot=\"price\">\n {__('Current Plan', 'surecart')}\n </sc-tag>\n )}\n </sc-choice>\n );\n })}\n </div>\n </sc-choices>\n );\n }\n\n buttonText() {\n if (this.subscription?.variant_options?.length) {\n if (this.selectedPrice?.id === (this.subscription?.price as Price)?.id) {\n return __('Update Options', 'surecart');\n } else {\n return __('Choose Options', 'surecart');\n }\n }\n\n if (this.selectedPrice?.ad_hoc) {\n if (this.selectedPrice?.id === (this.subscription?.price as Price)?.id) {\n return __('Update Amount', 'surecart');\n } else {\n return __('Choose Amount', 'surecart');\n }\n }\n return __('Next', 'surecart');\n }\n\n buttonDisabled() {\n if (this.subscription?.variant_options) {\n return false;\n }\n return (this.subscription?.price as Price)?.id === this.selectedPrice?.id && !this.selectedPrice?.ad_hoc;\n }\n\n render() {\n // we are not loading and we don't have enough prices to switch.\n if (!this.loading && this.prices?.length < 2) {\n if (!this.prices?.[0]?.ad_hoc && !this.subscription?.variant_options?.length) {\n return null;\n }\n }\n\n // subscription is a payment plan.\n if (this.subscription?.finite) {\n return (\n <sc-alert type=\"info\" open>\n {__('To make changes to your payment plan, please contact us.', 'surecart')}\n </sc-alert>\n );\n }\n\n return (\n <sc-dashboard-module heading={this.heading || __('Update Plan', 'surecart')} class=\"subscription-switch\" error={this.error}>\n <span slot=\"end\">{this.renderSwitcher()}</span>\n <sc-form class=\"subscriptions-switch\" onScFormSubmit={e => this.handleSubmit(e)}>\n {this.renderContent()}\n\n <sc-button type=\"primary\" full submit loading={this.loading || this.busy} disabled={this.buttonDisabled()}>\n {this.buttonText()} <sc-icon name=\"arrow-right\" slot=\"suffix\"></sc-icon>\n </sc-button>\n\n {this.busy && <sc-block-ui style={{ zIndex: '9' }}></sc-block-ui>}\n </sc-form>\n </sc-dashboard-module>\n );\n }\n}\n"],"version":3}