{"version":3,"sources":["libs/galaxy/frequency/src/interface.ts","libs/galaxy/frequency/src/frequency.pipe.ts","libs/galaxy/frequency/src/frequency.component.ts","libs/galaxy/frequency/src/frequency.component.html","libs/galaxy/frequency/src/frequency.module.ts","libs/galaxy/frequency/public_api.ts","libs/galaxy/frequency/index.ts","libs/country-state/src/lib/interfaces.ts","libs/country-state/src/lib/country-state.service.ts","libs/country-state/src/lib/country-state.module.ts","libs/country-state/src/lib/index.ts","libs/country-state/src/index.ts","node_modules/ng-recaptcha/fesm2020/ng-recaptcha.mjs","libs/billing-ui/src/lib/billing-erp-url.service.ts","libs/billing-ui/src/lib/discount/add-discount-modal/retail-discount-dialog.component.ts","libs/billing-ui/src/lib/discount/add-discount-modal/retail-discount-dialog.component.html","libs/billing-ui/src/lib/i18n/assets/en_devel.json","libs/billing-ui/src/lib/i18n/billing-ui-i18n.module.ts","libs/billing-ui/src/lib/stripe-url.service.ts","libs/billing-ui/src/lib/stripe.service.ts","libs/billing-ui/src/lib/utils/frequency.ts","libs/billing-ui/src/lib/utils/price-display.ts","libs/billing-ui/src/lib/simple-price-display/simple-price-display.component.ts","libs/billing-ui/src/lib/simple-price-display/simple-price-display.component.html","libs/billing-ui/src/lib/price-display/price-display.component.ts","libs/billing-ui/src/lib/price-display/price-display.component.html","libs/billing-ui/src/lib/shared/pipes.ts","libs/billing-ui/src/lib/simple-price-display/discount-display/format-date.pipe.ts","libs/billing-ui/src/lib/simple-price-display/discount-display/discount-display.component.ts","libs/billing-ui/src/lib/simple-price-display/discount-display/discount-display.component.html","libs/billing-ui/src/lib/utils/pipes/currency-without-usd.pipe.ts","libs/billing-ui/src/lib/simple-price-display/stairstep-price-display/stairstep-dialog/stairstep-dialog.component.ts","libs/billing-ui/src/lib/simple-price-display/stairstep-price-display/stairstep-dialog/stairstep-dialog.component.html","libs/billing-ui/src/lib/utils/pipes/resolve-currency-code.pipe.ts","libs/billing-ui/src/lib/utils/pipes/price-in-dollars.pipe.ts","libs/billing-ui/src/lib/simple-price-display/stairstep-price-display/stairstep-price-display.component.ts","libs/billing-ui/src/lib/simple-price-display/stairstep-price-display/stairstep-price-display.component.html","libs/billing-ui/src/lib/variable-price-input/variable-price-input.component.ts","libs/billing-ui/src/lib/variable-price-input/variable-price-input.component.html","libs/billing-ui/src/lib/billing-ui.module.ts","libs/billing-ui/src/lib/index.ts","libs/billing-ui/src/index.ts"],"sourcesContent":["export enum Frequency {\n ONE_TIME = 'One Time',\n YEARLY = 'Yearly',\n MONTHLY = 'Monthly',\n WEEKLY = 'Weekly',\n DAILY = 'Daily',\n QUARTERLY = 'Quarterly',\n BI_WEEKLY = 'Bi-Weekly',\n}\n\nexport enum Form {\n SHORT = 'short',\n LONG = 'long',\n VERBOSE = 'verbose',\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { Frequency } from './interface';\n\nconst BaseTranslationKey = 'GALAXY.FREQUENCY.';\n\nconst ShortFrequencyTranslationKey = BaseTranslationKey + 'SHORT.';\nconst LongFrequencyTranslationKey = BaseTranslationKey + 'LONG.';\nconst VerboseFrequencyTranslationKey = BaseTranslationKey + 'VERBOSE.';\n\nconst ShortFreqString: Record = {\n [Frequency.MONTHLY]: ShortFrequencyTranslationKey + 'MONTHLY',\n [Frequency.YEARLY]: ShortFrequencyTranslationKey + 'YEARLY',\n [Frequency.WEEKLY]: ShortFrequencyTranslationKey + 'WEEKLY',\n [Frequency.DAILY]: ShortFrequencyTranslationKey + 'DAILY',\n [Frequency.QUARTERLY]: ShortFrequencyTranslationKey + 'QUARTERLY',\n [Frequency.BI_WEEKLY]: ShortFrequencyTranslationKey + 'BI_WEEKLY',\n};\n\nconst LongFreqString: Record = {\n [Frequency.MONTHLY]: LongFrequencyTranslationKey + 'MONTHLY',\n [Frequency.YEARLY]: LongFrequencyTranslationKey + 'YEARLY',\n [Frequency.WEEKLY]: LongFrequencyTranslationKey + 'WEEKLY',\n [Frequency.DAILY]: LongFrequencyTranslationKey + 'DAILY',\n [Frequency.QUARTERLY]: LongFrequencyTranslationKey + 'QUARTERLY',\n [Frequency.BI_WEEKLY]: LongFrequencyTranslationKey + 'BI_WEEKLY',\n};\n\nconst VerboseFreqString: Record = {\n [Frequency.MONTHLY]: VerboseFrequencyTranslationKey + 'MONTHLY',\n [Frequency.YEARLY]: VerboseFrequencyTranslationKey + 'YEARLY',\n [Frequency.WEEKLY]: VerboseFrequencyTranslationKey + 'WEEKLY',\n [Frequency.DAILY]: VerboseFrequencyTranslationKey + 'DAILY',\n [Frequency.QUARTERLY]: VerboseFrequencyTranslationKey + 'QUARTERLY',\n [Frequency.BI_WEEKLY]: VerboseFrequencyTranslationKey + 'BI_WEEKLY',\n};\n\n@Pipe({\n name: 'glxyFrequency',\n standalone: false,\n})\nexport class GalaxyFrequencyPipe implements PipeTransform {\n transform(frequency: Frequency = Frequency.ONE_TIME, form: 'short' | 'long' | 'verbose' = 'short'): string {\n let freq = '';\n if (form.toLowerCase() === 'long') {\n freq = this.getLongFrequency(frequency);\n } else if (form.toLowerCase() === 'verbose') {\n freq = this.getVerboseFrequency(frequency);\n } else {\n freq = this.getShortFrequency(frequency);\n }\n\n if (freq !== '') {\n return `${freq}`;\n }\n return '';\n }\n\n getShortFrequency(frequency: Frequency): string {\n return ShortFreqString[frequency] || '';\n }\n\n getLongFrequency(frequency: Frequency): string {\n return LongFreqString[frequency] || '';\n }\n getVerboseFrequency(frequency: Frequency): string {\n return VerboseFreqString[frequency] || '';\n }\n}\n","import { booleanAttribute, Component, HostBinding, Input } from '@angular/core';\nimport { Form, Frequency } from './interface';\n\n@Component({\n selector: 'glxy-frequency',\n templateUrl: './frequency.component.html',\n styleUrls: ['./frequency.component.scss'],\n standalone: false,\n})\nexport class GalaxyFrequencyComponent {\n @HostBinding('class') class = 'glxy-frequency';\n @Input({ required: true }) value = '';\n /**\n * If you want the currency code to match the text sizing of the frequency, provide it separately from the value.\n */\n @Input() currencyCode?: string;\n @Input() frequency: Frequency = Frequency.ONE_TIME;\n @Input() form: Form = Form.SHORT;\n @Input() size: 'regular' | 'large' = 'regular';\n @Input() align: 'end' | 'start' = 'end';\n @Input({ transform: booleanAttribute }) allowCustomStyling = false;\n Frequency = Frequency;\n Form = Form;\n}\n","
\n \n {{ value }}\n
\n
\n @if (!!currencyCode) {\n  {{ currencyCode }}\n }\n @if (form.toLowerCase() === Form.VERBOSE) {\n  \n }\n {{ frequency | glxyFrequency: form | translate }}\n
\n\n","import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { GalaxyI18NModule } from '@vendasta/galaxy/i18n';\n\nimport { GalaxyFrequencyComponent } from './frequency.component';\nexport { GalaxyFrequencyComponent } from './frequency.component';\nimport { GalaxyFrequencyPipe } from './frequency.pipe';\nexport { GalaxyFrequencyPipe } from './frequency.pipe';\n\nexport const MODULE_IMPORTS = [CommonModule, GalaxyI18NModule, TranslateModule];\n\nexport const MODULE_DECLARATIONS = [GalaxyFrequencyComponent, GalaxyFrequencyPipe];\n\n@NgModule({\n declarations: MODULE_DECLARATIONS,\n exports: MODULE_DECLARATIONS,\n imports: MODULE_IMPORTS,\n})\nexport class GalaxyFrequencyModule {}\n","export * from './src/frequency.module';\nexport { Frequency } from './src/interface';\n","export * from './public_api';\n","import { InjectionToken } from '@angular/core';\nimport { Observable } from 'rxjs';\n\nexport const CountryStateServiceInterfaceToken: InjectionToken =\n new InjectionToken('CountryStateServiceInterface');\n\nexport interface Country {\n code: string; // ISO 3166 alpha-2 code\n name: string;\n}\n\nexport interface State {\n code: string;\n name: string;\n}\n\nexport interface CountryStateServiceInterface {\n getCountriesOptions(): Observable;\n getStatesOptions(countryCode: string): Observable;\n}\n","import { Inject, Injectable } from '@angular/core';\nimport { CountryStateServiceInterface, CountryStateServiceInterfaceToken, Country, State } from './interfaces';\nimport { Observable } from 'rxjs';\n\nexport const COUNTRIES_WITH_OPTIONAL_STATES: string[] = ['GB', 'FR', 'DE', 'MT', 'ZA', 'BS', 'BM', 'HK', 'PR'];\n\n@Injectable()\nexport class CountryStateService implements CountryStateServiceInterface {\n constructor(@Inject(CountryStateServiceInterfaceToken) private innerService: CountryStateServiceInterface) {}\n getCountriesOptions(): Observable {\n return this.innerService.getCountriesOptions();\n }\n\n getStatesOptions(countryCode: string): Observable {\n return this.innerService.getStatesOptions(countryCode);\n }\n}\n","import { NgModule } from '@angular/core';\n\nimport { CountryStateService } from './country-state.service';\n\n@NgModule({\n imports: [],\n providers: [CountryStateService],\n declarations: [],\n exports: [],\n})\nexport class CountryStateServiceModule {}\n","export { CountryStateServiceInterfaceToken, CountryStateServiceInterface, Country, State } from './interfaces';\nexport { CountryStateService, COUNTRIES_WITH_OPTIONAL_STATES } from './country-state.service';\nexport { CountryStateServiceModule } from './country-state.module';\n","export * from './lib/index';\n","import * as i0 from '@angular/core';\nimport { InjectionToken, PLATFORM_ID, Injectable, Inject, Optional, EventEmitter, Component, Input, HostBinding, Output, NgModule, forwardRef, Directive, HostListener } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { of, BehaviorSubject, Subject } from 'rxjs';\nimport { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';\nconst RECAPTCHA_LANGUAGE = new InjectionToken(\"recaptcha-language\");\nconst RECAPTCHA_BASE_URL = new InjectionToken(\"recaptcha-base-url\");\nconst RECAPTCHA_NONCE = new InjectionToken(\"recaptcha-nonce-tag\");\nconst RECAPTCHA_SETTINGS = new InjectionToken(\"recaptcha-settings\");\nconst RECAPTCHA_V3_SITE_KEY = new InjectionToken(\"recaptcha-v3-site-key\");\nfunction loadScript(renderMode, onLoaded, urlParams, url, nonce) {\n window.ng2recaptchaloaded = () => {\n onLoaded(grecaptcha);\n };\n const script = document.createElement(\"script\");\n script.innerHTML = \"\";\n const baseUrl = url || \"https://www.google.com/recaptcha/api.js\";\n script.src = `${baseUrl}?render=${renderMode}&onload=ng2recaptchaloaded${urlParams}`;\n if (nonce) {\n script.nonce = nonce;\n }\n script.async = true;\n script.defer = true;\n document.head.appendChild(script);\n}\nconst loader = {\n loadScript\n};\nlet RecaptchaLoaderService = /*#__PURE__*/(() => {\n class RecaptchaLoaderService {\n constructor(\n // eslint-disable-next-line @typescript-eslint/ban-types\n platformId, language, baseUrl, nonce, v3SiteKey) {\n this.platformId = platformId;\n this.language = language;\n this.baseUrl = baseUrl;\n this.nonce = nonce;\n this.v3SiteKey = v3SiteKey;\n this.init();\n this.ready = isPlatformBrowser(this.platformId) ? RecaptchaLoaderService.ready.asObservable() : of();\n }\n /** @internal */\n init() {\n if (RecaptchaLoaderService.ready) {\n return;\n }\n if (isPlatformBrowser(this.platformId)) {\n const subject = new BehaviorSubject(null);\n RecaptchaLoaderService.ready = subject;\n const langParam = this.language ? \"&hl=\" + this.language : \"\";\n const renderMode = this.v3SiteKey || \"explicit\";\n loader.loadScript(renderMode, grecaptcha => subject.next(grecaptcha), langParam, this.baseUrl, this.nonce);\n }\n }\n }\n /**\n * @internal\n * @nocollapse\n */\n RecaptchaLoaderService.ready = null;\n RecaptchaLoaderService.ɵfac = function RecaptchaLoaderService_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaLoaderService)(i0.ɵɵinject(PLATFORM_ID), i0.ɵɵinject(RECAPTCHA_LANGUAGE, 8), i0.ɵɵinject(RECAPTCHA_BASE_URL, 8), i0.ɵɵinject(RECAPTCHA_NONCE, 8), i0.ɵɵinject(RECAPTCHA_V3_SITE_KEY, 8));\n };\n RecaptchaLoaderService.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n token: RecaptchaLoaderService,\n factory: RecaptchaLoaderService.ɵfac\n });\n return RecaptchaLoaderService;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet nextId = 0;\nlet RecaptchaComponent = /*#__PURE__*/(() => {\n class RecaptchaComponent {\n constructor(elementRef, loader, zone, settings) {\n this.elementRef = elementRef;\n this.loader = loader;\n this.zone = zone;\n this.id = `ngrecaptcha-${nextId++}`;\n this.errorMode = \"default\";\n this.resolved = new EventEmitter();\n // The rename will happen a bit later\n // eslint-disable-next-line @angular-eslint/no-output-native\n this.error = new EventEmitter();\n if (settings) {\n this.siteKey = settings.siteKey;\n this.theme = settings.theme;\n this.type = settings.type;\n this.size = settings.size;\n this.badge = settings.badge;\n }\n }\n ngAfterViewInit() {\n this.subscription = this.loader.ready.subscribe(grecaptcha => {\n if (grecaptcha != null && grecaptcha.render instanceof Function) {\n this.grecaptcha = grecaptcha;\n this.renderRecaptcha();\n }\n });\n }\n ngOnDestroy() {\n // reset the captcha to ensure it does not leave anything behind\n // after the component is no longer needed\n this.grecaptchaReset();\n if (this.subscription) {\n this.subscription.unsubscribe();\n }\n }\n /**\n * Executes the invisible recaptcha.\n * Does nothing if component's size is not set to \"invisible\".\n */\n execute() {\n if (this.size !== \"invisible\") {\n return;\n }\n if (this.widget != null) {\n this.grecaptcha.execute(this.widget);\n } else {\n // delay execution of recaptcha until it actually renders\n this.executeRequested = true;\n }\n }\n reset() {\n if (this.widget != null) {\n if (this.grecaptcha.getResponse(this.widget)) {\n // Only emit an event in case if something would actually change.\n // That way we do not trigger \"touching\" of the control if someone does a \"reset\"\n // on a non-resolved captcha.\n this.resolved.emit(null);\n }\n this.grecaptchaReset();\n }\n }\n /**\n * ⚠️ Warning! Use this property at your own risk!\n *\n * While this member is `public`, it is not a part of the component's public API.\n * The semantic versioning guarantees _will not be honored_! Thus, you might find that this property behavior changes in incompatible ways in minor or even patch releases.\n * You are **strongly advised** against using this property.\n * Instead, use more idiomatic ways to get reCAPTCHA value, such as `resolved` EventEmitter, or form-bound methods (ngModel, formControl, and the likes).å\n */\n get __unsafe_widgetValue() {\n return this.widget != null ? this.grecaptcha.getResponse(this.widget) : null;\n }\n /** @internal */\n expired() {\n this.resolved.emit(null);\n }\n /** @internal */\n errored(args) {\n this.error.emit(args);\n }\n /** @internal */\n captchaResponseCallback(response) {\n this.resolved.emit(response);\n }\n /** @internal */\n grecaptchaReset() {\n if (this.widget != null) {\n this.zone.runOutsideAngular(() => this.grecaptcha.reset(this.widget));\n }\n }\n /** @internal */\n renderRecaptcha() {\n // This `any` can be removed after @types/grecaptcha get updated\n const renderOptions = {\n badge: this.badge,\n callback: response => {\n this.zone.run(() => this.captchaResponseCallback(response));\n },\n \"expired-callback\": () => {\n this.zone.run(() => this.expired());\n },\n sitekey: this.siteKey,\n size: this.size,\n tabindex: this.tabIndex,\n theme: this.theme,\n type: this.type\n };\n if (this.errorMode === \"handled\") {\n renderOptions[\"error-callback\"] = (...args) => {\n this.zone.run(() => this.errored(args));\n };\n }\n this.widget = this.grecaptcha.render(this.elementRef.nativeElement, renderOptions);\n if (this.executeRequested === true) {\n this.executeRequested = false;\n this.execute();\n }\n }\n }\n RecaptchaComponent.ɵfac = function RecaptchaComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(RecaptchaLoaderService), i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(RECAPTCHA_SETTINGS, 8));\n };\n RecaptchaComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: RecaptchaComponent,\n selectors: [[\"re-captcha\"]],\n hostVars: 1,\n hostBindings: function RecaptchaComponent_HostBindings(rf, ctx) {\n if (rf & 2) {\n i0.ɵɵattribute(\"id\", ctx.id);\n }\n },\n inputs: {\n id: \"id\",\n siteKey: \"siteKey\",\n theme: \"theme\",\n type: \"type\",\n size: \"size\",\n tabIndex: \"tabIndex\",\n badge: \"badge\",\n errorMode: \"errorMode\"\n },\n outputs: {\n resolved: \"resolved\",\n error: \"error\"\n },\n exportAs: [\"reCaptcha\"],\n standalone: false,\n decls: 0,\n vars: 0,\n template: function RecaptchaComponent_Template(rf, ctx) {},\n encapsulation: 2\n });\n return RecaptchaComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet RecaptchaCommonModule = /*#__PURE__*/(() => {\n class RecaptchaCommonModule {}\n RecaptchaCommonModule.ɵfac = function RecaptchaCommonModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaCommonModule)();\n };\n RecaptchaCommonModule.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: RecaptchaCommonModule\n });\n RecaptchaCommonModule.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({});\n return RecaptchaCommonModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet RecaptchaModule = /*#__PURE__*/(() => {\n class RecaptchaModule {}\n RecaptchaModule.ɵfac = function RecaptchaModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaModule)();\n };\n RecaptchaModule.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: RecaptchaModule\n });\n RecaptchaModule.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n providers: [RecaptchaLoaderService],\n imports: [RecaptchaCommonModule]\n });\n return RecaptchaModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * The main service for working with reCAPTCHA v3 APIs.\n *\n * Use the `execute` method for executing a single action, and\n * `onExecute` observable for listening to all actions at once.\n */\nlet ReCaptchaV3Service = /*#__PURE__*/(() => {\n class ReCaptchaV3Service {\n constructor(zone, siteKey,\n // eslint-disable-next-line @typescript-eslint/ban-types\n platformId, baseUrl, nonce, language) {\n /** @internal */\n this.onLoadComplete = grecaptcha => {\n this.grecaptcha = grecaptcha;\n if (this.actionBacklog && this.actionBacklog.length > 0) {\n this.actionBacklog.forEach(([action, subject]) => this.executeActionWithSubject(action, subject));\n this.actionBacklog = undefined;\n }\n };\n this.zone = zone;\n this.isBrowser = isPlatformBrowser(platformId);\n this.siteKey = siteKey;\n this.nonce = nonce;\n this.language = language;\n this.baseUrl = baseUrl;\n this.init();\n }\n get onExecute() {\n if (!this.onExecuteSubject) {\n this.onExecuteSubject = new Subject();\n this.onExecuteObservable = this.onExecuteSubject.asObservable();\n }\n return this.onExecuteObservable;\n }\n get onExecuteError() {\n if (!this.onExecuteErrorSubject) {\n this.onExecuteErrorSubject = new Subject();\n this.onExecuteErrorObservable = this.onExecuteErrorSubject.asObservable();\n }\n return this.onExecuteErrorObservable;\n }\n /**\n * Executes the provided `action` with reCAPTCHA v3 API.\n * Use the emitted token value for verification purposes on the backend.\n *\n * For more information about reCAPTCHA v3 actions and tokens refer to the official documentation at\n * https://developers.google.com/recaptcha/docs/v3.\n *\n * @param {string} action the action to execute\n * @returns {Observable} an `Observable` that will emit the reCAPTCHA v3 string `token` value whenever ready.\n * The returned `Observable` completes immediately after emitting a value.\n */\n execute(action) {\n const subject = new Subject();\n if (this.isBrowser) {\n if (!this.grecaptcha) {\n if (!this.actionBacklog) {\n this.actionBacklog = [];\n }\n this.actionBacklog.push([action, subject]);\n } else {\n this.executeActionWithSubject(action, subject);\n }\n }\n return subject.asObservable();\n }\n /** @internal */\n executeActionWithSubject(action, subject) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const onError = error => {\n this.zone.run(() => {\n subject.error(error);\n if (this.onExecuteErrorSubject) {\n // We don't know any better at this point, unfortunately, so have to resort to `any`\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n this.onExecuteErrorSubject.next({\n action,\n error\n });\n }\n });\n };\n this.zone.runOutsideAngular(() => {\n try {\n this.grecaptcha.execute(this.siteKey, {\n action\n }).then(token => {\n this.zone.run(() => {\n subject.next(token);\n subject.complete();\n if (this.onExecuteSubject) {\n this.onExecuteSubject.next({\n action,\n token\n });\n }\n });\n }, onError);\n } catch (e) {\n onError(e);\n }\n });\n }\n /** @internal */\n init() {\n if (this.isBrowser) {\n if (\"grecaptcha\" in window) {\n this.grecaptcha = grecaptcha;\n } else {\n const langParam = this.language ? \"&hl=\" + this.language : \"\";\n loader.loadScript(this.siteKey, this.onLoadComplete, langParam, this.baseUrl, this.nonce);\n }\n }\n }\n }\n ReCaptchaV3Service.ɵfac = function ReCaptchaV3Service_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || ReCaptchaV3Service)(i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(RECAPTCHA_V3_SITE_KEY), i0.ɵɵinject(PLATFORM_ID), i0.ɵɵinject(RECAPTCHA_BASE_URL, 8), i0.ɵɵinject(RECAPTCHA_NONCE, 8), i0.ɵɵinject(RECAPTCHA_LANGUAGE, 8));\n };\n ReCaptchaV3Service.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n token: ReCaptchaV3Service,\n factory: ReCaptchaV3Service.ɵfac\n });\n return ReCaptchaV3Service;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet RecaptchaV3Module = /*#__PURE__*/(() => {\n class RecaptchaV3Module {}\n RecaptchaV3Module.ɵfac = function RecaptchaV3Module_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaV3Module)();\n };\n RecaptchaV3Module.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: RecaptchaV3Module\n });\n RecaptchaV3Module.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n providers: [ReCaptchaV3Service]\n });\n return RecaptchaV3Module;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet RecaptchaValueAccessorDirective = /*#__PURE__*/(() => {\n class RecaptchaValueAccessorDirective {\n constructor(host) {\n this.host = host;\n this.requiresControllerReset = false;\n }\n writeValue(value) {\n if (!value) {\n this.host.reset();\n } else {\n // In this case, it is most likely that a form controller has requested to write a specific value into the component.\n // This isn't really a supported case - reCAPTCHA values are single-use, and, in a sense, readonly.\n // What this means is that the form controller has recaptcha control state of X, while reCAPTCHA itself can't \"restore\"\n // to that state. In order to make form controller aware of this discrepancy, and to fix the said misalignment,\n // we'll be telling the controller to \"reset\" the value back to null.\n if (this.host.__unsafe_widgetValue !== value && Boolean(this.host.__unsafe_widgetValue) === false) {\n this.requiresControllerReset = true;\n }\n }\n }\n registerOnChange(fn) {\n this.onChange = fn;\n if (this.requiresControllerReset) {\n this.requiresControllerReset = false;\n this.onChange(null);\n }\n }\n registerOnTouched(fn) {\n this.onTouched = fn;\n }\n onResolve($event) {\n if (this.onChange) {\n this.onChange($event);\n }\n if (this.onTouched) {\n this.onTouched();\n }\n }\n }\n RecaptchaValueAccessorDirective.ɵfac = function RecaptchaValueAccessorDirective_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaValueAccessorDirective)(i0.ɵɵdirectiveInject(RecaptchaComponent));\n };\n RecaptchaValueAccessorDirective.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: RecaptchaValueAccessorDirective,\n selectors: [[\"re-captcha\", \"formControlName\", \"\"], [\"re-captcha\", \"formControl\", \"\"], [\"re-captcha\", \"ngModel\", \"\"]],\n hostBindings: function RecaptchaValueAccessorDirective_HostBindings(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵlistener(\"resolved\", function RecaptchaValueAccessorDirective_resolved_HostBindingHandler($event) {\n return ctx.onResolve($event);\n });\n }\n },\n standalone: false,\n features: [i0.ɵɵProvidersFeature([{\n multi: true,\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => RecaptchaValueAccessorDirective)\n }])]\n });\n return RecaptchaValueAccessorDirective;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet RecaptchaFormsModule = /*#__PURE__*/(() => {\n class RecaptchaFormsModule {}\n RecaptchaFormsModule.ɵfac = function RecaptchaFormsModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || RecaptchaFormsModule)();\n };\n RecaptchaFormsModule.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: RecaptchaFormsModule\n });\n RecaptchaFormsModule.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n imports: [FormsModule, RecaptchaCommonModule]\n });\n return RecaptchaFormsModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { RECAPTCHA_BASE_URL, RECAPTCHA_LANGUAGE, RECAPTCHA_NONCE, RECAPTCHA_SETTINGS, RECAPTCHA_V3_SITE_KEY, ReCaptchaV3Service, RecaptchaComponent, RecaptchaFormsModule, RecaptchaLoaderService, RecaptchaModule, RecaptchaV3Module, RecaptchaValueAccessorDirective };\n","import { EnvironmentService, Environment } from '@galaxy/core';\nimport { Injectable } from '@angular/core';\n\n@Injectable()\nexport class BillingErpUrlService {\n private _host: string;\n\n constructor(private environmentService: EnvironmentService) {}\n\n private getFinancialForceHost(): void {\n if (this._host) {\n return;\n }\n\n switch (this.environmentService.getEnvironment()) {\n case Environment.PROD: {\n this._host = '//na102.lightning.force.com';\n break;\n }\n\n case Environment.DEMO: {\n this._host = '//cs3.lightning.force.com';\n break;\n }\n\n default: {\n this._host = '//cs3.lightning.force.com';\n break;\n }\n }\n }\n\n private getNetSuiteHost(): void {\n if (this._host) {\n return;\n }\n\n switch (this.environmentService.getEnvironment()) {\n case Environment.PROD: {\n this._host = '//4336003.app.netsuite.com';\n break;\n }\n\n case Environment.DEMO: {\n this._host = '//4336003-sb2.app.netsuite.com';\n break;\n }\n\n default: {\n this._host = '//4336003-sb2.app.netsuite.com';\n break;\n }\n }\n }\n\n public getSalesInvoiceDetails(invoiceId: string): string {\n this.getNetSuiteHost();\n return `${this._host}/app/accounting/transactions/custinvc.nl?id=${invoiceId}`;\n }\n\n public getSalesCreditNoteDetails(creditNoteId: string): string {\n this.getNetSuiteHost();\n return `${this._host}/app/accounting/transactions/custcred.nl?id=${creditNoteId}`;\n }\n\n public getBankReconciliationStatementList(): string {\n this.getFinancialForceHost();\n return `${this._host}/lightning/o/c2g__codaBankReconciliation__c/list`;\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n inject,\n Inject,\n OnInit,\n signal,\n WritableSignal,\n} from '@angular/core';\nimport { CommonModule, getCurrencySymbol, NgIf } from '@angular/common';\nimport { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';\nimport { MatButtonModule } from '@angular/material/button';\nimport { GalaxyFormFieldModule } from '@vendasta/galaxy/form-field';\nimport {\n FormBuilder,\n FormControl,\n ReactiveFormsModule,\n UntypedFormControl,\n UntypedFormGroup,\n Validators,\n} from '@angular/forms';\nimport { TranslationModule } from '@galaxy/form-builder';\nimport { Discount, DiscountType } from '@galaxy/billing';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatRadioModule } from '@angular/material/radio';\nimport { MatSelectModule } from '@angular/material/select';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { MatDatepickerModule } from '@angular/material/datepicker';\nimport { SnackbarService } from '@vendasta/galaxy/snackbar-service';\nimport {\n getTimezoneOffsetInMilliseconds,\n getUTCEndOfDay,\n getUTCStartOfDay,\n getUTCTime,\n} from '@vendasta/galaxy/utility/date-utils';\nimport { GalaxyButtonLoadingIndicatorModule } from '@vendasta/galaxy/button-loading-indicator';\nimport { TranslateService } from '@ngx-translate/core';\nimport { Moment } from 'moment/moment';\n\nexport type DiscountForRetailDialog = Partial & Pick;\nexport type DiscountFromRetailDialog = DiscountForRetailDialog & Required>;\nexport type OnSaveResponse = Promise<{ success: boolean; discount: DiscountFromRetailDialog }>;\n\nexport interface DiscountDialogInfo {\n discount: DiscountForRetailDialog;\n currencyCode: string;\n mode: 'edit' | 'create';\n onSave: (discount: DiscountFromRetailDialog, mode: 'edit' | 'create') => OnSaveResponse;\n hideDates?: boolean;\n}\n\nconst AMOUNT_VALIDATORS = [Validators.required, Validators.min(0)];\nconst PERCENT_VALIDATORS = [Validators.required, Validators.max(100), Validators.pattern('^[0-9]*$')]; // only whole numbers\nconst RETAIL_DISCOUNT_TYPE_OPTIONS = [DiscountType.FIXED_AMOUNT_PER_UNIT, DiscountType.PERCENT_AMOUNT];\nconst DISCOUNT_DURATION_OPTIONS = {\n FOREVER: 'forever',\n DATE: 'date',\n};\n\n@Component({\n selector: 'billing-ui-add-discount-modal',\n imports: [\n CommonModule,\n MatDialogModule,\n MatButtonModule,\n GalaxyFormFieldModule,\n ReactiveFormsModule,\n TranslationModule,\n MatInputModule,\n NgIf,\n MatRadioModule,\n MatSelectModule,\n MatDatepickerModule,\n GalaxyButtonLoadingIndicatorModule,\n ],\n providers: [],\n templateUrl: './retail-discount-dialog.component.html',\n styleUrls: ['./retail-discount-dialog.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class RetailDiscountDialogComponent implements OnInit {\n formGroup: UntypedFormGroup;\n\n fixedAmountPerUnitDiscountType = DiscountType.FIXED_AMOUNT_PER_UNIT;\n minimumEndDate: WritableSignal = signal(new Date());\n\n currencySymbol = getCurrencySymbol(this.data.currencyCode, 'wide');\n\n isLoading = signal(false);\n private _destroyRef: DestroyRef = inject(DestroyRef);\n\n now = new Date();\n constructor(\n public dialogRef: MatDialogRef,\n private translateService: TranslateService,\n private snackbarService: SnackbarService,\n private formBuilder: FormBuilder,\n @Inject(MAT_DIALOG_DATA) public data: DiscountDialogInfo,\n ) {}\n\n ngOnInit(): void {\n this.formGroup = this.formBuilder.group({\n description: new FormControl(this.data.discount?.description || '', [Validators.required]),\n discountType: new UntypedFormControl(this.data.discount?.discountType || this.fixedAmountPerUnitDiscountType, [\n Validators.required,\n isValidDiscountTypeForRetail,\n ]),\n amount: new FormControl(0, AMOUNT_VALIDATORS),\n percentAmount: new FormControl(0, PERCENT_VALIDATORS),\n startDate: new FormControl(this.now, [Validators.required]),\n endDate: new FormControl(null, []),\n duration: new UntypedFormControl(DISCOUNT_DURATION_OPTIONS.FOREVER, [Validators.required]),\n });\n\n if (this.data.discount.discountType === DiscountType.PERCENT_AMOUNT) {\n this.percentAmount.patchValue(this.data.discount.amount ?? 0, {\n emitEvent: false,\n });\n } else {\n this.amount.patchValue(this.data.discount.amount ?? 0, {\n emitEvent: false,\n });\n }\n\n this.duration.valueChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((value) => {\n // If we are setting the date to forever - then that assumes it starts now\n if (value === DISCOUNT_DURATION_OPTIONS.FOREVER) {\n this.startDate.patchValue(smallestDate(this.startDate.value, this.now), { emitEvent: false });\n }\n });\n\n this.startDate.valueChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((val) => {\n this.minimumEndDate.set(val);\n });\n\n if (this.data.mode === 'edit') {\n this.prefillValues(this.data.discount);\n }\n\n this.discountType.valueChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((val) => {\n if (val === DiscountType.FIXED_AMOUNT_PER_UNIT) {\n this.amount.patchValue(0);\n }\n if (val === DiscountType.PERCENT_AMOUNT) {\n this.percentAmount.patchValue(0);\n }\n });\n }\n\n get description(): FormControl {\n return this.formGroup.get('description') as UntypedFormControl;\n }\n\n get amount(): FormControl {\n return this.formGroup.get('amount') as UntypedFormControl;\n }\n\n get percentAmount(): FormControl {\n return this.formGroup.get('percentAmount') as UntypedFormControl;\n }\n\n get duration(): UntypedFormControl {\n return this.formGroup.get('duration') as UntypedFormControl;\n }\n\n get discountType(): UntypedFormControl {\n return this.formGroup.get('discountType') as UntypedFormControl;\n }\n\n get startDate(): UntypedFormControl {\n return this.formGroup.get('startDate') as UntypedFormControl;\n }\n\n get endDate(): UntypedFormControl {\n return this.formGroup.get('endDate') as UntypedFormControl;\n }\n\n private prefillValues(discount: Partial): void {\n this.description.patchValue(discount.description ?? '', {\n emitEvent: false,\n });\n this.discountType.patchValue(discount.discountType || this.fixedAmountPerUnitDiscountType, {\n emitEvent: false,\n });\n if (discount.discountType === DiscountType.FIXED_AMOUNT_PER_UNIT) {\n this.amount.patchValue((discount.amount ?? 0) / 100, {\n emitEvent: false,\n }); // convert to cents\n } else {\n this.percentAmount.patchValue(discount.amount ?? 0, {\n emitEvent: false,\n });\n }\n\n let hasDate = false;\n if (discount.start) {\n if (discount.start.getTime() > this.now.getTime()) {\n hasDate = true;\n }\n // need to convert discount start & end timestamps to UTC since at this point the value is the user's local time\n // (eg. converting to UTCStartOfDay could mean the wrong day)\n this.startDate.patchValue(adjustDateToForm(discount.start), {\n emitEvent: false,\n });\n }\n\n const zeroDate = new Date('0001-01-01T00:00:00Z').valueOf();\n if (discount.end && discount.end.valueOf() !== zeroDate) {\n hasDate = true;\n this.endDate.patchValue(adjustDateToForm(discount.end), {\n emitEvent: false,\n });\n }\n\n if (hasDate) {\n this.duration.patchValue(DISCOUNT_DURATION_OPTIONS.DATE, {\n emitEvent: false,\n });\n }\n }\n\n async onSave(): Promise {\n if (!this.formGroup.valid) {\n this.snackbarService.openErrorSnack(\n this.translateService.instant('BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.ERROR_MISSING_INFORMATION'),\n );\n return;\n }\n this.isLoading.set(true);\n // calculate discount amount according to the discount type\n let amount = this.amount.value;\n if (this.discountType.value === DiscountType.FIXED_AMOUNT_PER_UNIT) {\n amount = Math.round(amount * 100); // stored in cents\n } else {\n amount = this.percentAmount.value;\n }\n let end: Date | undefined = undefined;\n if (this.endDate?.value && this.duration.value === DISCOUNT_DURATION_OPTIONS.DATE) {\n end = getUTCEndOfDay(adjustDateFromForm(this.endDate.value));\n }\n const start = getUTCStartOfDay(adjustDateFromForm(this.startDate.value));\n\n const discount: DiscountFromRetailDialog = {\n ...this.data.discount,\n discountType: this.discountType.value,\n amount,\n start,\n end: end,\n description: this.description.value,\n };\n if (this.data.onSave) {\n const resp = await this.data.onSave(discount, this.data.mode);\n if (resp.success) {\n this.dialogRef.close(resp.discount);\n }\n } else {\n this.dialogRef.close(false);\n }\n this.isLoading.set(false);\n }\n}\n\nfunction isValidDiscountTypeForRetail() {\n return (control: UntypedFormControl) => {\n if (RETAIL_DISCOUNT_TYPE_OPTIONS.findIndex(control.value) === -1) {\n return { invalidDiscountType: true };\n }\n return null;\n };\n}\n\nfunction adjustDateToForm(d: Date): Date {\n return getUTCTime(d);\n}\n\nfunction adjustDateFromForm(d: Date | Moment): Date {\n return new Date(dateFromMoment(d).getTime() - getTimezoneOffsetInMilliseconds());\n}\n\nfunction smallestDate(d1: Date | Moment, d2: Date | Moment): Date | Moment {\n if (dateFromMoment(d1).getTime() < dateFromMoment(d2).getTime()) {\n return d1;\n }\n return d2;\n}\n\nfunction dateFromMoment(d: Date | Moment): Date {\n if ('toDate' in d) {\n return d.toDate();\n }\n return d;\n}\n","
\n

\n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.TITLE' | translate }}\n \n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.EDIT_TITLE' | translate }}\n \n

\n\n \n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.NAME_LABEL' | translate }}\n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.NAME_HINT' | translate }}\n \n \n\n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.TYPE_LABEL' | translate }}\n \n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.TYPE_FIXED_AMOUNT_PER_UNIT' | translate }}\n \n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.PERCENTAGE' | translate }}\n \n \n \n\n \n \n \n \n \n \n \n \n \n \n\n \n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.DURATION' | translate }}\n \n {{\n 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.DURATION_FOREVER' | translate\n }}\n {{\n 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.DURATION_CUSTOM' | translate\n }}\n \n \n\n \n
\n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.START_DATE' | translate }}\n {{\n 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.START_END_UTC' | translate\n }}\n \n \n \n {{\n 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.START_DATE_HINT' | translate\n }}\n \n\n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.END_DATE' | translate }}\n \n \n \n \n {{ 'BILLING_SUBSCRIPTIONS.CREATE_SUBSCRIPTIONS.DISCOUNTS.END_DATE_AFTER_START' | translate }}\n \n \n
\n
\n
\n
\n\n \n \n \n \n
\n","{\n \"PRICING_COMMONS\": {\n \"STARTING_AT_LABEL\": \"Starting at\",\n \"MINIMUM_PRICE_LABEL\": \"Minimum\",\n \"FREE_LABEL\": \"Free\",\n \"CONTACT_SALES_LABEL\": \"Contact Sales\",\n \"SETUP_FEE_LABEL\": \"Setup fee\",\n \"MANAGEMENT_FEE_LABEL\": \"Management fee\",\n \"PRICE_UNAVAILABLE_LABEL\": \"Price unavailable\",\n \"VARIABLE_PRICE_INPUT\": {\n \"LABEL\": \"Enter amount\",\n \"ADD_NOTE\": \"Add note\",\n \"NOTE_LABEL\": \"Note\"\n }\n },\n \"SUBSIDIARY_CHANGE_WARNING\": {\n \"TITLE\": \"Re-enter your payment method\",\n \"WILL_REQUIRE_PAYMENT_METHOD\": \"Changing your billing contact's country may require you to re-enter a payment method.\",\n \"AVOID_TRANSACTION_DELAYS\": \"Ensure that you re-enter a payment method to avoid delaying future transactions.\",\n \"CONFIRM\": \"Confirm\",\n \"WARNING\": \"Warning\"\n },\n \"REFUND_DIALOG\": {\n \"TITLE\": \"Issue refund\",\n \"ORIGINAL_CHARGE\": \"The original charge was\",\n \"AMOUNT\": \"Refund amount\",\n \"REFUND_MUST_BE_GREATER_THAN_ZERO\": \"Refund must be greater than\",\n \"REFUND_CANNOT_EXCEED_AMOUNT\": \"Refund cannot exceed amount\",\n \"REASON_LABEL\": \"Reason\",\n \"REASON_LABEL_HINT\": \"Reason will not be visible to customer\",\n \"SELECT_REASON\": \"Select a reason\",\n \"REASON_REQUIRED\": \"Reason for refund required\",\n \"DETAILED_NOTES\": \"Detailed notes\",\n \"NOTES_REQUIRED\": \"Notes required\",\n \"NOTES_LABEL\": \"Notes\",\n \"NOTES_LABEL_HINT\": \"Notes will be visible to customer\",\n \"CANCEL\": \"Cancel\",\n \"CONFIRM\": \"Confirm\"\n },\n \"SUBSCRIPTIONS\": {\n \"FILTERS\": {\n \"SEARCH\": \"Search subscriptions\",\n \"FREQUENCY\": {\n \"ONE_TIME\": \"One Time\",\n \"MONTHLY\": \"Monthly\",\n \"YEARLY\": \"Yearly\"\n },\n \"DATE\": {\n \"ENDS_BEFORE\": \"Ends Before\",\n \"ENDS_AFTER\": \"Ends After\"\n }\n },\n \"EXPORT\": {\n \"SUCCESS\": \"Export started, you will receive a download notification when your file is ready\",\n \"ERROR\": \"Failed to start subscriptions export\",\n \"LABEL\": \"Export as CSV\"\n }\n },\n \"BILLING_CONTACT\": {\n \"TITLE\": \"Billing contact\",\n \"COMPANY_NAME\": \"Company name\",\n \"NAME\": \"Name\",\n \"EMAIL_ADDRESS\": \"Email address(es)\",\n \"EMAIL_DELIMITER\": \"Comma or semicolon separated\",\n \"EMAIL_REQUIRED_ERROR\": \"At least one email address is required\",\n \"EMAIL_INVALID_ERROR\": \"Email addresses entered must be valid: example@example.com\",\n \"PHONE_NUMBER\": \"Phone number\",\n \"ADDRESS\": \"Address\",\n \"COUNTRY\": \"Country\",\n \"CITY\": \"City\",\n \"PROVINCE\": \"State/Province\",\n \"POSTAL_CODE\": \"Zip/Postal code\",\n \"CANCEL\": \"Cancel\",\n \"SAVE\": \"Save\",\n \"SAVE_SUCCESS\": \"Successfully updated contact information.\",\n \"SAVE_ERROR\": \"Error updating contact information.\"\n },\n \"CHARGE_INVOICE_DIALOG\": {\n \"TITLE\": \"Charge sales invoice ({{ number }})\",\n \"TOTAL\": \"Total amount:\",\n \"OUTSTANDING\": \"Outstanding balance:\",\n \"AMOUNT\": \"Amount\",\n \"CANCEL\": \"Cancel\",\n \"CHARGE\": \"Charge invoice\",\n \"ERROR_AMOUNT_IS_REQUIRED\": \"Amount is required\",\n \"ERROR_MINIMUM_AMOUNT\": \"Amount must be at least 50\"\n },\n \"PAYMENT_METHOD\": {\n \"UPDATE_SUCCESS\": \"Successfully updated payment method\"\n }\n}\n","import { NgModule } from '@angular/core';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { LexiconModule } from '@galaxy/lexicon';\nimport En from './assets/en_devel.json';\n\n@NgModule({\n imports: [\n TranslateModule,\n LexiconModule.forChild({\n componentName: 'common/ui-billing',\n baseTranslation: En,\n }),\n ],\n})\nexport class BillingUiI18nModule {}\n","import { EnvironmentService, Environment } from '@galaxy/core';\nimport { Injectable } from '@angular/core';\n\n@Injectable()\nexport class StripeUrlService {\n private _host: string;\n\n constructor(private environmentService: EnvironmentService) {}\n\n private getHost(): void {\n if (this._host) {\n return;\n }\n switch (this.environmentService.getEnvironment()) {\n case Environment.PROD: {\n this._host = '//dashboard.stripe.com';\n break;\n }\n\n case Environment.DEMO: {\n this._host = '//dashboard.stripe.com/test';\n break;\n }\n\n default: {\n this._host = '//dashboard.stripe.com/test';\n break;\n }\n }\n }\n\n public getChargeDetailsUrl(chargeId: string): string {\n this.getHost();\n return `${this._host}/payments/${chargeId}`;\n }\n}\n","import { Injectable } from '@angular/core';\nimport { Observable, Observer } from 'rxjs';\n\nexport interface Script {\n name: string;\n src: string;\n}\n\n@Injectable()\nexport class StripeService {\n private loaded = false;\n public loadScript(): Observable