{"version":3,"sources":["node_modules/@angular/material/fesm2022/grid-list.mjs","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/country-state/src/lib/interfaces.ts","libs/country-state/src/lib/country-state.service.ts","libs/country-state/src/lib/country-state.module.ts","node_modules/ng-recaptcha/fesm2020/ng-recaptcha.mjs","libs/billing-ui/src/lib/billing-erp-url.service.ts","libs/billing/src/lib/discount/discount_type.ts","libs/billing/src/lib/discount/discount.ts","libs/billing/src/lib/discount/discount_consumer.ts","libs/billing/src/lib/discount/discount.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/price-in-dollars.pipe.ts","libs/billing-ui/src/lib/utils/pipes/resolve-currency-code.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/billing-ui.module.ts","libs/inventory-ui/src/lib/common/edition-selector/edition-selector.component.ts","libs/inventory-ui/src/lib/common/edition-selector/edition-selector.component.html","libs/inventory-ui/src/lib/common/edition-selector/find-item-edition.pipe.ts","libs/inventory-ui/src/lib/common/pipes/billing-period.pipe.ts","libs/inventory-ui/src/lib/common/item-common.module.ts"],"sourcesContent":["import * as i0 from '@angular/core';\nimport { InjectionToken, Component, ViewEncapsulation, ChangeDetectionStrategy, Optional, Inject, Input, ContentChildren, Directive, NgModule } from '@angular/core';\nimport { setLines, MatLine, MatLineModule, MatCommonModule } from '@angular/material/core';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport * as i1 from '@angular/cdk/bidi';\n\n/**\n * Class for determining, from a list of tiles, the (row, col) position of each of those tiles\n * in the grid. This is necessary (rather than just rendering the tiles in normal document flow)\n * because the tiles can have a rowspan.\n *\n * The positioning algorithm greedily places each tile as soon as it encounters a gap in the grid\n * large enough to accommodate it so that the tiles still render in the same order in which they\n * are given.\n *\n * The basis of the algorithm is the use of an array to track the already placed tiles. Each\n * element of the array corresponds to a column, and the value indicates how many cells in that\n * column are already occupied; zero indicates an empty cell. Moving \"down\" to the next row\n * decrements each value in the tracking array (indicating that the column is one cell closer to\n * being free).\n *\n * @docs-private\n */\nconst _c0 = [\"*\"];\nconst _c1 = [[[\"\", \"mat-grid-avatar\", \"\"], [\"\", \"matGridAvatar\", \"\"]], [[\"\", \"mat-line\", \"\"], [\"\", \"matLine\", \"\"]], \"*\"];\nconst _c2 = [\"[mat-grid-avatar], [matGridAvatar]\", \"[mat-line], [matLine]\", \"*\"];\nconst _c3 = \".mat-grid-list{display:block;position:relative}.mat-grid-tile{display:block;position:absolute;overflow:hidden}.mat-grid-tile .mat-grid-tile-header,.mat-grid-tile .mat-grid-tile-footer{display:flex;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.38);overflow:hidden;padding:0 16px;position:absolute;left:0;right:0}.mat-grid-tile .mat-grid-tile-header>*,.mat-grid-tile .mat-grid-tile-footer>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-tile-header.mat-2-line,.mat-grid-tile .mat-grid-tile-footer.mat-2-line{height:68px}.mat-grid-tile .mat-grid-list-text{display:flex;flex-direction:column;flex:auto;box-sizing:border-box;overflow:hidden}.mat-grid-tile .mat-grid-list-text>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-list-text:empty{display:none}.mat-grid-tile .mat-grid-tile-header{top:0}.mat-grid-tile .mat-grid-tile-footer{bottom:0}.mat-grid-tile .mat-grid-avatar{padding-right:16px}[dir=rtl] .mat-grid-tile .mat-grid-avatar{padding-right:0;padding-left:16px}.mat-grid-tile .mat-grid-avatar:empty{display:none}.mat-grid-tile-header{font-size:var(--mat-grid-list-tile-header-primary-text-size)}.mat-grid-tile-header .mat-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;box-sizing:border-box}.mat-grid-tile-header .mat-line:nth-child(n+2){font-size:var(--mat-grid-list-tile-header-secondary-text-size)}.mat-grid-tile-footer{font-size:var(--mat-grid-list-tile-footer-primary-text-size)}.mat-grid-tile-footer .mat-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;box-sizing:border-box}.mat-grid-tile-footer .mat-line:nth-child(n+2){font-size:var(--mat-grid-list-tile-footer-secondary-text-size)}.mat-grid-tile-content{top:0;left:0;right:0;bottom:0;position:absolute;display:flex;align-items:center;justify-content:center;height:100%;padding:0;margin:0}\";\nclass TileCoordinator {\n constructor() {\n /** Index at which the search for the next gap will start. */\n this.columnIndex = 0;\n /** The current row index. */\n this.rowIndex = 0;\n }\n /** Gets the total number of rows occupied by tiles */\n get rowCount() {\n return this.rowIndex + 1;\n }\n /**\n * Gets the total span of rows occupied by tiles.\n * Ex: A list with 1 row that contains a tile with rowspan 2 will have a total rowspan of 2.\n */\n get rowspan() {\n const lastRowMax = Math.max(...this.tracker);\n // if any of the tiles has a rowspan that pushes it beyond the total row count,\n // add the difference to the rowcount\n return lastRowMax > 1 ? this.rowCount + lastRowMax - 1 : this.rowCount;\n }\n /**\n * Updates the tile positions.\n * @param numColumns Amount of columns in the grid.\n * @param tiles Tiles to be positioned.\n */\n update(numColumns, tiles) {\n this.columnIndex = 0;\n this.rowIndex = 0;\n this.tracker = new Array(numColumns);\n this.tracker.fill(0, 0, this.tracker.length);\n this.positions = tiles.map(tile => this._trackTile(tile));\n }\n /** Calculates the row and col position of a tile. */\n _trackTile(tile) {\n // Find a gap large enough for this tile.\n const gapStartIndex = this._findMatchingGap(tile.colspan);\n // Place tile in the resulting gap.\n this._markTilePosition(gapStartIndex, tile);\n // The next time we look for a gap, the search will start at columnIndex, which should be\n // immediately after the tile that has just been placed.\n this.columnIndex = gapStartIndex + tile.colspan;\n return new TilePosition(this.rowIndex, gapStartIndex);\n }\n /** Finds the next available space large enough to fit the tile. */\n _findMatchingGap(tileCols) {\n if (tileCols > this.tracker.length && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n throw Error(`mat-grid-list: tile with colspan ${tileCols} is wider than ` + `grid with cols=\"${this.tracker.length}\".`);\n }\n // Start index is inclusive, end index is exclusive.\n let gapStartIndex = -1;\n let gapEndIndex = -1;\n // Look for a gap large enough to fit the given tile. Empty spaces are marked with a zero.\n do {\n // If we've reached the end of the row, go to the next row.\n if (this.columnIndex + tileCols > this.tracker.length) {\n this._nextRow();\n gapStartIndex = this.tracker.indexOf(0, this.columnIndex);\n gapEndIndex = this._findGapEndIndex(gapStartIndex);\n continue;\n }\n gapStartIndex = this.tracker.indexOf(0, this.columnIndex);\n // If there are no more empty spaces in this row at all, move on to the next row.\n if (gapStartIndex == -1) {\n this._nextRow();\n gapStartIndex = this.tracker.indexOf(0, this.columnIndex);\n gapEndIndex = this._findGapEndIndex(gapStartIndex);\n continue;\n }\n gapEndIndex = this._findGapEndIndex(gapStartIndex);\n // If a gap large enough isn't found, we want to start looking immediately after the current\n // gap on the next iteration.\n this.columnIndex = gapStartIndex + 1;\n // Continue iterating until we find a gap wide enough for this tile. Since gapEndIndex is\n // exclusive, gapEndIndex is 0 means we didn't find a gap and should continue.\n } while (gapEndIndex - gapStartIndex < tileCols || gapEndIndex == 0);\n // If we still didn't manage to find a gap, ensure that the index is\n // at least zero so the tile doesn't get pulled out of the grid.\n return Math.max(gapStartIndex, 0);\n }\n /** Move \"down\" to the next row. */\n _nextRow() {\n this.columnIndex = 0;\n this.rowIndex++;\n // Decrement all spaces by one to reflect moving down one row.\n for (let i = 0; i < this.tracker.length; i++) {\n this.tracker[i] = Math.max(0, this.tracker[i] - 1);\n }\n }\n /**\n * Finds the end index (exclusive) of a gap given the index from which to start looking.\n * The gap ends when a non-zero value is found.\n */\n _findGapEndIndex(gapStartIndex) {\n for (let i = gapStartIndex + 1; i < this.tracker.length; i++) {\n if (this.tracker[i] != 0) {\n return i;\n }\n }\n // The gap ends with the end of the row.\n return this.tracker.length;\n }\n /** Update the tile tracker to account for the given tile in the given space. */\n _markTilePosition(start, tile) {\n for (let i = 0; i < tile.colspan; i++) {\n this.tracker[start + i] = tile.rowspan;\n }\n }\n}\n/**\n * Simple data structure for tile position (row, col).\n * @docs-private\n */\nclass TilePosition {\n constructor(row, col) {\n this.row = row;\n this.col = col;\n }\n}\n\n/**\n * Injection token used to provide a grid list to a tile and to avoid circular imports.\n * @docs-private\n */\nconst MAT_GRID_LIST = /*#__PURE__*/new InjectionToken('MAT_GRID_LIST');\nlet MatGridTile = /*#__PURE__*/(() => {\n class MatGridTile {\n constructor(_element, _gridList) {\n this._element = _element;\n this._gridList = _gridList;\n this._rowspan = 1;\n this._colspan = 1;\n }\n /** Amount of rows that the grid tile takes up. */\n get rowspan() {\n return this._rowspan;\n }\n set rowspan(value) {\n this._rowspan = Math.round(coerceNumberProperty(value));\n }\n /** Amount of columns that the grid tile takes up. */\n get colspan() {\n return this._colspan;\n }\n set colspan(value) {\n this._colspan = Math.round(coerceNumberProperty(value));\n }\n /**\n * Sets the style of the grid-tile element. Needs to be set manually to avoid\n * \"Changed after checked\" errors that would occur with HostBinding.\n */\n _setStyle(property, value) {\n this._element.nativeElement.style[property] = value;\n }\n static {\n this.ɵfac = function MatGridTile_Factory(t) {\n return new (t || MatGridTile)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(MAT_GRID_LIST, 8));\n };\n }\n static {\n this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: MatGridTile,\n selectors: [[\"mat-grid-tile\"]],\n hostAttrs: [1, \"mat-grid-tile\"],\n hostVars: 2,\n hostBindings: function MatGridTile_HostBindings(rf, ctx) {\n if (rf & 2) {\n i0.ɵɵattribute(\"rowspan\", ctx.rowspan)(\"colspan\", ctx.colspan);\n }\n },\n inputs: {\n rowspan: \"rowspan\",\n colspan: \"colspan\"\n },\n exportAs: [\"matGridTile\"],\n standalone: true,\n features: [i0.ɵɵStandaloneFeature],\n ngContentSelectors: _c0,\n decls: 2,\n vars: 0,\n consts: [[1, \"mat-grid-tile-content\"]],\n template: function MatGridTile_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵprojectionDef();\n i0.ɵɵelementStart(0, \"div\", 0);\n i0.ɵɵprojection(1);\n i0.ɵɵelementEnd();\n }\n },\n styles: [\".mat-grid-list{display:block;position:relative}.mat-grid-tile{display:block;position:absolute;overflow:hidden}.mat-grid-tile .mat-grid-tile-header,.mat-grid-tile .mat-grid-tile-footer{display:flex;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.38);overflow:hidden;padding:0 16px;position:absolute;left:0;right:0}.mat-grid-tile .mat-grid-tile-header>*,.mat-grid-tile .mat-grid-tile-footer>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-tile-header.mat-2-line,.mat-grid-tile .mat-grid-tile-footer.mat-2-line{height:68px}.mat-grid-tile .mat-grid-list-text{display:flex;flex-direction:column;flex:auto;box-sizing:border-box;overflow:hidden}.mat-grid-tile .mat-grid-list-text>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-list-text:empty{display:none}.mat-grid-tile .mat-grid-tile-header{top:0}.mat-grid-tile .mat-grid-tile-footer{bottom:0}.mat-grid-tile .mat-grid-avatar{padding-right:16px}[dir=rtl] .mat-grid-tile .mat-grid-avatar{padding-right:0;padding-left:16px}.mat-grid-tile .mat-grid-avatar:empty{display:none}.mat-grid-tile-header{font-size:var(--mat-grid-list-tile-header-primary-text-size)}.mat-grid-tile-header .mat-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;box-sizing:border-box}.mat-grid-tile-header .mat-line:nth-child(n+2){font-size:var(--mat-grid-list-tile-header-secondary-text-size)}.mat-grid-tile-footer{font-size:var(--mat-grid-list-tile-footer-primary-text-size)}.mat-grid-tile-footer .mat-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;box-sizing:border-box}.mat-grid-tile-footer .mat-line:nth-child(n+2){font-size:var(--mat-grid-list-tile-footer-secondary-text-size)}.mat-grid-tile-content{top:0;left:0;right:0;bottom:0;position:absolute;display:flex;align-items:center;justify-content:center;height:100%;padding:0;margin:0}\"],\n encapsulation: 2,\n changeDetection: 0\n });\n }\n }\n return MatGridTile;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet MatGridTileText = /*#__PURE__*/(() => {\n class MatGridTileText {\n constructor(_element) {\n this._element = _element;\n }\n ngAfterContentInit() {\n setLines(this._lines, this._element);\n }\n static {\n this.ɵfac = function MatGridTileText_Factory(t) {\n return new (t || MatGridTileText)(i0.ɵɵdirectiveInject(i0.ElementRef));\n };\n }\n static {\n this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: MatGridTileText,\n selectors: [[\"mat-grid-tile-header\"], [\"mat-grid-tile-footer\"]],\n contentQueries: function MatGridTileText_ContentQueries(rf, ctx, dirIndex) {\n if (rf & 1) {\n i0.ɵɵcontentQuery(dirIndex, MatLine, 5);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx._lines = _t);\n }\n },\n standalone: true,\n features: [i0.ɵɵStandaloneFeature],\n ngContentSelectors: _c2,\n decls: 4,\n vars: 0,\n consts: [[1, \"mat-grid-list-text\"]],\n template: function MatGridTileText_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵprojectionDef(_c1);\n i0.ɵɵprojection(0);\n i0.ɵɵelementStart(1, \"div\", 0);\n i0.ɵɵprojection(2, 1);\n i0.ɵɵelementEnd();\n i0.ɵɵprojection(3, 2);\n }\n },\n encapsulation: 2,\n changeDetection: 0\n });\n }\n }\n return MatGridTileText;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n/**\n * Directive whose purpose is to add the mat- CSS styling to this selector.\n * @docs-private\n */\nlet MatGridAvatarCssMatStyler = /*#__PURE__*/(() => {\n class MatGridAvatarCssMatStyler {\n static {\n this.ɵfac = function MatGridAvatarCssMatStyler_Factory(t) {\n return new (t || MatGridAvatarCssMatStyler)();\n };\n }\n static {\n this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: MatGridAvatarCssMatStyler,\n selectors: [[\"\", \"mat-grid-avatar\", \"\"], [\"\", \"matGridAvatar\", \"\"]],\n hostAttrs: [1, \"mat-grid-avatar\"],\n standalone: true\n });\n }\n }\n return MatGridAvatarCssMatStyler;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n/**\n * Directive whose purpose is to add the mat- CSS styling to this selector.\n * @docs-private\n */\nlet MatGridTileHeaderCssMatStyler = /*#__PURE__*/(() => {\n class MatGridTileHeaderCssMatStyler {\n static {\n this.ɵfac = function MatGridTileHeaderCssMatStyler_Factory(t) {\n return new (t || MatGridTileHeaderCssMatStyler)();\n };\n }\n static {\n this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: MatGridTileHeaderCssMatStyler,\n selectors: [[\"mat-grid-tile-header\"]],\n hostAttrs: [1, \"mat-grid-tile-header\"],\n standalone: true\n });\n }\n }\n return MatGridTileHeaderCssMatStyler;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n/**\n * Directive whose purpose is to add the mat- CSS styling to this selector.\n * @docs-private\n */\nlet MatGridTileFooterCssMatStyler = /*#__PURE__*/(() => {\n class MatGridTileFooterCssMatStyler {\n static {\n this.ɵfac = function MatGridTileFooterCssMatStyler_Factory(t) {\n return new (t || MatGridTileFooterCssMatStyler)();\n };\n }\n static {\n this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: MatGridTileFooterCssMatStyler,\n selectors: [[\"mat-grid-tile-footer\"]],\n hostAttrs: [1, \"mat-grid-tile-footer\"],\n standalone: true\n });\n }\n }\n return MatGridTileFooterCssMatStyler;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * RegExp that can be used to check whether a value will\n * be allowed inside a CSS `calc()` expression.\n */\nconst cssCalcAllowedValue = /^-?\\d+((\\.\\d+)?[A-Za-z%$]?)+$/;\n/**\n * Sets the style properties for an individual tile, given the position calculated by the\n * Tile Coordinator.\n * @docs-private\n */\nclass TileStyler {\n constructor() {\n this._rows = 0;\n this._rowspan = 0;\n }\n /**\n * Adds grid-list layout info once it is available. Cannot be processed in the constructor\n * because these properties haven't been calculated by that point.\n *\n * @param gutterSize Size of the grid's gutter.\n * @param tracker Instance of the TileCoordinator.\n * @param cols Amount of columns in the grid.\n * @param direction Layout direction of the grid.\n */\n init(gutterSize, tracker, cols, direction) {\n this._gutterSize = normalizeUnits(gutterSize);\n this._rows = tracker.rowCount;\n this._rowspan = tracker.rowspan;\n this._cols = cols;\n this._direction = direction;\n }\n /**\n * Computes the amount of space a single 1x1 tile would take up (width or height).\n * Used as a basis for other calculations.\n * @param sizePercent Percent of the total grid-list space that one 1x1 tile would take up.\n * @param gutterFraction Fraction of the gutter size taken up by one 1x1 tile.\n * @return The size of a 1x1 tile as an expression that can be evaluated via CSS calc().\n */\n getBaseTileSize(sizePercent, gutterFraction) {\n // Take the base size percent (as would be if evenly dividing the size between cells),\n // and then subtracting the size of one gutter. However, since there are no gutters on the\n // edges, each tile only uses a fraction (gutterShare = numGutters / numCells) of the gutter\n // size. (Imagine having one gutter per tile, and then breaking up the extra gutter on the\n // edge evenly among the cells).\n return `(${sizePercent}% - (${this._gutterSize} * ${gutterFraction}))`;\n }\n /**\n * Gets The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value.\n * @param offset Number of tiles that have already been rendered in the row/column.\n * @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize).\n * @return Position of the tile as a CSS calc() expression.\n */\n getTilePosition(baseSize, offset) {\n // The position comes the size of a 1x1 tile plus gutter for each previous tile in the\n // row/column (offset).\n return offset === 0 ? '0' : calc(`(${baseSize} + ${this._gutterSize}) * ${offset}`);\n }\n /**\n * Gets the actual size of a tile, e.g., width or height, taking rowspan or colspan into account.\n * @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize).\n * @param span The tile's rowspan or colspan.\n * @return Size of the tile as a CSS calc() expression.\n */\n getTileSize(baseSize, span) {\n return `(${baseSize} * ${span}) + (${span - 1} * ${this._gutterSize})`;\n }\n /**\n * Sets the style properties to be applied to a tile for the given row and column index.\n * @param tile Tile to which to apply the styling.\n * @param rowIndex Index of the tile's row.\n * @param colIndex Index of the tile's column.\n */\n setStyle(tile, rowIndex, colIndex) {\n // Percent of the available horizontal space that one column takes up.\n let percentWidthPerTile = 100 / this._cols;\n // Fraction of the vertical gutter size that each column takes up.\n // For example, if there are 5 columns, each column uses 4/5 = 0.8 times the gutter width.\n let gutterWidthFractionPerTile = (this._cols - 1) / this._cols;\n this.setColStyles(tile, colIndex, percentWidthPerTile, gutterWidthFractionPerTile);\n this.setRowStyles(tile, rowIndex, percentWidthPerTile, gutterWidthFractionPerTile);\n }\n /** Sets the horizontal placement of the tile in the list. */\n setColStyles(tile, colIndex, percentWidth, gutterWidth) {\n // Base horizontal size of a column.\n let baseTileWidth = this.getBaseTileSize(percentWidth, gutterWidth);\n // The width and horizontal position of each tile is always calculated the same way, but the\n // height and vertical position depends on the rowMode.\n let side = this._direction === 'rtl' ? 'right' : 'left';\n tile._setStyle(side, this.getTilePosition(baseTileWidth, colIndex));\n tile._setStyle('width', calc(this.getTileSize(baseTileWidth, tile.colspan)));\n }\n /**\n * Calculates the total size taken up by gutters across one axis of a list.\n */\n getGutterSpan() {\n return `${this._gutterSize} * (${this._rowspan} - 1)`;\n }\n /**\n * Calculates the total size taken up by tiles across one axis of a list.\n * @param tileHeight Height of the tile.\n */\n getTileSpan(tileHeight) {\n return `${this._rowspan} * ${this.getTileSize(tileHeight, 1)}`;\n }\n /**\n * Calculates the computed height and returns the correct style property to set.\n * This method can be implemented by each type of TileStyler.\n * @docs-private\n */\n getComputedHeight() {\n return null;\n }\n}\n/**\n * This type of styler is instantiated when the user passes in a fixed row height.\n * Example ``\n * @docs-private\n */\nclass FixedTileStyler extends TileStyler {\n constructor(fixedRowHeight) {\n super();\n this.fixedRowHeight = fixedRowHeight;\n }\n init(gutterSize, tracker, cols, direction) {\n super.init(gutterSize, tracker, cols, direction);\n this.fixedRowHeight = normalizeUnits(this.fixedRowHeight);\n if (!cssCalcAllowedValue.test(this.fixedRowHeight) && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n throw Error(`Invalid value \"${this.fixedRowHeight}\" set as rowHeight.`);\n }\n }\n setRowStyles(tile, rowIndex) {\n tile._setStyle('top', this.getTilePosition(this.fixedRowHeight, rowIndex));\n tile._setStyle('height', calc(this.getTileSize(this.fixedRowHeight, tile.rowspan)));\n }\n getComputedHeight() {\n return ['height', calc(`${this.getTileSpan(this.fixedRowHeight)} + ${this.getGutterSpan()}`)];\n }\n reset(list) {\n list._setListStyle(['height', null]);\n if (list._tiles) {\n list._tiles.forEach(tile => {\n tile._setStyle('top', null);\n tile._setStyle('height', null);\n });\n }\n }\n}\n/**\n * This type of styler is instantiated when the user passes in a width:height ratio\n * for the row height. Example ``\n * @docs-private\n */\nclass RatioTileStyler extends TileStyler {\n constructor(value) {\n super();\n this._parseRatio(value);\n }\n setRowStyles(tile, rowIndex, percentWidth, gutterWidth) {\n let percentHeightPerTile = percentWidth / this.rowHeightRatio;\n this.baseTileHeight = this.getBaseTileSize(percentHeightPerTile, gutterWidth);\n // Use padding-top and margin-top to maintain the given aspect ratio, as\n // a percentage-based value for these properties is applied versus the *width* of the\n // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties\n tile._setStyle('marginTop', this.getTilePosition(this.baseTileHeight, rowIndex));\n tile._setStyle('paddingTop', calc(this.getTileSize(this.baseTileHeight, tile.rowspan)));\n }\n getComputedHeight() {\n return ['paddingBottom', calc(`${this.getTileSpan(this.baseTileHeight)} + ${this.getGutterSpan()}`)];\n }\n reset(list) {\n list._setListStyle(['paddingBottom', null]);\n list._tiles.forEach(tile => {\n tile._setStyle('marginTop', null);\n tile._setStyle('paddingTop', null);\n });\n }\n _parseRatio(value) {\n const ratioParts = value.split(':');\n if (ratioParts.length !== 2 && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n throw Error(`mat-grid-list: invalid ratio given for row-height: \"${value}\"`);\n }\n this.rowHeightRatio = parseFloat(ratioParts[0]) / parseFloat(ratioParts[1]);\n }\n}\n/**\n * This type of styler is instantiated when the user selects a \"fit\" row height mode.\n * In other words, the row height will reflect the total height of the container divided\n * by the number of rows. Example ``\n *\n * @docs-private\n */\nclass FitTileStyler extends TileStyler {\n setRowStyles(tile, rowIndex) {\n // Percent of the available vertical space that one row takes up.\n let percentHeightPerTile = 100 / this._rowspan;\n // Fraction of the horizontal gutter size that each column takes up.\n let gutterHeightPerTile = (this._rows - 1) / this._rows;\n // Base vertical size of a column.\n let baseTileHeight = this.getBaseTileSize(percentHeightPerTile, gutterHeightPerTile);\n tile._setStyle('top', this.getTilePosition(baseTileHeight, rowIndex));\n tile._setStyle('height', calc(this.getTileSize(baseTileHeight, tile.rowspan)));\n }\n reset(list) {\n if (list._tiles) {\n list._tiles.forEach(tile => {\n tile._setStyle('top', null);\n tile._setStyle('height', null);\n });\n }\n }\n}\n/** Wraps a CSS string in a calc function */\nfunction calc(exp) {\n return `calc(${exp})`;\n}\n/** Appends pixels to a CSS string if no units are given. */\nfunction normalizeUnits(value) {\n return value.match(/([A-Za-z%]+)$/) ? value : `${value}px`;\n}\n\n// TODO(kara): Conditional (responsive) column count / row size.\n// TODO(kara): Re-layout on window resize / media change (debounced).\n// TODO(kara): gridTileHeader and gridTileFooter.\nconst MAT_FIT_MODE = 'fit';\nlet MatGridList = /*#__PURE__*/(() => {\n class MatGridList {\n constructor(_element, _dir) {\n this._element = _element;\n this._dir = _dir;\n /** The amount of space between tiles. This will be something like '5px' or '2em'. */\n this._gutter = '1px';\n }\n /** Amount of columns in the grid list. */\n get cols() {\n return this._cols;\n }\n set cols(value) {\n this._cols = Math.max(1, Math.round(coerceNumberProperty(value)));\n }\n /** Size of the grid list's gutter in pixels. */\n get gutterSize() {\n return this._gutter;\n }\n set gutterSize(value) {\n this._gutter = `${value == null ? '' : value}`;\n }\n /** Set internal representation of row height from the user-provided value. */\n get rowHeight() {\n return this._rowHeight;\n }\n set rowHeight(value) {\n const newValue = `${value == null ? '' : value}`;\n if (newValue !== this._rowHeight) {\n this._rowHeight = newValue;\n this._setTileStyler(this._rowHeight);\n }\n }\n ngOnInit() {\n this._checkCols();\n this._checkRowHeight();\n }\n /**\n * The layout calculation is fairly cheap if nothing changes, so there's little cost\n * to run it frequently.\n */\n ngAfterContentChecked() {\n this._layoutTiles();\n }\n /** Throw a friendly error if cols property is missing */\n _checkCols() {\n if (!this.cols && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n throw Error(`mat-grid-list: must pass in number of columns. ` + `Example: `);\n }\n }\n /** Default to equal width:height if rowHeight property is missing */\n _checkRowHeight() {\n if (!this._rowHeight) {\n this._setTileStyler('1:1');\n }\n }\n /** Creates correct Tile Styler subtype based on rowHeight passed in by user */\n _setTileStyler(rowHeight) {\n if (this._tileStyler) {\n this._tileStyler.reset(this);\n }\n if (rowHeight === MAT_FIT_MODE) {\n this._tileStyler = new FitTileStyler();\n } else if (rowHeight && rowHeight.indexOf(':') > -1) {\n this._tileStyler = new RatioTileStyler(rowHeight);\n } else {\n this._tileStyler = new FixedTileStyler(rowHeight);\n }\n }\n /** Computes and applies the size and position for all children grid tiles. */\n _layoutTiles() {\n if (!this._tileCoordinator) {\n this._tileCoordinator = new TileCoordinator();\n }\n const tracker = this._tileCoordinator;\n const tiles = this._tiles.filter(tile => !tile._gridList || tile._gridList === this);\n const direction = this._dir ? this._dir.value : 'ltr';\n this._tileCoordinator.update(this.cols, tiles);\n this._tileStyler.init(this.gutterSize, tracker, this.cols, direction);\n tiles.forEach((tile, index) => {\n const pos = tracker.positions[index];\n this._tileStyler.setStyle(tile, pos.row, pos.col);\n });\n this._setListStyle(this._tileStyler.getComputedHeight());\n }\n /** Sets style on the main grid-list element, given the style name and value. */\n _setListStyle(style) {\n if (style) {\n this._element.nativeElement.style[style[0]] = style[1];\n }\n }\n static {\n this.ɵfac = function MatGridList_Factory(t) {\n return new (t || MatGridList)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.Directionality, 8));\n };\n }\n static {\n this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: MatGridList,\n selectors: [[\"mat-grid-list\"]],\n contentQueries: function MatGridList_ContentQueries(rf, ctx, dirIndex) {\n if (rf & 1) {\n i0.ɵɵcontentQuery(dirIndex, MatGridTile, 5);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx._tiles = _t);\n }\n },\n hostAttrs: [1, \"mat-grid-list\"],\n hostVars: 1,\n hostBindings: function MatGridList_HostBindings(rf, ctx) {\n if (rf & 2) {\n i0.ɵɵattribute(\"cols\", ctx.cols);\n }\n },\n inputs: {\n cols: \"cols\",\n gutterSize: \"gutterSize\",\n rowHeight: \"rowHeight\"\n },\n exportAs: [\"matGridList\"],\n standalone: true,\n features: [i0.ɵɵProvidersFeature([{\n provide: MAT_GRID_LIST,\n useExisting: MatGridList\n }]), i0.ɵɵStandaloneFeature],\n ngContentSelectors: _c0,\n decls: 2,\n vars: 0,\n template: function MatGridList_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵprojectionDef();\n i0.ɵɵelementStart(0, \"div\");\n i0.ɵɵprojection(1);\n i0.ɵɵelementEnd();\n }\n },\n styles: [_c3],\n encapsulation: 2,\n changeDetection: 0\n });\n }\n }\n return MatGridList;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet MatGridListModule = /*#__PURE__*/(() => {\n class MatGridListModule {\n static {\n this.ɵfac = function MatGridListModule_Factory(t) {\n return new (t || MatGridListModule)();\n };\n }\n static {\n this.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: MatGridListModule\n });\n }\n static {\n this.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n imports: [MatLineModule, MatCommonModule, MatLineModule, MatCommonModule]\n });\n }\n }\n return MatGridListModule;\n})();\n/*#__PURE__*/(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n// Privately exported for the grid-list harness.\nconst ɵTileCoordinator = TileCoordinator;\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { MatGridAvatarCssMatStyler, MatGridList, MatGridListModule, MatGridTile, MatGridTileFooterCssMatStyler, MatGridTileHeaderCssMatStyler, MatGridTileText, ɵTileCoordinator };\n","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({ name: 'glxyFrequency' })\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 { 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})\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() allowCustomStyling = false;\n Frequency = Frequency;\n Form = Form;\n}\n","
\n \n {{ value }}\n
\n
\n  {{ currencyCode }}\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","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","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(t) {\n return new (t || 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(t) {\n return new (t || 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 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(t) {\n return new (t || 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(t) {\n return new (t || 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(t) {\n return new (t || 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(t) {\n return new (t || 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(t) {\n return new (t || 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 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(t) {\n return new (t || 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 { DiscountType as ApiDiscountType } from '@vendasta/billing';\n\nexport enum DiscountType {\n FIXED_AMOUNT = 'FIXED_AMOUNT',\n PERCENT_AMOUNT = 'PERCENT_AMOUNT',\n FIXED_NUMBER_OF_UNITS = 'FIXED_NUMBER_OF_UNITS',\n FIXED_AMOUNT_PER_UNIT = 'FIXED_AMOUNT_PER_UNIT',\n}\n\nexport function discountTypeFromApi(t: ApiDiscountType): DiscountType {\n switch (t) {\n case ApiDiscountType.DISCOUNT_TYPE_FIXED_AMOUNT_PER_UNIT:\n return DiscountType.FIXED_AMOUNT_PER_UNIT;\n case ApiDiscountType.DISCOUNT_TYPE_FIXED_NUMBER_OF_UNITS:\n return DiscountType.FIXED_NUMBER_OF_UNITS;\n case ApiDiscountType.DISCOUNT_TYPE_PERCENT_AMOUNT:\n return DiscountType.PERCENT_AMOUNT;\n }\n return DiscountType.FIXED_AMOUNT;\n}\n\nexport function discountTypeToApi(t: DiscountType): ApiDiscountType {\n switch (t) {\n case DiscountType.FIXED_AMOUNT_PER_UNIT:\n return ApiDiscountType.DISCOUNT_TYPE_FIXED_AMOUNT_PER_UNIT;\n case DiscountType.FIXED_NUMBER_OF_UNITS:\n return ApiDiscountType.DISCOUNT_TYPE_FIXED_NUMBER_OF_UNITS;\n case DiscountType.PERCENT_AMOUNT:\n return ApiDiscountType.DISCOUNT_TYPE_PERCENT_AMOUNT;\n }\n return ApiDiscountType.DISCOUNT_TYPE_FIXED_AMOUNT;\n}\n","import { Consumer, Discount as DiscountApi } from '@vendasta/billing';\nimport { discountTypeFromApi, DiscountType } from './discount_type';\n\nexport interface Discount {\n discountId: number;\n id: string;\n discountType: DiscountType;\n amount: number;\n start?: Date;\n end?: Date;\n resetEachPeriod: boolean;\n merchantId: string;\n created?: Date;\n updated?: Date;\n SKU: string;\n description: string;\n customerId?: string;\n consumer?: Consumer;\n subscriptionId?: string;\n numberOfBillingPeriods: number;\n}\n\nexport function discountFromApi(apiDiscount: DiscountApi): Discount {\n return {\n merchantId: apiDiscount.merchantId || '',\n discountId: apiDiscount.discountId || 0,\n id: apiDiscount.id || '',\n discountType: discountTypeFromApi(apiDiscount.discountType),\n amount: apiDiscount.amount || 0,\n start: apiDiscount.start ? new Date(apiDiscount.start) : undefined,\n end: apiDiscount.end ? new Date(apiDiscount.end) : undefined,\n resetEachPeriod: apiDiscount.resetEachPeriod || false,\n created: apiDiscount.created ? new Date(apiDiscount.created) : undefined,\n updated: apiDiscount.updated ? new Date(apiDiscount.updated) : undefined,\n SKU: apiDiscount.sku || '',\n description: apiDiscount.description || '',\n customerId: apiDiscount.customerId || '',\n consumer: apiDiscount.consumer || Consumer.CONSUMER_RETAIL,\n subscriptionId: apiDiscount.subscriptionId || '',\n numberOfBillingPeriods: apiDiscount.numberOfBillingPeriods ?? 0,\n };\n}\n","import { Consumer } from '@vendasta/billing';\n\nexport enum DiscountConsumer {\n RETAIL = 'retail',\n WHOLESALE = 'reseller',\n}\n\nexport function discountConsumerToApi(dc: DiscountConsumer): Consumer {\n switch (dc) {\n case DiscountConsumer.RETAIL:\n return Consumer.CONSUMER_RETAIL;\n case DiscountConsumer.WHOLESALE:\n return Consumer.CONSUMER_WHOLESALE;\n }\n}\n","import { HttpResponse } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { PagedResponse } from '../core';\nimport {\n CreateDiscountRequest,\n DiscountApiService,\n ExpireDiscountRequest,\n ListDiscountsRequest,\n PagedRequestOptions,\n UpdateDiscountRequest,\n} from '@vendasta/billing';\nimport { Discount, discountFromApi } from './discount';\nimport { DiscountType, discountTypeToApi } from './discount_type';\nimport { ListDiscountsFilter } from './list-discount';\nimport { DiscountConsumer, discountConsumerToApi } from './discount_consumer';\n\n@Injectable({ providedIn: 'root' })\nexport class DiscountService {\n constructor(private discountApi: DiscountApiService) {}\n\n list(filters: ListDiscountsFilter, cursor: string, pageSize: number): Observable> {\n const req = new ListDiscountsRequest({\n filters: filters,\n pagingOptions: new PagedRequestOptions({ cursor, pageSize }),\n });\n return this.discountApi.list(req).pipe(\n map((response) => {\n const discounts = (response.discounts || []).map((item) => discountFromApi(item));\n return new PagedResponse(discounts, response.pagingMetadata.nextCursor, response.pagingMetadata.hasMore);\n }),\n );\n }\n\n create(\n merchantId: string,\n sku: string,\n discountType: DiscountType,\n amount: number,\n startDateTime: string,\n endDateTime?: string,\n resetEachPeriodFlag?: boolean,\n description?: string,\n customerId?: string,\n discountConsumer = DiscountConsumer.WHOLESALE,\n subscriptionId?: string,\n numberOfBillingPeriods?: number,\n ): Observable {\n const start = new Date(startDateTime);\n const dt = discountTypeToApi(discountType);\n const consumer = discountConsumerToApi(discountConsumer);\n\n const r = new CreateDiscountRequest({\n merchantId: merchantId,\n sku: sku,\n discountType: dt,\n amount: amount,\n start: start,\n resetEachPeriod: resetEachPeriodFlag,\n description: description,\n customerId: customerId,\n consumer: consumer,\n subscriptionId: subscriptionId,\n numberOfBillingPeriods: numberOfBillingPeriods ?? 0,\n });\n\n if (endDateTime && endDateTime !== '') {\n r.end = new Date(endDateTime);\n }\n\n return this.discountApi.create(r).pipe(map((resp) => resp.id));\n }\n\n update(\n merchantId: string,\n id: string,\n discountType: DiscountType,\n amount: number,\n startDateTime: string,\n endDateTime?: string,\n resetEachPeriodFlag?: boolean,\n description?: string,\n customerId?: string,\n subscriptionId?: string,\n numberOfBillingPeriods?: number,\n ): Observable> {\n const start = new Date(startDateTime);\n const dt = discountTypeToApi(discountType);\n\n const r = new UpdateDiscountRequest({\n merchantId: merchantId,\n id: id,\n discountType: dt,\n amount: amount,\n start: start,\n resetEachPeriod: resetEachPeriodFlag,\n description: description,\n customerId: customerId,\n subscriptionId: subscriptionId,\n numberOfBillingPeriods: numberOfBillingPeriods ?? 0,\n });\n\n if (endDateTime && endDateTime !== '') {\n r.end = new Date(endDateTime);\n }\n return this.discountApi.update(r);\n }\n\n expire(merchantId: string, id: string): Observable> {\n return this.discountApi.expire(\n new ExpireDiscountRequest({\n merchantId: merchantId,\n id: id,\n }),\n );\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, NgFor, 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 standalone: true,\n imports: [\n CommonModule,\n MatDialogModule,\n MatButtonModule,\n GalaxyFormFieldModule,\n ReactiveFormsModule,\n TranslationModule,\n MatInputModule,\n NgIf,\n NgFor,\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 = 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}\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