@posiwise/admin-module 0.0.203 → 0.0.205
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -68,13 +68,13 @@ import * as i15$1 from '@angular/cdk/drag-drop';
|
|
|
68
68
|
import { moveItemInArray } from '@angular/cdk/drag-drop';
|
|
69
69
|
import groupBy from 'lodash/groupBy';
|
|
70
70
|
import { forkJoin, map, Subject, takeUntil, of } from 'rxjs';
|
|
71
|
+
import { finalize, debounceTime, switchMap, filter, take, concatMap, map as map$1, catchError } from 'rxjs/operators';
|
|
71
72
|
import * as i3$3 from '@angular/platform-browser';
|
|
72
73
|
import * as i4$3 from 'primeng/tabs';
|
|
73
74
|
import uniqBy from 'lodash/uniqBy';
|
|
74
75
|
import moment from 'moment';
|
|
75
76
|
import { StatusCodes } from 'http-status-codes';
|
|
76
77
|
import { HttpParams } from '@angular/common/http';
|
|
77
|
-
import { debounceTime, switchMap, filter, take, concatMap, map as map$1, catchError } from 'rxjs/operators';
|
|
78
78
|
|
|
79
79
|
class GlobalConfigDetailsComponent extends AppBaseComponent {
|
|
80
80
|
constructor(adminService, injector, cdr) {
|
|
@@ -172,6 +172,9 @@ const ROUTERS = {
|
|
|
172
172
|
loginNotifications: `${baseUrl}/login-notifications/`,
|
|
173
173
|
incidentConfig: `${baseUrl}/incident/list/`,
|
|
174
174
|
incidentDetails: `${baseUrl}/incident/`,
|
|
175
|
+
featureFlagsConfig: `${baseUrl}/feature-flags/list/`,
|
|
176
|
+
featureFlagsDetails: `${baseUrl}/feature-flags/`,
|
|
177
|
+
featureFlagRules: `${baseUrl}/feature-flags/rules/`,
|
|
175
178
|
faqConfig: `${baseUrl}/faq/list/`,
|
|
176
179
|
faqDetails: `${baseUrl}/faq/`,
|
|
177
180
|
inviteUsers: `${baseUrl}/subscriptions/invite-user/`,
|
|
@@ -352,11 +355,11 @@ class ContactUsListComponent extends AppBaseComponent {
|
|
|
352
355
|
super.ngOnDestroy();
|
|
353
356
|
}
|
|
354
357
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: ContactUsListComponent, deps: [{ token: i0.Injector }, { token: i1.AdminService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
355
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: ContactUsListComponent, isStandalone: false, selector: "pw-contact-us-list", usesInheritance: true, ngImport: i0, template: "<div class=\"row\">\n <div class=\"col-12 d-flex justify-content-between align-items-center text-end\">\n <h2 class=\"card-title p-0 float-start\">Contact Us</h2>\n </div>\n <div class=\"col-12 mb-3\">\n <p>The <strong>Contact US</strong> module shows all submissions from the contact forms embedded on select landing pages.<br/>\n It\u2019s your central hub to track and follow up with interested users or potential leads.</p>\n</div>\n</div>\n@if (!isLoaded) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"> </p-progressSpinner>\n </div>\n}\n<div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"data.unfiltered_count === 0\">\n <p-table #tt\n [value]=\"data.contact_request\"\n [paginator]=\"data.object_count !== 0\"\n [rows]=\"PAGE_SIZE\"\n [totalRecords]=\"data.object_count\"\n [loading]=\"loading\"\n [lazy]=\"true\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <!--sort order should be default empty when page load-->\n\n <!-- Caption Header, Search Bar -->\n <ng-template pTemplate=\"caption\">\n <div class=\"search-contact\">\n <div class=\"col-12 col-sm-3\">\n <p-select\n [options]=\"[\n { label: 'Select status', value: null },\n { label: 'Open', value: false },\n { label: 'Closed', value: true }\n ]\"\n placeholder=\"Select status\"\n (onChange)=\"filterClosed($event.value)\">\n </p-select>\n </div>\n <div class=\"text-end ms-auto\">\n <i class=\"fa fa-search mt-2 me-2\" aria-hidden=\"true\"></i>\n <label for=\"contact-us-list-search\" class=\"visually-hidden\">Search</label>\n <input type=\"text\"\n id=\"contact-us-list-search\"\n name=\"contact-us-list-search\"\n pInputText\n size=\"50\"\n placeholder=\"Search\"\n [(ngModel)]=\"searchText\"\n (input)=\"tt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n\n <!-- Column Header -->\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"created_at\">\n {{ 'Admin.ContactUs.CreatedAt' | transloco }}\n <p-sortIcon field=\"created_at\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"name\">\n {{ 'Label.Name' | transloco }}\n <p-sortIcon field=\"name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"email\">\n {{ 'Label.Email' | transloco }}\n <p-sortIcon field=\"email\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"subject\">\n {{ 'Admin.ContactUs.Subject' | transloco }}\n <p-sortIcon field=\"subject\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"message\">\n {{ 'Admin.ContactUs.Message' | transloco }}\n <p-sortIcon field=\"message\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n\n <!-- Table Body -->\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"created_at\">{{ item?.created_at | dateFormat }}</td>\n <td data-head=\"name\">{{ item?.name }}</td>\n <td data-head=\"email\">\n <a href=\"mailto:{{ item?.email }}\">{{ item?.email }}</a>\n </td>\n <td data-head=\"subject\">{{ item?.subject }}</td>\n <td data-head=\"message\">\n <span\n [ngbTooltip]=\"item?.message\"\n container=\"body\"\n tooltipClass=\"custom-tooltip\"\n placement=\"bottom\"\n >\n {{ item?.message | textTruncate: 200 }}\n </span>\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n @if (!item.closed) {\n <li\n ngbTooltip=\"Close\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"toggleRecord(item.id, true)\"\n (click)=\"toggleRecord(item.id, true)\">\n <i class=\"fa fa-times delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n @if (item.closed) {\n <li\n ngbTooltip=\"Re Open\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"onKeyDown($event, item.id)\"\n (click)=\"toggleRecord(item.id, false)\">\n <i class=\"fa fa-retweet edit-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (data.object_count === 0 && data.unfiltered_count !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (data.object_count !== 0) {\n <span class=\"total-records-count\" >Total: {{ data.object_count }}</span>\n }\n</div>\n@if (data.unfiltered_count === 0 && isLoaded) {\n <div>\n <pw-no-data [withImage]=\"true\" message=\"No Contact Requests\"> </pw-no-data>\n </div>\n}\n", styles: [".search-contact{display:flex;justify-content:space-between}@media(min-width:320px)and (max-width:720px){.search-contact{display:block}}\n"], dependencies: [{ kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i3$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i2.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i2$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.NoDataComponent, selector: "pw-no-data", inputs: ["message", "description", "withImage"] }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }, { kind: "pipe", type: i15.DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: i15.TextTruncatePipe, name: "textTruncate" }] }); }
|
|
358
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: ContactUsListComponent, isStandalone: false, selector: "pw-contact-us-list", usesInheritance: true, ngImport: i0, template: "<div class=\"row\">\n <div class=\"col-12 d-flex justify-content-between align-items-center text-end\">\n <h2 class=\"card-title p-0 float-start\">Contact Us</h2>\n </div>\n <div class=\"col-12 mb-3\">\n <p>The <strong>Contact US</strong> module shows all submissions from the contact forms embedded on select landing pages.<br/>\n It\u2019s your central hub to track and follow up with interested users or potential leads.</p>\n</div>\n</div>\n@if (!isLoaded) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"> </p-progressSpinner>\n </div>\n}\n<div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"data.unfiltered_count === 0\">\n <p-table #tt\n [value]=\"data.contact_request\"\n [paginator]=\"data.object_count !== 0\"\n [rows]=\"PAGE_SIZE\"\n [totalRecords]=\"data.object_count\"\n [loading]=\"loading\"\n [lazy]=\"true\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <!--sort order should be default empty when page load-->\n\n <!-- Caption Header, Search Bar -->\n <ng-template pTemplate=\"caption\">\n <div class=\"search-contact\">\n <div class=\"col-12 col-sm-3\">\n <p-select\n [options]=\"[\n { label: 'Select status', value: null },\n { label: 'Open', value: false },\n { label: 'Closed', value: true }\n ]\"\n placeholder=\"Select status\"\n (onChange)=\"filterClosed($event.value)\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"text-end ms-auto\">\n <i class=\"fa fa-search mt-2 me-2\" aria-hidden=\"true\"></i>\n <label for=\"contact-us-list-search\" class=\"visually-hidden\">Search</label>\n <input type=\"text\"\n id=\"contact-us-list-search\"\n name=\"contact-us-list-search\"\n pInputText\n size=\"50\"\n placeholder=\"Search\"\n [(ngModel)]=\"searchText\"\n (input)=\"tt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n\n <!-- Column Header -->\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"created_at\">\n {{ 'Admin.ContactUs.CreatedAt' | transloco }}\n <p-sortIcon field=\"created_at\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"name\">\n {{ 'Label.Name' | transloco }}\n <p-sortIcon field=\"name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"email\">\n {{ 'Label.Email' | transloco }}\n <p-sortIcon field=\"email\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"subject\">\n {{ 'Admin.ContactUs.Subject' | transloco }}\n <p-sortIcon field=\"subject\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"message\">\n {{ 'Admin.ContactUs.Message' | transloco }}\n <p-sortIcon field=\"message\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n\n <!-- Table Body -->\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"created_at\">{{ item?.created_at | dateFormat }}</td>\n <td data-head=\"name\">{{ item?.name }}</td>\n <td data-head=\"email\">\n <a href=\"mailto:{{ item?.email }}\">{{ item?.email }}</a>\n </td>\n <td data-head=\"subject\">{{ item?.subject }}</td>\n <td data-head=\"message\">\n <span\n [ngbTooltip]=\"item?.message\"\n container=\"body\"\n tooltipClass=\"custom-tooltip\"\n placement=\"bottom\"\n >\n {{ item?.message | textTruncate: 200 }}\n </span>\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n @if (!item.closed) {\n <li\n ngbTooltip=\"Close\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"toggleRecord(item.id, true)\"\n (click)=\"toggleRecord(item.id, true)\">\n <i class=\"fa fa-times delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n @if (item.closed) {\n <li\n ngbTooltip=\"Re Open\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"onKeyDown($event, item.id)\"\n (click)=\"toggleRecord(item.id, false)\">\n <i class=\"fa fa-retweet edit-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (data.object_count === 0 && data.unfiltered_count !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (data.object_count !== 0) {\n <span class=\"total-records-count\" >Total: {{ data.object_count }}</span>\n }\n</div>\n@if (data.unfiltered_count === 0 && isLoaded) {\n <div>\n <pw-no-data [withImage]=\"true\" message=\"No Contact Requests\"> </pw-no-data>\n </div>\n}\n", styles: [".search-contact{display:flex;justify-content:space-between}@media(min-width:320px)and (max-width:720px){.search-contact{display:block}}\n"], dependencies: [{ kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i3$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i2.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i2$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.NoDataComponent, selector: "pw-no-data", inputs: ["message", "description", "withImage"] }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }, { kind: "pipe", type: i15.DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: i15.TextTruncatePipe, name: "textTruncate" }] }); }
|
|
356
359
|
}
|
|
357
360
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: ContactUsListComponent, decorators: [{
|
|
358
361
|
type: Component,
|
|
359
|
-
args: [{ selector: 'pw-contact-us-list', standalone: false, template: "<div class=\"row\">\n <div class=\"col-12 d-flex justify-content-between align-items-center text-end\">\n <h2 class=\"card-title p-0 float-start\">Contact Us</h2>\n </div>\n <div class=\"col-12 mb-3\">\n <p>The <strong>Contact US</strong> module shows all submissions from the contact forms embedded on select landing pages.<br/>\n It\u2019s your central hub to track and follow up with interested users or potential leads.</p>\n</div>\n</div>\n@if (!isLoaded) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"> </p-progressSpinner>\n </div>\n}\n<div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"data.unfiltered_count === 0\">\n <p-table #tt\n [value]=\"data.contact_request\"\n [paginator]=\"data.object_count !== 0\"\n [rows]=\"PAGE_SIZE\"\n [totalRecords]=\"data.object_count\"\n [loading]=\"loading\"\n [lazy]=\"true\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <!--sort order should be default empty when page load-->\n\n <!-- Caption Header, Search Bar -->\n <ng-template pTemplate=\"caption\">\n <div class=\"search-contact\">\n <div class=\"col-12 col-sm-3\">\n <p-select\n [options]=\"[\n { label: 'Select status', value: null },\n { label: 'Open', value: false },\n { label: 'Closed', value: true }\n ]\"\n placeholder=\"Select status\"\n (onChange)=\"filterClosed($event.value)\">\n </p-select>\n </div>\n <div class=\"text-end ms-auto\">\n <i class=\"fa fa-search mt-2 me-2\" aria-hidden=\"true\"></i>\n <label for=\"contact-us-list-search\" class=\"visually-hidden\">Search</label>\n <input type=\"text\"\n id=\"contact-us-list-search\"\n name=\"contact-us-list-search\"\n pInputText\n size=\"50\"\n placeholder=\"Search\"\n [(ngModel)]=\"searchText\"\n (input)=\"tt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n\n <!-- Column Header -->\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"created_at\">\n {{ 'Admin.ContactUs.CreatedAt' | transloco }}\n <p-sortIcon field=\"created_at\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"name\">\n {{ 'Label.Name' | transloco }}\n <p-sortIcon field=\"name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"email\">\n {{ 'Label.Email' | transloco }}\n <p-sortIcon field=\"email\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"subject\">\n {{ 'Admin.ContactUs.Subject' | transloco }}\n <p-sortIcon field=\"subject\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"message\">\n {{ 'Admin.ContactUs.Message' | transloco }}\n <p-sortIcon field=\"message\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n\n <!-- Table Body -->\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"created_at\">{{ item?.created_at | dateFormat }}</td>\n <td data-head=\"name\">{{ item?.name }}</td>\n <td data-head=\"email\">\n <a href=\"mailto:{{ item?.email }}\">{{ item?.email }}</a>\n </td>\n <td data-head=\"subject\">{{ item?.subject }}</td>\n <td data-head=\"message\">\n <span\n [ngbTooltip]=\"item?.message\"\n container=\"body\"\n tooltipClass=\"custom-tooltip\"\n placement=\"bottom\"\n >\n {{ item?.message | textTruncate: 200 }}\n </span>\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n @if (!item.closed) {\n <li\n ngbTooltip=\"Close\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"toggleRecord(item.id, true)\"\n (click)=\"toggleRecord(item.id, true)\">\n <i class=\"fa fa-times delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n @if (item.closed) {\n <li\n ngbTooltip=\"Re Open\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"onKeyDown($event, item.id)\"\n (click)=\"toggleRecord(item.id, false)\">\n <i class=\"fa fa-retweet edit-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (data.object_count === 0 && data.unfiltered_count !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (data.object_count !== 0) {\n <span class=\"total-records-count\" >Total: {{ data.object_count }}</span>\n }\n</div>\n@if (data.unfiltered_count === 0 && isLoaded) {\n <div>\n <pw-no-data [withImage]=\"true\" message=\"No Contact Requests\"> </pw-no-data>\n </div>\n}\n", styles: [".search-contact{display:flex;justify-content:space-between}@media(min-width:320px)and (max-width:720px){.search-contact{display:block}}\n"] }]
|
|
362
|
+
args: [{ selector: 'pw-contact-us-list', standalone: false, template: "<div class=\"row\">\n <div class=\"col-12 d-flex justify-content-between align-items-center text-end\">\n <h2 class=\"card-title p-0 float-start\">Contact Us</h2>\n </div>\n <div class=\"col-12 mb-3\">\n <p>The <strong>Contact US</strong> module shows all submissions from the contact forms embedded on select landing pages.<br/>\n It\u2019s your central hub to track and follow up with interested users or potential leads.</p>\n</div>\n</div>\n@if (!isLoaded) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"> </p-progressSpinner>\n </div>\n}\n<div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"data.unfiltered_count === 0\">\n <p-table #tt\n [value]=\"data.contact_request\"\n [paginator]=\"data.object_count !== 0\"\n [rows]=\"PAGE_SIZE\"\n [totalRecords]=\"data.object_count\"\n [loading]=\"loading\"\n [lazy]=\"true\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <!--sort order should be default empty when page load-->\n\n <!-- Caption Header, Search Bar -->\n <ng-template pTemplate=\"caption\">\n <div class=\"search-contact\">\n <div class=\"col-12 col-sm-3\">\n <p-select\n [options]=\"[\n { label: 'Select status', value: null },\n { label: 'Open', value: false },\n { label: 'Closed', value: true }\n ]\"\n placeholder=\"Select status\"\n (onChange)=\"filterClosed($event.value)\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"text-end ms-auto\">\n <i class=\"fa fa-search mt-2 me-2\" aria-hidden=\"true\"></i>\n <label for=\"contact-us-list-search\" class=\"visually-hidden\">Search</label>\n <input type=\"text\"\n id=\"contact-us-list-search\"\n name=\"contact-us-list-search\"\n pInputText\n size=\"50\"\n placeholder=\"Search\"\n [(ngModel)]=\"searchText\"\n (input)=\"tt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n\n <!-- Column Header -->\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"created_at\">\n {{ 'Admin.ContactUs.CreatedAt' | transloco }}\n <p-sortIcon field=\"created_at\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"name\">\n {{ 'Label.Name' | transloco }}\n <p-sortIcon field=\"name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"email\">\n {{ 'Label.Email' | transloco }}\n <p-sortIcon field=\"email\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"subject\">\n {{ 'Admin.ContactUs.Subject' | transloco }}\n <p-sortIcon field=\"subject\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"message\">\n {{ 'Admin.ContactUs.Message' | transloco }}\n <p-sortIcon field=\"message\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n\n <!-- Table Body -->\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"created_at\">{{ item?.created_at | dateFormat }}</td>\n <td data-head=\"name\">{{ item?.name }}</td>\n <td data-head=\"email\">\n <a href=\"mailto:{{ item?.email }}\">{{ item?.email }}</a>\n </td>\n <td data-head=\"subject\">{{ item?.subject }}</td>\n <td data-head=\"message\">\n <span\n [ngbTooltip]=\"item?.message\"\n container=\"body\"\n tooltipClass=\"custom-tooltip\"\n placement=\"bottom\"\n >\n {{ item?.message | textTruncate: 200 }}\n </span>\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n @if (!item.closed) {\n <li\n ngbTooltip=\"Close\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"toggleRecord(item.id, true)\"\n (click)=\"toggleRecord(item.id, true)\">\n <i class=\"fa fa-times delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n @if (item.closed) {\n <li\n ngbTooltip=\"Re Open\"\n class=\"me-2 me-sm-3\"\n (keydown.enter)=\"onKeyDown($event, item.id)\"\n (click)=\"toggleRecord(item.id, false)\">\n <i class=\"fa fa-retweet edit-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (data.object_count === 0 && data.unfiltered_count !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (data.object_count !== 0) {\n <span class=\"total-records-count\" >Total: {{ data.object_count }}</span>\n }\n</div>\n@if (data.unfiltered_count === 0 && isLoaded) {\n <div>\n <pw-no-data [withImage]=\"true\" message=\"No Contact Requests\"> </pw-no-data>\n </div>\n}\n", styles: [".search-contact{display:flex;justify-content:space-between}@media(min-width:320px)and (max-width:720px){.search-contact{display:block}}\n"] }]
|
|
360
363
|
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1.AdminService }, { type: i0.ChangeDetectorRef }] });
|
|
361
364
|
|
|
362
365
|
class ContactUsTabsComponent {
|
|
@@ -2713,6 +2716,388 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImpor
|
|
|
2713
2716
|
}]
|
|
2714
2717
|
}] });
|
|
2715
2718
|
|
|
2719
|
+
class FeatureFlagDetailsComponent extends AppBaseComponent {
|
|
2720
|
+
constructor(injector, adminService) {
|
|
2721
|
+
super(injector);
|
|
2722
|
+
this.adminService = adminService;
|
|
2723
|
+
this.form = AppAdmin.getFeatureFlagForm();
|
|
2724
|
+
this.routers = ROUTERS;
|
|
2725
|
+
this.id = 0;
|
|
2726
|
+
this.isLoading = true;
|
|
2727
|
+
this.submitted = false;
|
|
2728
|
+
this.buttonBusy = false;
|
|
2729
|
+
this.flagTypeOptions = [];
|
|
2730
|
+
this.statusOptions = [];
|
|
2731
|
+
this.allUsers = [];
|
|
2732
|
+
this.filteredUsers = [];
|
|
2733
|
+
}
|
|
2734
|
+
ngOnInit() {
|
|
2735
|
+
this.subscriptionId = PermissionService.selectedSubscription?.id;
|
|
2736
|
+
this.loadSupportedOptions();
|
|
2737
|
+
this.loadOwners();
|
|
2738
|
+
this.route.params.subscribe(params => {
|
|
2739
|
+
this.id = +params['id'] || 0;
|
|
2740
|
+
if (this.id) {
|
|
2741
|
+
this.getFeatureFlag();
|
|
2742
|
+
}
|
|
2743
|
+
else {
|
|
2744
|
+
this.isLoading = false;
|
|
2745
|
+
}
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
loadSupportedOptions() {
|
|
2749
|
+
this.adminService.getSupportedFeatureFlagOptions().subscribe(response => {
|
|
2750
|
+
this.flagTypeOptions = (response.flag_types ?? []).map(option => ({
|
|
2751
|
+
label: option,
|
|
2752
|
+
value: option
|
|
2753
|
+
}));
|
|
2754
|
+
this.statusOptions = (response.statuses ?? []).map(option => ({
|
|
2755
|
+
label: option,
|
|
2756
|
+
value: option
|
|
2757
|
+
}));
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
loadOwners() {
|
|
2761
|
+
this.adminService
|
|
2762
|
+
.getUsers({}, { subscription_id: this.subscriptionId })
|
|
2763
|
+
.subscribe(response => {
|
|
2764
|
+
this.allUsers = response.users ?? [];
|
|
2765
|
+
this.allUsers.forEach(user => {
|
|
2766
|
+
const firstName = user.first_name ?? '';
|
|
2767
|
+
const lastName = user.last_name ?? '';
|
|
2768
|
+
user.displayName = `${firstName} ${lastName} ${user.email}`.trim();
|
|
2769
|
+
});
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
searchOwners(event) {
|
|
2773
|
+
const query = event.query?.toLowerCase() ?? '';
|
|
2774
|
+
this.filteredUsers = !query
|
|
2775
|
+
? [...this.allUsers]
|
|
2776
|
+
: this.allUsers.filter(user => user.displayName.toLowerCase().includes(query));
|
|
2777
|
+
}
|
|
2778
|
+
get f() {
|
|
2779
|
+
return this.form.controls;
|
|
2780
|
+
}
|
|
2781
|
+
getFeatureFlag() {
|
|
2782
|
+
this.adminService.getFeatureFlagById(this.id).subscribe({
|
|
2783
|
+
next: response => {
|
|
2784
|
+
this.form.patchValue({
|
|
2785
|
+
id: response.id,
|
|
2786
|
+
key: response.key,
|
|
2787
|
+
name: response.name,
|
|
2788
|
+
description: response.description,
|
|
2789
|
+
flag_type: response.flag_type,
|
|
2790
|
+
status: response.status,
|
|
2791
|
+
owner: this.allUsers.find(user => user.email === response.owner) ?? response.owner,
|
|
2792
|
+
expires_at: response.expires_at ? new Date(response.expires_at) : null
|
|
2793
|
+
});
|
|
2794
|
+
},
|
|
2795
|
+
complete: () => {
|
|
2796
|
+
this.isLoading = false;
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
}
|
|
2800
|
+
onSubmit() {
|
|
2801
|
+
this.form.markAllAsTouched();
|
|
2802
|
+
this.submitted = true;
|
|
2803
|
+
if (!this.form.valid) {
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
this.buttonBusy = true;
|
|
2807
|
+
const formValue = this.form.value;
|
|
2808
|
+
const payload = {
|
|
2809
|
+
...formValue,
|
|
2810
|
+
owner: typeof formValue.owner === 'string' ? formValue.owner : formValue.owner?.email
|
|
2811
|
+
};
|
|
2812
|
+
const request$ = this.id
|
|
2813
|
+
? this.adminService.updateFeatureFlag(this.id, payload)
|
|
2814
|
+
: this.adminService.createFeatureFlag(payload);
|
|
2815
|
+
request$.subscribe({
|
|
2816
|
+
next: () => {
|
|
2817
|
+
this.toast.success(this.id
|
|
2818
|
+
? 'Feature flag updated successfully.'
|
|
2819
|
+
: 'Feature flag created successfully.');
|
|
2820
|
+
this.router.navigate([this.routers.featureFlagsConfig]);
|
|
2821
|
+
},
|
|
2822
|
+
complete: () => {
|
|
2823
|
+
this.buttonBusy = false;
|
|
2824
|
+
}
|
|
2825
|
+
});
|
|
2826
|
+
}
|
|
2827
|
+
onCancel() {
|
|
2828
|
+
this.router.navigate([this.routers.featureFlagsConfig]);
|
|
2829
|
+
}
|
|
2830
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagDetailsComponent, deps: [{ token: i0.Injector }, { token: i1.AdminService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2831
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FeatureFlagDetailsComponent, isStandalone: false, selector: "pw-feature-flag-details", usesInheritance: true, ngImport: i0, template: "<div class=\"container-fluid pw-tab\">\n <div class=\"dashboard\">\n <div class=\"dashboard-body\">\n <div class=\"me-auto col-xs-6 mt-4\">\n <a routerLink=\"/admin/feature-flags/list\" class=\"previous\" aria-label=\"Back to feature flags list\">\n <i class=\"fa fa-arrow-alt-circle-left\" aria-hidden=\"true\"></i>\n </a>\n <h3 class=\"mt-3\">{{ id ? 'Edit' : 'Add New' }} Feature Flag</h3>\n </div>\n\n @if (isLoading) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"></p-progressSpinner>\n </div>\n }\n\n <div class=\"p-2 mt-3\">\n @if (!isLoading) {\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n <div class=\"row\">\n <pw-input-container class=\"col-lg-6\" label=\"Key\" name=\"key\" controlId=\"feature-flag-key\" errorMsg=\"Key is required\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.Key' | transloco\">\n <input type=\"text\" id=\"feature-flag-key\" formControlName=\"key\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': submitted && f['key'].errors }\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-6\" label=\"Name\" name=\"name\" controlId=\"feature-flag-name\" errorMsg=\"Name is required\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.Name' | transloco\">\n <input type=\"text\" id=\"feature-flag-name\" formControlName=\"name\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': submitted && f['name'].errors }\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-12\" label=\"Description\" name=\"description\" controlId=\"feature-flag-description\">\n <textarea id=\"feature-flag-description\" formControlName=\"description\" rows=\"4\" class=\"form-control\"></textarea>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Type\" name=\"flag_type\" controlId=\"feature-flag-type\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"flagTypeOptions\" [attr.aria-labelledby]=\"'feature-flag-type-label'\" formControlName=\"flag_type\" class=\"dropdown-bg-transparent\" placeholder=\"Select type\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Status\" name=\"status\" controlId=\"feature-flag-status\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"statusOptions\" [attr.aria-labelledby]=\"'feature-flag-status-label'\" formControlName=\"status\" class=\"dropdown-bg-transparent\" placeholder=\"Select status\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Owner\" name=\"owner\" controlId=\"feature-flag-owner\" [useAriaLabelledbyOnly]=\"true\">\n <p-autoComplete\n [suggestions]=\"filteredUsers\"\n [attr.aria-labelledby]=\"'feature-flag-owner-label'\"\n formControlName=\"owner\"\n dataKey=\"id\"\n field=\"displayName\"\n [dropdown]=\"true\"\n (completeMethod)=\"searchOwners($event)\"\n styleClass=\"w-100\"\n placeholder=\"Search owner\"\n [multiple]=\"false\"\n autocomplete=\"off\">\n </p-autoComplete>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Expires At\" name=\"expires_at\" controlId=\"feature-flag-expires_at\" [useAriaLabelledbyOnly]=\"true\">\n <p-datepicker inputId=\"feature-flag-expires_at\" formControlName=\"expires_at\" [showIcon]=\"true\" [showTime]=\"true\" [appendTo]=\"'body'\" dateFormat=\"dd-M-yy\"></p-datepicker>\n </pw-input-container>\n </div>\n\n <div class=\"row\">\n <div class=\"col-12 mt-4\">\n <div class=\"mb-3 text-end\">\n <button type=\"button\" class=\"btn btn-outline-default me-2\" (click)=\"onCancel()\">{{ 'Button.Cancel' | transloco }}</button>\n <button class=\"btn btn-primary\" [buttonBusy]=\"buttonBusy\">{{ 'Button.Save' | transloco }}</button>\n </div>\n </div>\n </div>\n </form>\n }\n </div>\n </div>\n </div>\n</div>", dependencies: [{ kind: "component", type: i5.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i8.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "directive", type: i3$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i6.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.InputContainerComponent, selector: "pw-input-container", inputs: ["name", "controlId", "useAriaLabelledbyOnly", "label", "labelClass", "tooltipPosition", "required", "errorMsg", "isReadOnly", "showTooltip", "tooltipText", "showTriangle", "afterLabel", "showAfterLabel", "showTriangleText", "isLeftTooltip"] }, { kind: "directive", type: i9.ButtonBusyDirective, selector: "[buttonBusy]", inputs: ["buttonBusy", "busyText"] }, { kind: "directive", type: i10.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i6.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i6.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }] }); }
|
|
2832
|
+
}
|
|
2833
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagDetailsComponent, decorators: [{
|
|
2834
|
+
type: Component,
|
|
2835
|
+
args: [{ selector: 'pw-feature-flag-details', standalone: false, template: "<div class=\"container-fluid pw-tab\">\n <div class=\"dashboard\">\n <div class=\"dashboard-body\">\n <div class=\"me-auto col-xs-6 mt-4\">\n <a routerLink=\"/admin/feature-flags/list\" class=\"previous\" aria-label=\"Back to feature flags list\">\n <i class=\"fa fa-arrow-alt-circle-left\" aria-hidden=\"true\"></i>\n </a>\n <h3 class=\"mt-3\">{{ id ? 'Edit' : 'Add New' }} Feature Flag</h3>\n </div>\n\n @if (isLoading) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"></p-progressSpinner>\n </div>\n }\n\n <div class=\"p-2 mt-3\">\n @if (!isLoading) {\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n <div class=\"row\">\n <pw-input-container class=\"col-lg-6\" label=\"Key\" name=\"key\" controlId=\"feature-flag-key\" errorMsg=\"Key is required\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.Key' | transloco\">\n <input type=\"text\" id=\"feature-flag-key\" formControlName=\"key\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': submitted && f['key'].errors }\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-6\" label=\"Name\" name=\"name\" controlId=\"feature-flag-name\" errorMsg=\"Name is required\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.Name' | transloco\">\n <input type=\"text\" id=\"feature-flag-name\" formControlName=\"name\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': submitted && f['name'].errors }\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-12\" label=\"Description\" name=\"description\" controlId=\"feature-flag-description\">\n <textarea id=\"feature-flag-description\" formControlName=\"description\" rows=\"4\" class=\"form-control\"></textarea>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Type\" name=\"flag_type\" controlId=\"feature-flag-type\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"flagTypeOptions\" [attr.aria-labelledby]=\"'feature-flag-type-label'\" formControlName=\"flag_type\" class=\"dropdown-bg-transparent\" placeholder=\"Select type\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Status\" name=\"status\" controlId=\"feature-flag-status\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"statusOptions\" [attr.aria-labelledby]=\"'feature-flag-status-label'\" formControlName=\"status\" class=\"dropdown-bg-transparent\" placeholder=\"Select status\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Owner\" name=\"owner\" controlId=\"feature-flag-owner\" [useAriaLabelledbyOnly]=\"true\">\n <p-autoComplete\n [suggestions]=\"filteredUsers\"\n [attr.aria-labelledby]=\"'feature-flag-owner-label'\"\n formControlName=\"owner\"\n dataKey=\"id\"\n field=\"displayName\"\n [dropdown]=\"true\"\n (completeMethod)=\"searchOwners($event)\"\n styleClass=\"w-100\"\n placeholder=\"Search owner\"\n [multiple]=\"false\"\n autocomplete=\"off\">\n </p-autoComplete>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-3\" label=\"Expires At\" name=\"expires_at\" controlId=\"feature-flag-expires_at\" [useAriaLabelledbyOnly]=\"true\">\n <p-datepicker inputId=\"feature-flag-expires_at\" formControlName=\"expires_at\" [showIcon]=\"true\" [showTime]=\"true\" [appendTo]=\"'body'\" dateFormat=\"dd-M-yy\"></p-datepicker>\n </pw-input-container>\n </div>\n\n <div class=\"row\">\n <div class=\"col-12 mt-4\">\n <div class=\"mb-3 text-end\">\n <button type=\"button\" class=\"btn btn-outline-default me-2\" (click)=\"onCancel()\">{{ 'Button.Cancel' | transloco }}</button>\n <button class=\"btn btn-primary\" [buttonBusy]=\"buttonBusy\">{{ 'Button.Save' | transloco }}</button>\n </div>\n </div>\n </div>\n </form>\n }\n </div>\n </div>\n </div>\n</div>" }]
|
|
2836
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1.AdminService }] });
|
|
2837
|
+
|
|
2838
|
+
class FeatureFlagRulesComponent extends AppBaseComponent {
|
|
2839
|
+
constructor(injector, adminService, cdr) {
|
|
2840
|
+
super(injector);
|
|
2841
|
+
this.adminService = adminService;
|
|
2842
|
+
this.cdr = cdr;
|
|
2843
|
+
this.routers = ROUTERS;
|
|
2844
|
+
this.ruleForm = AppAdmin.getFeatureFlagRuleForm();
|
|
2845
|
+
this.featureFlagId = 0;
|
|
2846
|
+
this.featureFlagName = '';
|
|
2847
|
+
this.isLoading = true;
|
|
2848
|
+
this.ruleSubmitted = false;
|
|
2849
|
+
this.ruleButtonBusy = false;
|
|
2850
|
+
this.rules = [];
|
|
2851
|
+
this.editingRuleId = null;
|
|
2852
|
+
this.targetTypeOptions = [];
|
|
2853
|
+
this.environmentOptions = [];
|
|
2854
|
+
this.productFeatureKeyOptions = [];
|
|
2855
|
+
}
|
|
2856
|
+
ngOnInit() {
|
|
2857
|
+
this.route.params.subscribe(params => {
|
|
2858
|
+
const parsedId = Number(params['id']);
|
|
2859
|
+
this.featureFlagId = Number.isFinite(parsedId) ? parsedId : 0;
|
|
2860
|
+
this.loadPageData();
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2863
|
+
nonEmpty(value) {
|
|
2864
|
+
return value && value.length > 0 ? value : undefined;
|
|
2865
|
+
}
|
|
2866
|
+
get rf() {
|
|
2867
|
+
return this.ruleForm.controls;
|
|
2868
|
+
}
|
|
2869
|
+
loadPageData() {
|
|
2870
|
+
if (!this.featureFlagId) {
|
|
2871
|
+
this.isLoading = false;
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
this.isLoading = true;
|
|
2875
|
+
this.loadSupportedOptions();
|
|
2876
|
+
forkJoin({
|
|
2877
|
+
flag: this.adminService.getFeatureFlagById(this.featureFlagId),
|
|
2878
|
+
rulesResponse: this.adminService.getFeatureFlagRules(this.featureFlagId)
|
|
2879
|
+
})
|
|
2880
|
+
.pipe(finalize(() => {
|
|
2881
|
+
this.isLoading = false;
|
|
2882
|
+
this.cdr.detectChanges();
|
|
2883
|
+
}))
|
|
2884
|
+
.subscribe({
|
|
2885
|
+
next: ({ flag, rulesResponse }) => {
|
|
2886
|
+
const name = flag.name?.trim();
|
|
2887
|
+
const key = flag.key?.trim();
|
|
2888
|
+
this.featureFlagName =
|
|
2889
|
+
this.nonEmpty(name) ?? this.nonEmpty(key) ?? 'Feature Flag';
|
|
2890
|
+
this.rules = rulesResponse.feature_flag_rules ?? [];
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
loadSupportedOptions() {
|
|
2895
|
+
this.adminService.getSupportedFeatureFlagRuleOptions().subscribe(response => {
|
|
2896
|
+
this.targetTypeOptions = (response.target_types ?? []).map(option => ({
|
|
2897
|
+
label: option,
|
|
2898
|
+
value: option
|
|
2899
|
+
}));
|
|
2900
|
+
this.environmentOptions = (response.environments ?? []).map(option => ({
|
|
2901
|
+
label: option,
|
|
2902
|
+
value: option
|
|
2903
|
+
}));
|
|
2904
|
+
this.productFeatureKeyOptions = (response.product_feature_keys ?? []).map(option => ({
|
|
2905
|
+
label: option,
|
|
2906
|
+
value: option
|
|
2907
|
+
}));
|
|
2908
|
+
});
|
|
2909
|
+
}
|
|
2910
|
+
loadRules() {
|
|
2911
|
+
this.adminService.getFeatureFlagRules(this.featureFlagId).subscribe(response => {
|
|
2912
|
+
this.rules = response.feature_flag_rules ?? [];
|
|
2913
|
+
this.cdr.detectChanges();
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
editRule(rule) {
|
|
2917
|
+
this.editingRuleId = rule.id;
|
|
2918
|
+
this.ruleForm.patchValue({
|
|
2919
|
+
environment: rule.environment,
|
|
2920
|
+
product_feature_key: rule.product_feature_key,
|
|
2921
|
+
target_type: rule.target_type,
|
|
2922
|
+
target_value: rule.target_value,
|
|
2923
|
+
enabled: rule.enabled,
|
|
2924
|
+
priority: rule.priority,
|
|
2925
|
+
rollout_percentage: rule.rollout_percentage
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
resetRuleForm() {
|
|
2929
|
+
this.ruleForm.reset({
|
|
2930
|
+
environment: null,
|
|
2931
|
+
product_feature_key: null,
|
|
2932
|
+
target_type: null,
|
|
2933
|
+
target_value: '',
|
|
2934
|
+
enabled: false,
|
|
2935
|
+
priority: 0,
|
|
2936
|
+
rollout_percentage: null
|
|
2937
|
+
});
|
|
2938
|
+
this.ruleSubmitted = false;
|
|
2939
|
+
this.editingRuleId = null;
|
|
2940
|
+
}
|
|
2941
|
+
saveRule() {
|
|
2942
|
+
this.ruleForm.markAllAsTouched();
|
|
2943
|
+
this.ruleSubmitted = true;
|
|
2944
|
+
if (!this.ruleForm.valid) {
|
|
2945
|
+
return;
|
|
2946
|
+
}
|
|
2947
|
+
this.ruleButtonBusy = true;
|
|
2948
|
+
const payload = this.ruleForm.value;
|
|
2949
|
+
const request$ = this.editingRuleId
|
|
2950
|
+
? this.adminService.updateFeatureFlagRule(this.featureFlagId, this.editingRuleId, payload)
|
|
2951
|
+
: this.adminService.createFeatureFlagRule(this.featureFlagId, payload);
|
|
2952
|
+
request$.subscribe({
|
|
2953
|
+
next: () => {
|
|
2954
|
+
this.toast.success(this.editingRuleId
|
|
2955
|
+
? 'Feature flag rule updated successfully.'
|
|
2956
|
+
: 'Feature flag rule created successfully.');
|
|
2957
|
+
this.loadRules();
|
|
2958
|
+
this.resetRuleForm();
|
|
2959
|
+
},
|
|
2960
|
+
complete: () => {
|
|
2961
|
+
this.ruleButtonBusy = false;
|
|
2962
|
+
}
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
deleteRule(rule) {
|
|
2966
|
+
swal.fire({
|
|
2967
|
+
title: 'Delete feature flag rule?',
|
|
2968
|
+
text: `This will remove the rule with target type ${rule.target_type}.`,
|
|
2969
|
+
icon: 'warning',
|
|
2970
|
+
showCancelButton: true,
|
|
2971
|
+
confirmButtonText: 'Delete'
|
|
2972
|
+
}).then(result => {
|
|
2973
|
+
if (!result.isConfirmed) {
|
|
2974
|
+
return;
|
|
2975
|
+
}
|
|
2976
|
+
this.adminService.deleteFeatureFlagRule(this.featureFlagId, rule.id).subscribe(() => {
|
|
2977
|
+
this.toast.success('Feature flag rule has been destroyed.');
|
|
2978
|
+
this.loadRules();
|
|
2979
|
+
if (this.editingRuleId === rule.id) {
|
|
2980
|
+
this.resetRuleForm();
|
|
2981
|
+
}
|
|
2982
|
+
});
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
backToFlags() {
|
|
2986
|
+
this.router.navigate([this.routers.featureFlagsConfig]);
|
|
2987
|
+
}
|
|
2988
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagRulesComponent, deps: [{ token: i0.Injector }, { token: i1.AdminService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2989
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FeatureFlagRulesComponent, isStandalone: false, selector: "pw-feature-flag-rules", usesInheritance: true, ngImport: i0, template: "<div class=\"container-fluid pw-tab\">\n <div class=\"dashboard\">\n <div class=\"dashboard-body\">\n <div class=\"me-auto col-xs-6 mt-4\">\n <a [routerLink]=\"routers.featureFlagsConfig\" class=\"previous\" aria-label=\"Back to feature flags\">\n <i class=\"fa fa-arrow-alt-circle-left\" aria-hidden=\"true\"></i>\n </a>\n <h3 class=\"mt-3\">Feature Flag Rules \u2014 {{ featureFlagName }}</h3>\n </div>\n\n @if (isLoading) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"></p-progressSpinner>\n </div>\n }\n\n @if (!isLoading) {\n <div class=\"p-2 mt-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h4 class=\"mb-0\">Manage Rules</h4>\n </div>\n\n <form [formGroup]=\"ruleForm\" (ngSubmit)=\"saveRule()\" class=\"mb-4\">\n <div class=\"row\">\n <pw-input-container class=\"col-lg-4\" label=\"Environment\" name=\"environment\" controlId=\"feature-flag-rule-environment\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"environmentOptions\" formControlName=\"environment\" class=\"dropdown-bg-transparent\" placeholder=\"Select environment\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Product Feature Key\" name=\"product_feature_key\" controlId=\"feature-flag-rule-product_feature_key\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"productFeatureKeyOptions\" formControlName=\"product_feature_key\" class=\"dropdown-bg-transparent\" placeholder=\"Select product feature key\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Target Type\" name=\"target_type\" controlId=\"feature-flag-rule-target_type\" errorMsg=\"Target type is required\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"targetTypeOptions\" formControlName=\"target_type\" class=\"dropdown-bg-transparent\" placeholder=\"Select target type\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Target Value\" name=\"target_value\" controlId=\"feature-flag-rule-target_value\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.TargetValue' | transloco\">\n <input type=\"text\" id=\"feature-flag-rule-target_value\" formControlName=\"target_value\" class=\"form-control\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Priority\" name=\"priority\" controlId=\"feature-flag-rule-priority\" errorMsg=\"Priority is required\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.Priority' | transloco\">\n <input type=\"number\" id=\"feature-flag-rule-priority\" formControlName=\"priority\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': ruleSubmitted && rf['priority'].errors }\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Rollout Percentage\" name=\"rollout_percentage\" controlId=\"feature-flag-rule-rollout_percentage\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.RolloutPercentage' | transloco\">\n <input type=\"number\" id=\"feature-flag-rule-rollout_percentage\" formControlName=\"rollout_percentage\" class=\"form-control\" min=\"0\" max=\"100\" />\n </pw-input-container>\n\n <div class=\"col-lg-3 d-flex align-items-end\">\n <div>\n <span id=\"feature-flag-rule-enabled-label\" class=\"pw-label-style\">Enabled</span><br />\n <ui-switch formControlName=\"enabled\" [attr.aria-labelledby]=\"'feature-flag-rule-enabled-label'\"></ui-switch>\n </div>\n </div>\n\n <div class=\"col-lg-9 d-flex align-items-end justify-content-end gap-2\">\n @if (editingRuleId) {\n <button type=\"button\" class=\"btn btn-outline-default\" (click)=\"resetRuleForm()\">\n Cancel editing rule\n </button>\n }\n <button type=\"submit\" class=\"btn btn-primary\" [buttonBusy]=\"ruleButtonBusy\">\n {{ editingRuleId ? 'Update Rule' : 'Add Rule' }}\n </button>\n </div>\n </div>\n </form>\n\n <div class=\"row primeng-datatable-container mt-0\">\n <div class=\"col-12 px-0\">\n <p-table [value]=\"rules\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">Environment</th>\n <th scope=\"col\">Product Feature Key</th>\n <th scope=\"col\">Target Type</th>\n <th scope=\"col\">Target Value</th>\n <th scope=\"col\">Enabled</th>\n <th scope=\"col\">Priority</th>\n <th scope=\"col\">Rollout %</th>\n <th scope=\"col\" class=\"actions-list-two\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-rule>\n <tr>\n <td>{{ rule.environment || '-' }}</td>\n <td>{{ rule.product_feature_key || '-' }}</td>\n <td>{{ rule.target_type }}</td>\n <td>{{ rule.target_value || '-' }}</td>\n <td><span class=\"badge\" [ngClass]=\"rule.enabled ? 'bg-success' : 'bg-secondary'\">{{ rule.enabled }}</span></td>\n <td>{{ rule.priority }}</td>\n <td>{{ rule.rollout_percentage ?? '-' }}</td>\n <td>\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Edit rule\"\n (click)=\"editRule(rule)\">\n <i class=\"fa fa-edit edit-icon\"></i>\n </button>\n </li>\n <li ngbTooltip=\"Delete\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Delete rule\"\n (click)=\"deleteRule(rule)\">\n <i class=\"fa fa-trash delete-icon\"></i>\n </button>\n </li>\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (rules.length === 0) {\n <pw-no-data [withImage]=\"true\" [message]=\"'No feature flag rules found'\"></pw-no-data>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>", dependencies: [{ kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i3$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i2$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "directive", type: i3$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i6.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i6.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i6.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "component", type: i7.UiSwitchComponent, selector: "ui-switch", inputs: ["size", "color", "switchOffColor", "switchColor", "defaultBgColor", "defaultBoColor", "checkedLabel", "uncheckedLabel", "checkedTextColor", "uncheckedTextColor", "beforeChange", "ariaLabel", "checked", "disabled", "reverse", "loading"], outputs: ["change", "changeEvent", "valueChange"] }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.InputContainerComponent, selector: "pw-input-container", inputs: ["name", "controlId", "useAriaLabelledbyOnly", "label", "labelClass", "tooltipPosition", "required", "errorMsg", "isReadOnly", "showTooltip", "tooltipText", "showTriangle", "afterLabel", "showAfterLabel", "showTriangleText", "isLeftTooltip"] }, { kind: "component", type: i1$1.NoDataComponent, selector: "pw-no-data", inputs: ["message", "description", "withImage"] }, { kind: "directive", type: i9.ButtonBusyDirective, selector: "[buttonBusy]", inputs: ["buttonBusy", "busyText"] }, { kind: "directive", type: i10.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i6.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i6.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }] }); }
|
|
2990
|
+
}
|
|
2991
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagRulesComponent, decorators: [{
|
|
2992
|
+
type: Component,
|
|
2993
|
+
args: [{ selector: 'pw-feature-flag-rules', standalone: false, template: "<div class=\"container-fluid pw-tab\">\n <div class=\"dashboard\">\n <div class=\"dashboard-body\">\n <div class=\"me-auto col-xs-6 mt-4\">\n <a [routerLink]=\"routers.featureFlagsConfig\" class=\"previous\" aria-label=\"Back to feature flags\">\n <i class=\"fa fa-arrow-alt-circle-left\" aria-hidden=\"true\"></i>\n </a>\n <h3 class=\"mt-3\">Feature Flag Rules \u2014 {{ featureFlagName }}</h3>\n </div>\n\n @if (isLoading) {\n <div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"></p-progressSpinner>\n </div>\n }\n\n @if (!isLoading) {\n <div class=\"p-2 mt-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h4 class=\"mb-0\">Manage Rules</h4>\n </div>\n\n <form [formGroup]=\"ruleForm\" (ngSubmit)=\"saveRule()\" class=\"mb-4\">\n <div class=\"row\">\n <pw-input-container class=\"col-lg-4\" label=\"Environment\" name=\"environment\" controlId=\"feature-flag-rule-environment\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"environmentOptions\" formControlName=\"environment\" class=\"dropdown-bg-transparent\" placeholder=\"Select environment\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Product Feature Key\" name=\"product_feature_key\" controlId=\"feature-flag-rule-product_feature_key\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"productFeatureKeyOptions\" formControlName=\"product_feature_key\" class=\"dropdown-bg-transparent\" placeholder=\"Select product feature key\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Target Type\" name=\"target_type\" controlId=\"feature-flag-rule-target_type\" errorMsg=\"Target type is required\" [useAriaLabelledbyOnly]=\"true\">\n <p-select [options]=\"targetTypeOptions\" formControlName=\"target_type\" class=\"dropdown-bg-transparent\" placeholder=\"Select target type\"></p-select>\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Target Value\" name=\"target_value\" controlId=\"feature-flag-rule-target_value\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.TargetValue' | transloco\">\n <input type=\"text\" id=\"feature-flag-rule-target_value\" formControlName=\"target_value\" class=\"form-control\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Priority\" name=\"priority\" controlId=\"feature-flag-rule-priority\" errorMsg=\"Priority is required\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.Priority' | transloco\">\n <input type=\"number\" id=\"feature-flag-rule-priority\" formControlName=\"priority\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': ruleSubmitted && rf['priority'].errors }\" />\n </pw-input-container>\n\n <pw-input-container class=\"col-lg-4\" label=\"Rollout Percentage\" name=\"rollout_percentage\" controlId=\"feature-flag-rule-rollout_percentage\"\n [showTooltip]=\"true\" [tooltipText]=\"'FeatureFlag.Tooltip.RolloutPercentage' | transloco\">\n <input type=\"number\" id=\"feature-flag-rule-rollout_percentage\" formControlName=\"rollout_percentage\" class=\"form-control\" min=\"0\" max=\"100\" />\n </pw-input-container>\n\n <div class=\"col-lg-3 d-flex align-items-end\">\n <div>\n <span id=\"feature-flag-rule-enabled-label\" class=\"pw-label-style\">Enabled</span><br />\n <ui-switch formControlName=\"enabled\" [attr.aria-labelledby]=\"'feature-flag-rule-enabled-label'\"></ui-switch>\n </div>\n </div>\n\n <div class=\"col-lg-9 d-flex align-items-end justify-content-end gap-2\">\n @if (editingRuleId) {\n <button type=\"button\" class=\"btn btn-outline-default\" (click)=\"resetRuleForm()\">\n Cancel editing rule\n </button>\n }\n <button type=\"submit\" class=\"btn btn-primary\" [buttonBusy]=\"ruleButtonBusy\">\n {{ editingRuleId ? 'Update Rule' : 'Add Rule' }}\n </button>\n </div>\n </div>\n </form>\n\n <div class=\"row primeng-datatable-container mt-0\">\n <div class=\"col-12 px-0\">\n <p-table [value]=\"rules\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">Environment</th>\n <th scope=\"col\">Product Feature Key</th>\n <th scope=\"col\">Target Type</th>\n <th scope=\"col\">Target Value</th>\n <th scope=\"col\">Enabled</th>\n <th scope=\"col\">Priority</th>\n <th scope=\"col\">Rollout %</th>\n <th scope=\"col\" class=\"actions-list-two\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-rule>\n <tr>\n <td>{{ rule.environment || '-' }}</td>\n <td>{{ rule.product_feature_key || '-' }}</td>\n <td>{{ rule.target_type }}</td>\n <td>{{ rule.target_value || '-' }}</td>\n <td><span class=\"badge\" [ngClass]=\"rule.enabled ? 'bg-success' : 'bg-secondary'\">{{ rule.enabled }}</span></td>\n <td>{{ rule.priority }}</td>\n <td>{{ rule.rollout_percentage ?? '-' }}</td>\n <td>\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Edit rule\"\n (click)=\"editRule(rule)\">\n <i class=\"fa fa-edit edit-icon\"></i>\n </button>\n </li>\n <li ngbTooltip=\"Delete\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Delete rule\"\n (click)=\"deleteRule(rule)\">\n <i class=\"fa fa-trash delete-icon\"></i>\n </button>\n </li>\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (rules.length === 0) {\n <pw-no-data [withImage]=\"true\" [message]=\"'No feature flag rules found'\"></pw-no-data>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n</div>" }]
|
|
2994
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1.AdminService }, { type: i0.ChangeDetectorRef }] });
|
|
2995
|
+
|
|
2996
|
+
class FeatureFlagsListComponent extends AppBaseComponent {
|
|
2997
|
+
constructor(injector, adminService) {
|
|
2998
|
+
super(injector);
|
|
2999
|
+
this.adminService = adminService;
|
|
3000
|
+
this.PAGE_SIZE = PAGE_SIZE;
|
|
3001
|
+
this.routers = ROUTERS;
|
|
3002
|
+
this.buttonBusy = false;
|
|
3003
|
+
this.isLoaded = true;
|
|
3004
|
+
this.searchText = '';
|
|
3005
|
+
this.featureFlags = [];
|
|
3006
|
+
this.totalRecords = 0;
|
|
3007
|
+
this.page = 1;
|
|
3008
|
+
}
|
|
3009
|
+
nonEmpty(value) {
|
|
3010
|
+
return value && value.length > 0 ? value : undefined;
|
|
3011
|
+
}
|
|
3012
|
+
onLazyLoad(event) {
|
|
3013
|
+
const pageDetails = HelperService.onTableLazyLoad(event);
|
|
3014
|
+
this.page = pageDetails.page;
|
|
3015
|
+
this.loadFeatureFlags({
|
|
3016
|
+
page: pageDetails.page,
|
|
3017
|
+
page_size: PAGE_SIZE,
|
|
3018
|
+
order_by: pageDetails.sortField,
|
|
3019
|
+
order_direction: pageDetails.sortOrder
|
|
3020
|
+
});
|
|
3021
|
+
}
|
|
3022
|
+
loadFeatureFlags(paging) {
|
|
3023
|
+
this.isLoaded = false;
|
|
3024
|
+
this.adminService.getFeatureFlags(paging).subscribe({
|
|
3025
|
+
next: response => {
|
|
3026
|
+
this.featureFlags = response.feature_flags ?? [];
|
|
3027
|
+
this.totalRecords = response.object_count ?? this.featureFlags.length;
|
|
3028
|
+
},
|
|
3029
|
+
complete: () => {
|
|
3030
|
+
this.isLoaded = true;
|
|
3031
|
+
}
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
navigateToEdit(item) {
|
|
3035
|
+
this.router.navigate([this.routers.featureFlagsDetails + item.id], {
|
|
3036
|
+
state: { data: item }
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
navigateToRules(item) {
|
|
3040
|
+
this.router.navigate([this.routers.featureFlagRules + item.id], {
|
|
3041
|
+
state: { data: item }
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
onDelete(item) {
|
|
3045
|
+
const name = item.name?.trim();
|
|
3046
|
+
const key = item.key?.trim();
|
|
3047
|
+
const displayName = this.nonEmpty(name) ?? this.nonEmpty(key) ?? 'this feature flag';
|
|
3048
|
+
swal.fire({
|
|
3049
|
+
title: 'Delete feature flag?',
|
|
3050
|
+
text: `This will remove ${displayName}.`,
|
|
3051
|
+
icon: 'warning',
|
|
3052
|
+
showCancelButton: true,
|
|
3053
|
+
confirmButtonText: 'Delete'
|
|
3054
|
+
}).then(result => {
|
|
3055
|
+
if (!result.isConfirmed) {
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
this.buttonBusy = true;
|
|
3059
|
+
this.adminService.deleteFeatureFlag(item.id).subscribe({
|
|
3060
|
+
next: () => {
|
|
3061
|
+
this.toast.success('Feature flag has been destroyed.');
|
|
3062
|
+
this.loadFeatureFlags({ page: 1, page_size: PAGE_SIZE });
|
|
3063
|
+
},
|
|
3064
|
+
complete: () => {
|
|
3065
|
+
this.buttonBusy = false;
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
3068
|
+
});
|
|
3069
|
+
}
|
|
3070
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagsListComponent, deps: [{ token: i0.Injector }, { token: i1.AdminService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3071
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FeatureFlagsListComponent, isStandalone: false, selector: "pw-feature-flags-list", usesInheritance: true, ngImport: i0, template: "<div class=\"row\" [class.custom-disable-wrapper]=\"buttonBusy\">\n <div class=\"col-12 d-flex flex-wrap justify-content-between align-items-center\">\n <h2 class=\"card-title p-0 float-start\">Feature Flags</h2>\n <a [routerLink]=\"[routers.featureFlagsDetails + 'add']\" class=\"btn btn-sm btn-outline-primary float-end\">\n <i class=\"fa fa-plus-circle\" aria-hidden=\"true\"></i> {{ 'Label.AddNew' | transloco }} Feature Flag\n </a>\n </div>\n <div class=\"col-12 mb-3\">\n <p>Manage feature flags for controlled rollout, testing, and admin-driven enablement.</p>\n </div>\n</div>\n\n@if (!isLoaded) {\n<div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"></p-progressSpinner>\n</div>\n}\n\n<div class=\"row primeng-datatable-container mt-0\" [class.custom-disable-wrapper]=\"buttonBusy\">\n <div class=\"col-12 px-0\">\n <p-table\n [value]=\"featureFlags\"\n [paginator]=\"totalRecords !== 0\"\n [lazy]=\"true\"\n [rows]=\"PAGE_SIZE\"\n [totalRecords]=\"totalRecords\"\n [filterDelay]=\"300\"\n (onLazyLoad)=\"onLazyLoad($event)\">\n <ng-template pTemplate=\"caption\">\n <div class=\"text-end\">\n <i class=\"fa fa-search mt-2 me-2\" aria-hidden=\"true\"></i>\n <input type=\"text\" [(ngModel)]=\"searchText\" pInputText size=\"50\" placeholder=\"Search feature flags...\" class=\"mw-90\" />\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">Key</th>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Type</th>\n <th scope=\"col\">Owner</th>\n <th scope=\"col\">Expires</th>\n <th scope=\"col\" class=\"actions-list-two\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-flag>\n <tr>\n <td>{{ flag.key }}</td>\n <td>{{ flag.name }}</td>\n <td><span class=\"badge bg-info\">{{ flag.status || 'draft' }}</span></td>\n <td>{{ flag.flag_type || '-' }}</td>\n <td>{{ flag.owner || '-' }}</td>\n <td>{{ flag.expires_at ? (flag.expires_at | date:'dd-MMM-yyyy HH:mm') : '-' }}</td>\n <td>\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Edit feature flag\"\n (click)=\"navigateToEdit(flag)\">\n <i class=\"fa fa-edit edit-icon\"></i>\n </button>\n </li>\n <li ngbTooltip=\"Rules\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Manage feature flag rules\"\n (click)=\"navigateToRules(flag)\">\n <i class=\"fa fa-list-ul edit-icon\"></i>\n </button>\n </li>\n <li ngbTooltip=\"Delete\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Delete feature flag\"\n (click)=\"onDelete(flag)\">\n <i class=\"fa fa-trash delete-icon\"></i>\n </button>\n </li>\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (totalRecords === 0) {\n <pw-no-data [withImage]=\"true\" [message]=\"'No feature flags found'\"></pw-no-data>\n }\n </div>\n</div>", dependencies: [{ kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i3$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i2$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.NoDataComponent, selector: "pw-no-data", inputs: ["message", "description", "withImage"] }, { kind: "directive", type: i10.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i3$1.DatePipe, name: "date" }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }] }); }
|
|
3072
|
+
}
|
|
3073
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagsListComponent, decorators: [{
|
|
3074
|
+
type: Component,
|
|
3075
|
+
args: [{ selector: 'pw-feature-flags-list', standalone: false, template: "<div class=\"row\" [class.custom-disable-wrapper]=\"buttonBusy\">\n <div class=\"col-12 d-flex flex-wrap justify-content-between align-items-center\">\n <h2 class=\"card-title p-0 float-start\">Feature Flags</h2>\n <a [routerLink]=\"[routers.featureFlagsDetails + 'add']\" class=\"btn btn-sm btn-outline-primary float-end\">\n <i class=\"fa fa-plus-circle\" aria-hidden=\"true\"></i> {{ 'Label.AddNew' | transloco }} Feature Flag\n </a>\n </div>\n <div class=\"col-12 mb-3\">\n <p>Manage feature flags for controlled rollout, testing, and admin-driven enablement.</p>\n </div>\n</div>\n\n@if (!isLoaded) {\n<div class=\"w-100 text-center mt-3\">\n <p-progressSpinner strokeWidth=\"2\"></p-progressSpinner>\n</div>\n}\n\n<div class=\"row primeng-datatable-container mt-0\" [class.custom-disable-wrapper]=\"buttonBusy\">\n <div class=\"col-12 px-0\">\n <p-table\n [value]=\"featureFlags\"\n [paginator]=\"totalRecords !== 0\"\n [lazy]=\"true\"\n [rows]=\"PAGE_SIZE\"\n [totalRecords]=\"totalRecords\"\n [filterDelay]=\"300\"\n (onLazyLoad)=\"onLazyLoad($event)\">\n <ng-template pTemplate=\"caption\">\n <div class=\"text-end\">\n <i class=\"fa fa-search mt-2 me-2\" aria-hidden=\"true\"></i>\n <input type=\"text\" [(ngModel)]=\"searchText\" pInputText size=\"50\" placeholder=\"Search feature flags...\" class=\"mw-90\" />\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">Key</th>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Status</th>\n <th scope=\"col\">Type</th>\n <th scope=\"col\">Owner</th>\n <th scope=\"col\">Expires</th>\n <th scope=\"col\" class=\"actions-list-two\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-flag>\n <tr>\n <td>{{ flag.key }}</td>\n <td>{{ flag.name }}</td>\n <td><span class=\"badge bg-info\">{{ flag.status || 'draft' }}</span></td>\n <td>{{ flag.flag_type || '-' }}</td>\n <td>{{ flag.owner || '-' }}</td>\n <td>{{ flag.expires_at ? (flag.expires_at | date:'dd-MMM-yyyy HH:mm') : '-' }}</td>\n <td>\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Edit feature flag\"\n (click)=\"navigateToEdit(flag)\">\n <i class=\"fa fa-edit edit-icon\"></i>\n </button>\n </li>\n <li ngbTooltip=\"Rules\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Manage feature flag rules\"\n (click)=\"navigateToRules(flag)\">\n <i class=\"fa fa-list-ul edit-icon\"></i>\n </button>\n </li>\n <li ngbTooltip=\"Delete\" class=\"me-2 me-sm-2\">\n <button\n type=\"button\"\n class=\"btn p-0 border-0 bg-transparent\"\n aria-label=\"Delete feature flag\"\n (click)=\"onDelete(flag)\">\n <i class=\"fa fa-trash delete-icon\"></i>\n </button>\n </li>\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (totalRecords === 0) {\n <pw-no-data [withImage]=\"true\" [message]=\"'No feature flags found'\"></pw-no-data>\n }\n </div>\n</div>" }]
|
|
3076
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1.AdminService }] });
|
|
3077
|
+
|
|
3078
|
+
class FeatureFlagsTabsComponent {
|
|
3079
|
+
constructor() {
|
|
3080
|
+
this.items = [
|
|
3081
|
+
{
|
|
3082
|
+
id: 'feature-flags',
|
|
3083
|
+
label: 'Feature Flags',
|
|
3084
|
+
icon: 'fa fa-fw fa-toggle-on',
|
|
3085
|
+
routerLink: ['/admin/feature-flags/list']
|
|
3086
|
+
}
|
|
3087
|
+
];
|
|
3088
|
+
}
|
|
3089
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagsTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3090
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.6", type: FeatureFlagsTabsComponent, isStandalone: false, selector: "feature-flags-tabs", ngImport: i0, template: `<pw-tabs [items]="items"></pw-tabs>`, isInline: true, dependencies: [{ kind: "component", type: i1$1.PwTabsComponent, selector: "pw-tabs", inputs: ["items", "withSubscription"] }] }); }
|
|
3091
|
+
}
|
|
3092
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FeatureFlagsTabsComponent, decorators: [{
|
|
3093
|
+
type: Component,
|
|
3094
|
+
args: [{
|
|
3095
|
+
selector: 'feature-flags-tabs',
|
|
3096
|
+
template: `<pw-tabs [items]="items"></pw-tabs>`,
|
|
3097
|
+
standalone: false
|
|
3098
|
+
}]
|
|
3099
|
+
}] });
|
|
3100
|
+
|
|
2716
3101
|
class FeedbackQuestionsDetailsComponent extends AppBaseComponent {
|
|
2717
3102
|
constructor(injector, cdr, adminService, productService) {
|
|
2718
3103
|
super(injector);
|
|
@@ -8075,11 +8460,11 @@ class SubscriptionsListComponent extends AppBaseComponent {
|
|
|
8075
8460
|
super.ngOnDestroy();
|
|
8076
8461
|
}
|
|
8077
8462
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: SubscriptionsListComponent, deps: [{ token: i1$2.ProductService }, { token: i1.AdminService }, { token: i6.UntypedFormBuilder }, { token: i0.Injector }, { token: i0.ChangeDetectorRef }, { token: i1$2.CommonService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8078
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: SubscriptionsListComponent, isStandalone: false, selector: "pw-subscriptions-list", viewQueries: [{ propertyName: "refProduct", first: true, predicate: ["refProduct"], descendants: true }, { propertyName: "recoverSubscriptionPasswordModal", first: true, predicate: ["recoverSubscriptionPasswordModal"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"row\">\n <div\n class=\"col-12 d-flex justify-content-between align-items-center\">\n <h2 class=\"card-title p-0 float-start\">Subscriptions</h2>\n <a class=\"btn btn-sm btn-outline-primary float-end\"\n id=\"btn-create\"\n [routerLink]=\"[routers.inviteUsers]\">\n <i class=\"fa fa-plus-circle\" aria-hidden=\"true\"></i> Invite a User to a Trial\n </a>\n </div>\n</div>\n<div class=\"p-2 mt-3\">\n <div class=\"w-100 text-center mt-3\">\n @if (!isLoaded) {\n <p-progressSpinner> </p-progressSpinner>\n }\n </div>\n <div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"totalRecordsUnFiltered === 0\">\n <p-table #dt\n [value]=\"subscriptions\"\n [paginator]=\"totalRecords !== 0\"\n [rows]=\"PAGE_SIZE\"\n [lazy]=\"true\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <ng-template pTemplate=\"caption\">\n @if (subscriptionInsights) {\n <div class=\"row mb-4\">\n @for (temp of objectKeys(subscriptionInsights); track temp) {\n <div class=\"col-12 col-lg-3 mt-2 summary\">\n <div class=\"card\">\n <div class=\"card-body\">\n @for (item of subscriptionInsights[temp]; track item) {\n <div>\n <p class=\"mb-0 px-2\">{{ item?.title }}\n @if (item?.info) {\n <span\n [pTooltip]=\"item?.info\">\n <i class=\"fas fa-info-circle\"></i>\n </span>\n }\n </p>\n <h4 class=\"fw-bold mt-2 mb-2\">\n {{ item?.value }}\n </h4>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n <div class=\"search-filter\">\n <div class=\"col-12 col-sm-4\">\n <p-multiSelect inputId=\"subscriptions-list-feature-keys\"\n placeholder=\"Select Feature Keys\"\n [showToggleAll]=\"true\"\n [showHeader]=\"true\"\n [(ngModel)]=\"selectedFeatureKeys\"\n [options]=\"searchOptions\"\n (onChange)=\"onSearchOptionChange($event)\"\n appendTo=\"body\">\n </p-multiSelect>\n </div>\n <div class=\"col-12 col-sm-4\">\n <p-select\n inputId=\"subscriptions-list-status\"\n [options]=\"subscriptionStatus\"\n [placeholder]=\"'Select status'\"\n (onChange)=\"filterByStatus($event.value)\"\n [(ngModel)]=\"status\">\n </p-select>\n </div>\n <div class=\"text-end\">\n <label for=\"subscriptions-list-search\" class=\"visually-hidden\">Search Subscription</label>\n <i class=\"fa fa-search mt-2 me-2 d-none\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n id=\"subscriptions-list-search\"\n name=\"subscriptions-list-search\"\n [(ngModel)]=\"searchText\"\n pInputText\n size=\"30\"\n placeholder=\"Search Subscription...\"\n class=\"mw-90\"\n (input)=\"dt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"id\">\n {{ 'Admin.Subscriptions.Id' | transloco }}\n <p-sortIcon field=\"id\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"organisation\">\n {{ 'Admin.Subscriptions.Organization' | transloco }}\n <p-sortIcon field=\"organisation\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_name\">\n {{ 'Admin.Subscriptions.ContactName' | transloco }}\n <p-sortIcon field=\"contact_name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_email\">\n {{ 'Admin.Subscriptions.ContactEmail' | transloco }}\n <p-sortIcon field=\"contact_email\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Product' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Currency' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.PricePerMonth' | transloco }}</th>\n <th scope=\"true\"\n pSortableColumn=\"stripe_customer_id\">\n {{ 'Admin.Subscriptions.Paid' | transloco }}\n <p-sortIcon field=\"stripe_customer_id\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Billing' | transloco }}</th>\n <th scope=\"true\"\n class=\"expires-at-width\">\n {{ 'Admin.Subscriptions.ExpiresAt' | transloco }}\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_total\">\n {{ 'Admin.Subscriptions.SessionsTotal' | transloco }}\n <p-sortIcon field=\"sessions_total\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_last_month\">\n {{ 'Admin.Subscriptions.SessionsTotalLastMonth' | transloco }}\n <p-sortIcon field=\"sessions_last_month\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"deleted_at\">\n {{ 'Admin.Subscriptions.Deleted' | transloco }}\n <p-sortIcon field=\"deleted_at\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"id\">{{ item.id }}</td>\n <td data-head=\"Organization\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.organisation\"\n tooltipPosition=\"top\">{{ item.organisation | textTruncate: 20 }}</span>\n </td>\n <td data-head=\"ContactName\"\n class=\"custom-break-word\">\n {{ item.contact_name }}\n </td>\n <td data-head=\"ContactEmail\">{{ item.contact_email }}</td>\n <td data-head=\"Name\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.productName\"\n tooltipPosition=\"top\">{{ item.productName }}</span>\n </td>\n <td data-head=\"Currency\">\n <span [appDynamicBadge]=\"{ itemsArray: currency, item: item.currency }\"\n color=\"blue-grey\"\n class=\"badge\">{{ item.currency }}</span>\n </td>\n <td data-head=\"Price/M\">\n <span>{{ (item.price / 100).toFixed(2) | currency:item.currency }}\n @if (item?.discount && objectKeys(item?.discount)?.length) {\n <i\n class=\"fa fa-info-circle\"\n aria-hidden=\"true\"\n [pTooltip]=\"item?.discount | json\"\n tooltipPosition=\"top\"\n ></i>\n }\n </span>\n </td>\n <td data-head=\"Paid\">\n @if (item?.unsubscribed_at) {\n <span class=\"badge bg-warning d-inline-flex align-items-center\">\n Unsub\n @if (item?.reason_to_cancel) {\n <i class=\"fa fa-info-circle ms-2\"\n [pTooltip]=\"item.reason_to_cancel\"\n tooltipPosition=\"top\"\n aria-hidden=\"true\"></i>\n }\n </span>\n }\n @if (!item?.unsubscribed_at) {\n @if ((item?.stripe_customer_id || item?.external_payment) && item?.signed_up_at) {\n <span class=\"badge bg-success\">Yes</span>\n } @else {\n <span class=\"badge bg-blue-grey\">No</span>\n }\n }\n @if (item?.pause_collection && objectKeys(item?.pause_collection)?.length) {\n <span\n class=\"ms-2\"\n [pTooltip]=\"item?.pause_collection | json\"\n tooltipPosition=\"top\">\n <i class=\"fas fa-exclamation-triangle text-warning\"></i>\n </span>\n }\n </td>\n <td data-head=\"Billing\">\n <span class=\"badge\"\n [appDynamicBadge]=\"{\n itemsArray: billingFrequency,\n item: item?.billingFrequency\n }\"\n color=\"blue-grey\">{{ item.billingFrequency }}</span>\n </td>\n <td data-head=\"Expires\">\n <span [ngClass]=\"getExpiresAtColor(item)\">{{item?.expires_at | dateFormat}}</span><br />\n <span>{{item?.created_at | sinceAgo}}</span>\n </td>\n <td data-head=\"S#\">{{ item?.sessions_total }}</td>\n <td data-head=\"S/m #\">\n <span [ngClass]=\"{\n 'text-danger fw-bold': item?.sessions_last_month < 3\n }\">{{ item?.sessions_last_month }}</span>\n </td>\n <td data-head=\"Deleted\">\n @if (item?.deleted_at) {\n <span\n class=\"badge bg-danger\">Yes</span>\n }\n @if (!item?.deleted_at) {\n <span\n class=\"badge bg-blue-grey\">No</span>\n }\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\"\n class=\"me-2 me-sm-3\"\n [routerLink]=\"[routers.subscriptionDetails, item.id]\">\n <i class=\"fa fa-edit edit-icon\" aria-hidden=\"true\"></i>\n </li>\n @if (item.deleted) {\n <li ngbTooltip=\"Reactivate subscription\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Alpha'\"\n (keydown.enter)=\"openRecoverSubscriptionModal(item)\"\n (click)=\"openRecoverSubscriptionModal(item)\">\n <i class=\"fa fa-trash delete-icon text-success\" aria-hidden=\"true\"></i>\n </li>\n } @else {\n <li ngbTooltip=\"Delete\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Admin.Subscription.Delete'\"\n (keydown.enter)=\"onDelete(item)\"\n (click)=\"onDelete(item)\">\n <i class=\"fa fa-trash delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (totalRecords === 0 && totalRecordsUnFiltered !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (totalRecords !== 0) {\n <span class=\"total-records-count\">{{ 'Label.Total' | transloco }}: {{ totalRecords }}</span>\n }\n </div>\n</div>\n@if (totalRecordsUnFiltered === 0 && isLoaded) {\n <pw-no-data [withImage]=\"true\" [message]=\"'Admin.Subscriptions.NoSubscriptionMessage' | transloco\">\n </pw-no-data>\n}\n<pw-password-validation\n #recoverSubscriptionPasswordModal\n (successEvent)=\"onRecoverConfirmed()\">\n</pw-password-validation>\n\n", styles: [":root{--first: rgb(23 105 225);--second: rgb(54 194 131);--third: rgb(255 171 0);--text: rgb(34 34 34);--tabs_bg: rgb(23 105 225);--tabs_sub_bg: rgb(70, 136, 236);--tabs_text: rgb(255 255 255);--titles: rgb(34 34 34);--sidebar_bg: rgb(0, 48, 63);--sidebar_text: rgb(255 255 255)}@media(min-width:767px){.table-responsive td,.table-responsive th{font-size:11px!important}}.expires-at-width{width:8%}.custom-break-word{word-break:break-word!important}.summary .card{padding-bottom:0}.summary .card .card-body{padding-top:20px;text-align:center}.summary .card .card-body p,.summary .card .card-body h4{font-size:14px}@media(min-width:320px)and (max-width:720px){.primeng-datatable-container .search-filter{display:flex;flex-direction:column;gap:.5rem;justify-content:space-between}}\n"], dependencies: [{ kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i3$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i2.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "directive", type: i4$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i7$2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i2$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "directive", type: i3$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1$1.PasswordValidationComponent, selector: "pw-password-validation", inputs: ["confirmMessage"], outputs: ["successEvent"] }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.NoDataComponent, selector: "pw-no-data", inputs: ["message", "description", "withImage"] }, { kind: "directive", type: i9.DynamicBadgeDirective, selector: "[appDynamicBadge]", inputs: ["appDynamicBadge", "color", "colorByName", "dataName"] }, { kind: "directive", type: i9.RbacAllowDirective, selector: "[rbacAllow]", inputs: ["rbacAllow"] }, { kind: "directive", type: i10.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i3$1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }, { kind: "pipe", type: i15.DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: i15.SinceAgoPipe, name: "sinceAgo" }, { kind: "pipe", type: i15.TextTruncatePipe, name: "textTruncate" }] }); }
|
|
8463
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: SubscriptionsListComponent, isStandalone: false, selector: "pw-subscriptions-list", viewQueries: [{ propertyName: "refProduct", first: true, predicate: ["refProduct"], descendants: true }, { propertyName: "recoverSubscriptionPasswordModal", first: true, predicate: ["recoverSubscriptionPasswordModal"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"row\">\n <div\n class=\"col-12 d-flex justify-content-between align-items-center\">\n <h2 class=\"card-title p-0 float-start\">Subscriptions</h2>\n <a class=\"btn btn-sm btn-outline-primary float-end\"\n id=\"btn-create\"\n [routerLink]=\"[routers.inviteUsers]\">\n <i class=\"fa fa-plus-circle\" aria-hidden=\"true\"></i> Invite a User to a Trial\n </a>\n </div>\n</div>\n<div class=\"p-2 mt-3\">\n <div class=\"w-100 text-center mt-3\">\n @if (!isLoaded) {\n <p-progressSpinner> </p-progressSpinner>\n }\n </div>\n <div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"totalRecordsUnFiltered === 0\">\n <p-table #dt\n [value]=\"subscriptions\"\n [paginator]=\"totalRecords !== 0\"\n [rows]=\"PAGE_SIZE\"\n [lazy]=\"true\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <ng-template pTemplate=\"caption\">\n @if (subscriptionInsights) {\n <div class=\"row mb-4\">\n @for (temp of objectKeys(subscriptionInsights); track temp) {\n <div class=\"col-12 col-lg-3 mt-2 summary\">\n <div class=\"card\">\n <div class=\"card-body\">\n @for (item of subscriptionInsights[temp]; track item) {\n <div>\n <p class=\"mb-0 px-2\">{{ item?.title }}\n @if (item?.info) {\n <span\n [pTooltip]=\"item?.info\">\n <i class=\"fas fa-info-circle\"></i>\n </span>\n }\n </p>\n <h4 class=\"fw-bold mt-2 mb-2\">\n {{ item?.value }}\n </h4>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n <div class=\"search-filter\">\n <div class=\"col-12 col-sm-4\">\n <p-multiSelect inputId=\"subscriptions-list-feature-keys\"\n placeholder=\"Select Feature Keys\"\n [showToggleAll]=\"true\"\n [showHeader]=\"true\"\n [(ngModel)]=\"selectedFeatureKeys\"\n [options]=\"searchOptions\"\n (onChange)=\"onSearchOptionChange($event)\"\n appendTo=\"body\">\n </p-multiSelect>\n </div>\n <div class=\"col-12 col-sm-4\">\n <p-select\n inputId=\"subscriptions-list-status\"\n [options]=\"subscriptionStatus\"\n [placeholder]=\"'Select status'\"\n (onChange)=\"filterByStatus($event.value)\"\n [(ngModel)]=\"status\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"text-end\">\n <label for=\"subscriptions-list-search\" class=\"visually-hidden\">Search Subscription</label>\n <i class=\"fa fa-search mt-2 me-2 d-none\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n id=\"subscriptions-list-search\"\n name=\"subscriptions-list-search\"\n [(ngModel)]=\"searchText\"\n pInputText\n size=\"30\"\n placeholder=\"Search Subscription...\"\n class=\"mw-90\"\n (input)=\"dt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"id\">\n {{ 'Admin.Subscriptions.Id' | transloco }}\n <p-sortIcon field=\"id\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"organisation\">\n {{ 'Admin.Subscriptions.Organization' | transloco }}\n <p-sortIcon field=\"organisation\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_name\">\n {{ 'Admin.Subscriptions.ContactName' | transloco }}\n <p-sortIcon field=\"contact_name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_email\">\n {{ 'Admin.Subscriptions.ContactEmail' | transloco }}\n <p-sortIcon field=\"contact_email\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Product' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Currency' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.PricePerMonth' | transloco }}</th>\n <th scope=\"true\"\n pSortableColumn=\"stripe_customer_id\">\n {{ 'Admin.Subscriptions.Paid' | transloco }}\n <p-sortIcon field=\"stripe_customer_id\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Billing' | transloco }}</th>\n <th scope=\"true\"\n class=\"expires-at-width\">\n {{ 'Admin.Subscriptions.ExpiresAt' | transloco }}\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_total\">\n {{ 'Admin.Subscriptions.SessionsTotal' | transloco }}\n <p-sortIcon field=\"sessions_total\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_last_month\">\n {{ 'Admin.Subscriptions.SessionsTotalLastMonth' | transloco }}\n <p-sortIcon field=\"sessions_last_month\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"deleted_at\">\n {{ 'Admin.Subscriptions.Deleted' | transloco }}\n <p-sortIcon field=\"deleted_at\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"id\">{{ item.id }}</td>\n <td data-head=\"Organization\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.organisation\"\n tooltipPosition=\"top\">{{ item.organisation | textTruncate: 20 }}</span>\n </td>\n <td data-head=\"ContactName\"\n class=\"custom-break-word\">\n {{ item.contact_name }}\n </td>\n <td data-head=\"ContactEmail\">{{ item.contact_email }}</td>\n <td data-head=\"Name\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.productName\"\n tooltipPosition=\"top\">{{ item.productName }}</span>\n </td>\n <td data-head=\"Currency\">\n <span [appDynamicBadge]=\"{ itemsArray: currency, item: item.currency }\"\n color=\"blue-grey\"\n class=\"badge\">{{ item.currency }}</span>\n </td>\n <td data-head=\"Price/M\">\n <span>{{ (item.price / 100).toFixed(2) | currency:item.currency }}\n @if (item?.discount && objectKeys(item?.discount)?.length) {\n <i\n class=\"fa fa-info-circle\"\n aria-hidden=\"true\"\n [pTooltip]=\"item?.discount | json\"\n tooltipPosition=\"top\"\n ></i>\n }\n </span>\n </td>\n <td data-head=\"Paid\">\n @if (item?.unsubscribed_at) {\n <span class=\"badge bg-warning d-inline-flex align-items-center\">\n Unsub\n @if (item?.reason_to_cancel) {\n <i class=\"fa fa-info-circle ms-2\"\n [pTooltip]=\"item.reason_to_cancel\"\n tooltipPosition=\"top\"\n aria-hidden=\"true\"></i>\n }\n </span>\n }\n @if (!item?.unsubscribed_at) {\n @if ((item?.stripe_customer_id || item?.external_payment) && item?.signed_up_at) {\n <span class=\"badge bg-success\">Yes</span>\n } @else {\n <span class=\"badge bg-blue-grey\">No</span>\n }\n }\n @if (item?.pause_collection && objectKeys(item?.pause_collection)?.length) {\n <span\n class=\"ms-2\"\n [pTooltip]=\"item?.pause_collection | json\"\n tooltipPosition=\"top\">\n <i class=\"fas fa-exclamation-triangle text-warning\"></i>\n </span>\n }\n </td>\n <td data-head=\"Billing\">\n <span class=\"badge\"\n [appDynamicBadge]=\"{\n itemsArray: billingFrequency,\n item: item?.billingFrequency\n }\"\n color=\"blue-grey\">{{ item.billingFrequency }}</span>\n </td>\n <td data-head=\"Expires\">\n <span [ngClass]=\"getExpiresAtColor(item)\">{{item?.expires_at | dateFormat}}</span><br />\n <span>{{item?.created_at | sinceAgo}}</span>\n </td>\n <td data-head=\"S#\">{{ item?.sessions_total }}</td>\n <td data-head=\"S/m #\">\n <span [ngClass]=\"{\n 'text-danger fw-bold': item?.sessions_last_month < 3\n }\">{{ item?.sessions_last_month }}</span>\n </td>\n <td data-head=\"Deleted\">\n @if (item?.deleted_at) {\n <span\n class=\"badge bg-danger\">Yes</span>\n }\n @if (!item?.deleted_at) {\n <span\n class=\"badge bg-blue-grey\">No</span>\n }\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\"\n class=\"me-2 me-sm-3\"\n [routerLink]=\"[routers.subscriptionDetails, item.id]\">\n <i class=\"fa fa-edit edit-icon\" aria-hidden=\"true\"></i>\n </li>\n @if (item.deleted) {\n <li ngbTooltip=\"Reactivate subscription\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Alpha'\"\n (keydown.enter)=\"openRecoverSubscriptionModal(item)\"\n (click)=\"openRecoverSubscriptionModal(item)\">\n <i class=\"fa fa-trash delete-icon text-success\" aria-hidden=\"true\"></i>\n </li>\n } @else {\n <li ngbTooltip=\"Delete\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Admin.Subscription.Delete'\"\n (keydown.enter)=\"onDelete(item)\"\n (click)=\"onDelete(item)\">\n <i class=\"fa fa-trash delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (totalRecords === 0 && totalRecordsUnFiltered !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (totalRecords !== 0) {\n <span class=\"total-records-count\">{{ 'Label.Total' | transloco }}: {{ totalRecords }}</span>\n }\n </div>\n</div>\n@if (totalRecordsUnFiltered === 0 && isLoaded) {\n <pw-no-data [withImage]=\"true\" [message]=\"'Admin.Subscriptions.NoSubscriptionMessage' | transloco\">\n </pw-no-data>\n}\n<pw-password-validation\n #recoverSubscriptionPasswordModal\n (successEvent)=\"onRecoverConfirmed()\">\n</pw-password-validation>\n\n", styles: [":root{--first: rgb(23 105 225);--second: rgb(54 194 131);--third: rgb(255 171 0);--text: rgb(34 34 34);--tabs_bg: rgb(23 105 225);--tabs_sub_bg: rgb(70, 136, 236);--tabs_text: rgb(255 255 255);--titles: rgb(34 34 34);--sidebar_bg: rgb(0, 48, 63);--sidebar_text: rgb(255 255 255)}@media(min-width:767px){.table-responsive td,.table-responsive th{font-size:11px!important}}.expires-at-width{width:8%}.custom-break-word{word-break:break-word!important}.summary .card{padding-bottom:0}.summary .card .card-body{padding-top:20px;text-align:center}.summary .card .card-body p,.summary .card .card-body h4{font-size:14px}@media(min-width:320px)and (max-width:720px){.primeng-datatable-container .search-filter{display:flex;flex-direction:column;gap:.5rem;justify-content:space-between}}\n"], dependencies: [{ kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i3$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i2.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "directive", type: i4$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i7$2.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i2$1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { kind: "directive", type: i3$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1$1.PasswordValidationComponent, selector: "pw-password-validation", inputs: ["confirmMessage"], outputs: ["successEvent"] }, { kind: "component", type: i4.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "component", type: i1$1.NoDataComponent, selector: "pw-no-data", inputs: ["message", "description", "withImage"] }, { kind: "directive", type: i9.DynamicBadgeDirective, selector: "[appDynamicBadge]", inputs: ["appDynamicBadge", "color", "colorByName", "dataName"] }, { kind: "directive", type: i9.RbacAllowDirective, selector: "[rbacAllow]", inputs: ["rbacAllow"] }, { kind: "directive", type: i10.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i3$1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i11.TranslocoPipe, name: "transloco" }, { kind: "pipe", type: i15.DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: i15.SinceAgoPipe, name: "sinceAgo" }, { kind: "pipe", type: i15.TextTruncatePipe, name: "textTruncate" }] }); }
|
|
8079
8464
|
}
|
|
8080
8465
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: SubscriptionsListComponent, decorators: [{
|
|
8081
8466
|
type: Component,
|
|
8082
|
-
args: [{ selector: 'pw-subscriptions-list', standalone: false, template: "<div class=\"row\">\n <div\n class=\"col-12 d-flex justify-content-between align-items-center\">\n <h2 class=\"card-title p-0 float-start\">Subscriptions</h2>\n <a class=\"btn btn-sm btn-outline-primary float-end\"\n id=\"btn-create\"\n [routerLink]=\"[routers.inviteUsers]\">\n <i class=\"fa fa-plus-circle\" aria-hidden=\"true\"></i> Invite a User to a Trial\n </a>\n </div>\n</div>\n<div class=\"p-2 mt-3\">\n <div class=\"w-100 text-center mt-3\">\n @if (!isLoaded) {\n <p-progressSpinner> </p-progressSpinner>\n }\n </div>\n <div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"totalRecordsUnFiltered === 0\">\n <p-table #dt\n [value]=\"subscriptions\"\n [paginator]=\"totalRecords !== 0\"\n [rows]=\"PAGE_SIZE\"\n [lazy]=\"true\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <ng-template pTemplate=\"caption\">\n @if (subscriptionInsights) {\n <div class=\"row mb-4\">\n @for (temp of objectKeys(subscriptionInsights); track temp) {\n <div class=\"col-12 col-lg-3 mt-2 summary\">\n <div class=\"card\">\n <div class=\"card-body\">\n @for (item of subscriptionInsights[temp]; track item) {\n <div>\n <p class=\"mb-0 px-2\">{{ item?.title }}\n @if (item?.info) {\n <span\n [pTooltip]=\"item?.info\">\n <i class=\"fas fa-info-circle\"></i>\n </span>\n }\n </p>\n <h4 class=\"fw-bold mt-2 mb-2\">\n {{ item?.value }}\n </h4>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n <div class=\"search-filter\">\n <div class=\"col-12 col-sm-4\">\n <p-multiSelect inputId=\"subscriptions-list-feature-keys\"\n placeholder=\"Select Feature Keys\"\n [showToggleAll]=\"true\"\n [showHeader]=\"true\"\n [(ngModel)]=\"selectedFeatureKeys\"\n [options]=\"searchOptions\"\n (onChange)=\"onSearchOptionChange($event)\"\n appendTo=\"body\">\n </p-multiSelect>\n </div>\n <div class=\"col-12 col-sm-4\">\n <p-select\n inputId=\"subscriptions-list-status\"\n [options]=\"subscriptionStatus\"\n [placeholder]=\"'Select status'\"\n (onChange)=\"filterByStatus($event.value)\"\n [(ngModel)]=\"status\">\n </p-select>\n </div>\n <div class=\"text-end\">\n <label for=\"subscriptions-list-search\" class=\"visually-hidden\">Search Subscription</label>\n <i class=\"fa fa-search mt-2 me-2 d-none\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n id=\"subscriptions-list-search\"\n name=\"subscriptions-list-search\"\n [(ngModel)]=\"searchText\"\n pInputText\n size=\"30\"\n placeholder=\"Search Subscription...\"\n class=\"mw-90\"\n (input)=\"dt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"id\">\n {{ 'Admin.Subscriptions.Id' | transloco }}\n <p-sortIcon field=\"id\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"organisation\">\n {{ 'Admin.Subscriptions.Organization' | transloco }}\n <p-sortIcon field=\"organisation\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_name\">\n {{ 'Admin.Subscriptions.ContactName' | transloco }}\n <p-sortIcon field=\"contact_name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_email\">\n {{ 'Admin.Subscriptions.ContactEmail' | transloco }}\n <p-sortIcon field=\"contact_email\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Product' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Currency' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.PricePerMonth' | transloco }}</th>\n <th scope=\"true\"\n pSortableColumn=\"stripe_customer_id\">\n {{ 'Admin.Subscriptions.Paid' | transloco }}\n <p-sortIcon field=\"stripe_customer_id\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Billing' | transloco }}</th>\n <th scope=\"true\"\n class=\"expires-at-width\">\n {{ 'Admin.Subscriptions.ExpiresAt' | transloco }}\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_total\">\n {{ 'Admin.Subscriptions.SessionsTotal' | transloco }}\n <p-sortIcon field=\"sessions_total\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_last_month\">\n {{ 'Admin.Subscriptions.SessionsTotalLastMonth' | transloco }}\n <p-sortIcon field=\"sessions_last_month\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"deleted_at\">\n {{ 'Admin.Subscriptions.Deleted' | transloco }}\n <p-sortIcon field=\"deleted_at\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"id\">{{ item.id }}</td>\n <td data-head=\"Organization\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.organisation\"\n tooltipPosition=\"top\">{{ item.organisation | textTruncate: 20 }}</span>\n </td>\n <td data-head=\"ContactName\"\n class=\"custom-break-word\">\n {{ item.contact_name }}\n </td>\n <td data-head=\"ContactEmail\">{{ item.contact_email }}</td>\n <td data-head=\"Name\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.productName\"\n tooltipPosition=\"top\">{{ item.productName }}</span>\n </td>\n <td data-head=\"Currency\">\n <span [appDynamicBadge]=\"{ itemsArray: currency, item: item.currency }\"\n color=\"blue-grey\"\n class=\"badge\">{{ item.currency }}</span>\n </td>\n <td data-head=\"Price/M\">\n <span>{{ (item.price / 100).toFixed(2) | currency:item.currency }}\n @if (item?.discount && objectKeys(item?.discount)?.length) {\n <i\n class=\"fa fa-info-circle\"\n aria-hidden=\"true\"\n [pTooltip]=\"item?.discount | json\"\n tooltipPosition=\"top\"\n ></i>\n }\n </span>\n </td>\n <td data-head=\"Paid\">\n @if (item?.unsubscribed_at) {\n <span class=\"badge bg-warning d-inline-flex align-items-center\">\n Unsub\n @if (item?.reason_to_cancel) {\n <i class=\"fa fa-info-circle ms-2\"\n [pTooltip]=\"item.reason_to_cancel\"\n tooltipPosition=\"top\"\n aria-hidden=\"true\"></i>\n }\n </span>\n }\n @if (!item?.unsubscribed_at) {\n @if ((item?.stripe_customer_id || item?.external_payment) && item?.signed_up_at) {\n <span class=\"badge bg-success\">Yes</span>\n } @else {\n <span class=\"badge bg-blue-grey\">No</span>\n }\n }\n @if (item?.pause_collection && objectKeys(item?.pause_collection)?.length) {\n <span\n class=\"ms-2\"\n [pTooltip]=\"item?.pause_collection | json\"\n tooltipPosition=\"top\">\n <i class=\"fas fa-exclamation-triangle text-warning\"></i>\n </span>\n }\n </td>\n <td data-head=\"Billing\">\n <span class=\"badge\"\n [appDynamicBadge]=\"{\n itemsArray: billingFrequency,\n item: item?.billingFrequency\n }\"\n color=\"blue-grey\">{{ item.billingFrequency }}</span>\n </td>\n <td data-head=\"Expires\">\n <span [ngClass]=\"getExpiresAtColor(item)\">{{item?.expires_at | dateFormat}}</span><br />\n <span>{{item?.created_at | sinceAgo}}</span>\n </td>\n <td data-head=\"S#\">{{ item?.sessions_total }}</td>\n <td data-head=\"S/m #\">\n <span [ngClass]=\"{\n 'text-danger fw-bold': item?.sessions_last_month < 3\n }\">{{ item?.sessions_last_month }}</span>\n </td>\n <td data-head=\"Deleted\">\n @if (item?.deleted_at) {\n <span\n class=\"badge bg-danger\">Yes</span>\n }\n @if (!item?.deleted_at) {\n <span\n class=\"badge bg-blue-grey\">No</span>\n }\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\"\n class=\"me-2 me-sm-3\"\n [routerLink]=\"[routers.subscriptionDetails, item.id]\">\n <i class=\"fa fa-edit edit-icon\" aria-hidden=\"true\"></i>\n </li>\n @if (item.deleted) {\n <li ngbTooltip=\"Reactivate subscription\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Alpha'\"\n (keydown.enter)=\"openRecoverSubscriptionModal(item)\"\n (click)=\"openRecoverSubscriptionModal(item)\">\n <i class=\"fa fa-trash delete-icon text-success\" aria-hidden=\"true\"></i>\n </li>\n } @else {\n <li ngbTooltip=\"Delete\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Admin.Subscription.Delete'\"\n (keydown.enter)=\"onDelete(item)\"\n (click)=\"onDelete(item)\">\n <i class=\"fa fa-trash delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (totalRecords === 0 && totalRecordsUnFiltered !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (totalRecords !== 0) {\n <span class=\"total-records-count\">{{ 'Label.Total' | transloco }}: {{ totalRecords }}</span>\n }\n </div>\n</div>\n@if (totalRecordsUnFiltered === 0 && isLoaded) {\n <pw-no-data [withImage]=\"true\" [message]=\"'Admin.Subscriptions.NoSubscriptionMessage' | transloco\">\n </pw-no-data>\n}\n<pw-password-validation\n #recoverSubscriptionPasswordModal\n (successEvent)=\"onRecoverConfirmed()\">\n</pw-password-validation>\n\n", styles: [":root{--first: rgb(23 105 225);--second: rgb(54 194 131);--third: rgb(255 171 0);--text: rgb(34 34 34);--tabs_bg: rgb(23 105 225);--tabs_sub_bg: rgb(70, 136, 236);--tabs_text: rgb(255 255 255);--titles: rgb(34 34 34);--sidebar_bg: rgb(0, 48, 63);--sidebar_text: rgb(255 255 255)}@media(min-width:767px){.table-responsive td,.table-responsive th{font-size:11px!important}}.expires-at-width{width:8%}.custom-break-word{word-break:break-word!important}.summary .card{padding-bottom:0}.summary .card .card-body{padding-top:20px;text-align:center}.summary .card .card-body p,.summary .card .card-body h4{font-size:14px}@media(min-width:320px)and (max-width:720px){.primeng-datatable-container .search-filter{display:flex;flex-direction:column;gap:.5rem;justify-content:space-between}}\n"] }]
|
|
8467
|
+
args: [{ selector: 'pw-subscriptions-list', standalone: false, template: "<div class=\"row\">\n <div\n class=\"col-12 d-flex justify-content-between align-items-center\">\n <h2 class=\"card-title p-0 float-start\">Subscriptions</h2>\n <a class=\"btn btn-sm btn-outline-primary float-end\"\n id=\"btn-create\"\n [routerLink]=\"[routers.inviteUsers]\">\n <i class=\"fa fa-plus-circle\" aria-hidden=\"true\"></i> Invite a User to a Trial\n </a>\n </div>\n</div>\n<div class=\"p-2 mt-3\">\n <div class=\"w-100 text-center mt-3\">\n @if (!isLoaded) {\n <p-progressSpinner> </p-progressSpinner>\n }\n </div>\n <div class=\"primeng-datatable-container table-responsive mt-0\"\n [class.hideTable]=\"totalRecordsUnFiltered === 0\">\n <p-table #dt\n [value]=\"subscriptions\"\n [paginator]=\"totalRecords !== 0\"\n [rows]=\"PAGE_SIZE\"\n [lazy]=\"true\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [filterDelay]=\"1000\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [customSort]=\"true\">\n <ng-template pTemplate=\"caption\">\n @if (subscriptionInsights) {\n <div class=\"row mb-4\">\n @for (temp of objectKeys(subscriptionInsights); track temp) {\n <div class=\"col-12 col-lg-3 mt-2 summary\">\n <div class=\"card\">\n <div class=\"card-body\">\n @for (item of subscriptionInsights[temp]; track item) {\n <div>\n <p class=\"mb-0 px-2\">{{ item?.title }}\n @if (item?.info) {\n <span\n [pTooltip]=\"item?.info\">\n <i class=\"fas fa-info-circle\"></i>\n </span>\n }\n </p>\n <h4 class=\"fw-bold mt-2 mb-2\">\n {{ item?.value }}\n </h4>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n <div class=\"search-filter\">\n <div class=\"col-12 col-sm-4\">\n <p-multiSelect inputId=\"subscriptions-list-feature-keys\"\n placeholder=\"Select Feature Keys\"\n [showToggleAll]=\"true\"\n [showHeader]=\"true\"\n [(ngModel)]=\"selectedFeatureKeys\"\n [options]=\"searchOptions\"\n (onChange)=\"onSearchOptionChange($event)\"\n appendTo=\"body\">\n </p-multiSelect>\n </div>\n <div class=\"col-12 col-sm-4\">\n <p-select\n inputId=\"subscriptions-list-status\"\n [options]=\"subscriptionStatus\"\n [placeholder]=\"'Select status'\"\n (onChange)=\"filterByStatus($event.value)\"\n [(ngModel)]=\"status\"\n appendTo=\"body\">\n </p-select>\n </div>\n <div class=\"text-end\">\n <label for=\"subscriptions-list-search\" class=\"visually-hidden\">Search Subscription</label>\n <i class=\"fa fa-search mt-2 me-2 d-none\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n id=\"subscriptions-list-search\"\n name=\"subscriptions-list-search\"\n [(ngModel)]=\"searchText\"\n pInputText\n size=\"30\"\n placeholder=\"Search Subscription...\"\n class=\"mw-90\"\n (input)=\"dt.filterGlobal($event.target.value, 'contains')\" />\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"true\"\n pSortableColumn=\"id\">\n {{ 'Admin.Subscriptions.Id' | transloco }}\n <p-sortIcon field=\"id\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"organisation\">\n {{ 'Admin.Subscriptions.Organization' | transloco }}\n <p-sortIcon field=\"organisation\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_name\">\n {{ 'Admin.Subscriptions.ContactName' | transloco }}\n <p-sortIcon field=\"contact_name\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"contact_email\">\n {{ 'Admin.Subscriptions.ContactEmail' | transloco }}\n <p-sortIcon field=\"contact_email\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Product' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Currency' | transloco }}</th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.PricePerMonth' | transloco }}</th>\n <th scope=\"true\"\n pSortableColumn=\"stripe_customer_id\">\n {{ 'Admin.Subscriptions.Paid' | transloco }}\n <p-sortIcon field=\"stripe_customer_id\"></p-sortIcon>\n </th>\n <th scope=\"true\">{{ 'Admin.Subscriptions.Billing' | transloco }}</th>\n <th scope=\"true\"\n class=\"expires-at-width\">\n {{ 'Admin.Subscriptions.ExpiresAt' | transloco }}\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_total\">\n {{ 'Admin.Subscriptions.SessionsTotal' | transloco }}\n <p-sortIcon field=\"sessions_total\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"sessions_last_month\">\n {{ 'Admin.Subscriptions.SessionsTotalLastMonth' | transloco }}\n <p-sortIcon field=\"sessions_last_month\"></p-sortIcon>\n </th>\n <th scope=\"true\"\n pSortableColumn=\"deleted_at\">\n {{ 'Admin.Subscriptions.Deleted' | transloco }}\n <p-sortIcon field=\"deleted_at\"></p-sortIcon>\n </th>\n <th class=\"actions-list-two\"\n scope=\"true\">{{ 'Label.Actions' | transloco }}</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\"\n let-item>\n <tr>\n <td data-head=\"id\">{{ item.id }}</td>\n <td data-head=\"Organization\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.organisation\"\n tooltipPosition=\"top\">{{ item.organisation | textTruncate: 20 }}</span>\n </td>\n <td data-head=\"ContactName\"\n class=\"custom-break-word\">\n {{ item.contact_name }}\n </td>\n <td data-head=\"ContactEmail\">{{ item.contact_email }}</td>\n <td data-head=\"Name\">\n <span class=\"cursor-pointer custom-break-word\"\n [pTooltip]=\"item.productName\"\n tooltipPosition=\"top\">{{ item.productName }}</span>\n </td>\n <td data-head=\"Currency\">\n <span [appDynamicBadge]=\"{ itemsArray: currency, item: item.currency }\"\n color=\"blue-grey\"\n class=\"badge\">{{ item.currency }}</span>\n </td>\n <td data-head=\"Price/M\">\n <span>{{ (item.price / 100).toFixed(2) | currency:item.currency }}\n @if (item?.discount && objectKeys(item?.discount)?.length) {\n <i\n class=\"fa fa-info-circle\"\n aria-hidden=\"true\"\n [pTooltip]=\"item?.discount | json\"\n tooltipPosition=\"top\"\n ></i>\n }\n </span>\n </td>\n <td data-head=\"Paid\">\n @if (item?.unsubscribed_at) {\n <span class=\"badge bg-warning d-inline-flex align-items-center\">\n Unsub\n @if (item?.reason_to_cancel) {\n <i class=\"fa fa-info-circle ms-2\"\n [pTooltip]=\"item.reason_to_cancel\"\n tooltipPosition=\"top\"\n aria-hidden=\"true\"></i>\n }\n </span>\n }\n @if (!item?.unsubscribed_at) {\n @if ((item?.stripe_customer_id || item?.external_payment) && item?.signed_up_at) {\n <span class=\"badge bg-success\">Yes</span>\n } @else {\n <span class=\"badge bg-blue-grey\">No</span>\n }\n }\n @if (item?.pause_collection && objectKeys(item?.pause_collection)?.length) {\n <span\n class=\"ms-2\"\n [pTooltip]=\"item?.pause_collection | json\"\n tooltipPosition=\"top\">\n <i class=\"fas fa-exclamation-triangle text-warning\"></i>\n </span>\n }\n </td>\n <td data-head=\"Billing\">\n <span class=\"badge\"\n [appDynamicBadge]=\"{\n itemsArray: billingFrequency,\n item: item?.billingFrequency\n }\"\n color=\"blue-grey\">{{ item.billingFrequency }}</span>\n </td>\n <td data-head=\"Expires\">\n <span [ngClass]=\"getExpiresAtColor(item)\">{{item?.expires_at | dateFormat}}</span><br />\n <span>{{item?.created_at | sinceAgo}}</span>\n </td>\n <td data-head=\"S#\">{{ item?.sessions_total }}</td>\n <td data-head=\"S/m #\">\n <span [ngClass]=\"{\n 'text-danger fw-bold': item?.sessions_last_month < 3\n }\">{{ item?.sessions_last_month }}</span>\n </td>\n <td data-head=\"Deleted\">\n @if (item?.deleted_at) {\n <span\n class=\"badge bg-danger\">Yes</span>\n }\n @if (!item?.deleted_at) {\n <span\n class=\"badge bg-blue-grey\">No</span>\n }\n </td>\n <td data-head=\"Action\">\n <ul class=\"list-unstyled list-inline list-action\">\n <li ngbTooltip=\"Edit\"\n class=\"me-2 me-sm-3\"\n [routerLink]=\"[routers.subscriptionDetails, item.id]\">\n <i class=\"fa fa-edit edit-icon\" aria-hidden=\"true\"></i>\n </li>\n @if (item.deleted) {\n <li ngbTooltip=\"Reactivate subscription\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Alpha'\"\n (keydown.enter)=\"openRecoverSubscriptionModal(item)\"\n (click)=\"openRecoverSubscriptionModal(item)\">\n <i class=\"fa fa-trash delete-icon text-success\" aria-hidden=\"true\"></i>\n </li>\n } @else {\n <li ngbTooltip=\"Delete\"\n class=\"me-2 me-sm-3\"\n *rbacAllow=\"'Pages.Admin.Subscription.Delete'\"\n (keydown.enter)=\"onDelete(item)\"\n (click)=\"onDelete(item)\">\n <i class=\"fa fa-trash delete-icon\" aria-hidden=\"true\"></i>\n </li>\n }\n </ul>\n </td>\n </tr>\n </ng-template>\n </p-table>\n @if (totalRecords === 0 && totalRecordsUnFiltered !== 0) {\n <div>\n <pw-no-data [withImage]=\"true\" [message]=\"'Search.NoDataMessage' | transloco\" [description]=\"'Search.NoDataDescription' | transloco\" >\n </pw-no-data>\n </div>\n }\n @if (totalRecords !== 0) {\n <span class=\"total-records-count\">{{ 'Label.Total' | transloco }}: {{ totalRecords }}</span>\n }\n </div>\n</div>\n@if (totalRecordsUnFiltered === 0 && isLoaded) {\n <pw-no-data [withImage]=\"true\" [message]=\"'Admin.Subscriptions.NoSubscriptionMessage' | transloco\">\n </pw-no-data>\n}\n<pw-password-validation\n #recoverSubscriptionPasswordModal\n (successEvent)=\"onRecoverConfirmed()\">\n</pw-password-validation>\n\n", styles: [":root{--first: rgb(23 105 225);--second: rgb(54 194 131);--third: rgb(255 171 0);--text: rgb(34 34 34);--tabs_bg: rgb(23 105 225);--tabs_sub_bg: rgb(70, 136, 236);--tabs_text: rgb(255 255 255);--titles: rgb(34 34 34);--sidebar_bg: rgb(0, 48, 63);--sidebar_text: rgb(255 255 255)}@media(min-width:767px){.table-responsive td,.table-responsive th{font-size:11px!important}}.expires-at-width{width:8%}.custom-break-word{word-break:break-word!important}.summary .card{padding-bottom:0}.summary .card .card-body{padding-top:20px;text-align:center}.summary .card .card-body p,.summary .card .card-body h4{font-size:14px}@media(min-width:320px)and (max-width:720px){.primeng-datatable-container .search-filter{display:flex;flex-direction:column;gap:.5rem;justify-content:space-between}}\n"] }]
|
|
8083
8468
|
}], ctorParameters: () => [{ type: i1$2.ProductService }, { type: i1.AdminService }, { type: i6.UntypedFormBuilder }, { type: i0.Injector }, { type: i0.ChangeDetectorRef }, { type: i1$2.CommonService }], propDecorators: { refProduct: [{
|
|
8084
8469
|
type: ViewChild,
|
|
8085
8470
|
args: ['refProduct']
|
|
@@ -10004,62 +10389,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImpor
|
|
|
10004
10389
|
args: [{ selector: 'pw-users-information-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"users-information-sidebar\">\n <p class=\"users-information-sidebar__lead\">\n {{ 'Admin.Users.SidebarIntro.Lead' | transloco }}\n </p>\n <ul class=\"users-information-sidebar__list\">\n <li>{{ 'Admin.Users.SidebarIntro.FilterHint' | transloco }}</li>\n <li>{{ 'Admin.Users.SidebarIntro.SearchHint' | transloco }}</li>\n </ul>\n</div>\n", styles: [".users-information-sidebar{color:#333;padding:0 .25rem}.users-information-sidebar__lead{line-height:1.45;margin:0 0 1rem}.users-information-sidebar__list{line-height:1.45;margin:0;padding-left:1.15rem}\n"] }]
|
|
10005
10390
|
}] });
|
|
10006
10391
|
|
|
10007
|
-
class
|
|
10008
|
-
constructor(adminService,
|
|
10392
|
+
class UsersListBaseComponent extends AppBaseComponent {
|
|
10393
|
+
constructor(adminService, productService, injector, cdr, sidebarService) {
|
|
10009
10394
|
super(injector);
|
|
10010
10395
|
this.adminService = adminService;
|
|
10011
|
-
this.toaster = toaster;
|
|
10012
10396
|
this.productService = productService;
|
|
10013
|
-
this.authService = authService;
|
|
10014
10397
|
this.cdr = cdr;
|
|
10015
|
-
this.windowService = windowService;
|
|
10016
10398
|
this.sidebarService = sidebarService;
|
|
10017
10399
|
this.page = 1;
|
|
10018
10400
|
this.filterByProducts = [];
|
|
10019
10401
|
this.filterByRoles = [];
|
|
10402
|
+
this.filterByFeatureKeys = [];
|
|
10403
|
+
this.filterByAlphaBeta = [];
|
|
10020
10404
|
this.users = [];
|
|
10021
10405
|
this.allUsers = [];
|
|
10022
10406
|
this.loading = true;
|
|
10023
10407
|
this.isLoaded = false;
|
|
10024
|
-
this.searchOptions = [
|
|
10025
|
-
{
|
|
10026
|
-
label: 'Role',
|
|
10027
|
-
value: 'role'
|
|
10028
|
-
},
|
|
10029
|
-
{
|
|
10030
|
-
label: 'Product',
|
|
10031
|
-
value: 'product'
|
|
10032
|
-
},
|
|
10033
|
-
{
|
|
10034
|
-
label: 'Feature Key',
|
|
10035
|
-
value: 'feature_key'
|
|
10036
|
-
}
|
|
10037
|
-
];
|
|
10038
|
-
this.searchText = '';
|
|
10039
10408
|
this.searchDataSource = [];
|
|
10040
10409
|
this.featureKeysList = [];
|
|
10041
|
-
this.filterByFeatureKeys = [];
|
|
10042
10410
|
this.criteria = [];
|
|
10043
|
-
this.routers = ROUTERS;
|
|
10044
|
-
this.masterProductId = this.appConfig.master_subscription.product_id;
|
|
10045
10411
|
this.totalRecordsUnFiltered = 0;
|
|
10412
|
+
this.searchText = '';
|
|
10413
|
+
this.masterProductId = this.appConfig.master_subscription.product_id;
|
|
10414
|
+
this.searchOptions = [
|
|
10415
|
+
{ label: 'Role', value: 'role' },
|
|
10416
|
+
{ label: 'Product', value: 'product' },
|
|
10417
|
+
{ label: 'Feature Key', value: 'feature_key' },
|
|
10418
|
+
{ label: 'Alpha / Beta', value: 'alpha_beta' }
|
|
10419
|
+
];
|
|
10420
|
+
this.alphaBetaOptions = [
|
|
10421
|
+
{ label: 'Alpha', value: 'alpha' },
|
|
10422
|
+
{ label: 'Beta', value: 'beta' }
|
|
10423
|
+
];
|
|
10046
10424
|
this.getTableState();
|
|
10047
10425
|
}
|
|
10048
|
-
ngOnInit() {
|
|
10049
|
-
this.subscriptionId = PermissionService.selectedSubscription?.id;
|
|
10050
|
-
this.chatPermission = `${PermissionService.selectedProduct?.permission}.${PermissionService.selectedProduct?.feature_key}`;
|
|
10051
|
-
this.getRoles();
|
|
10052
|
-
this.getProducts();
|
|
10053
|
-
this.userStore().subscribe(response => {
|
|
10054
|
-
this.currentUser = response;
|
|
10055
|
-
this.cdr.markForCheck();
|
|
10056
|
-
});
|
|
10057
|
-
this.sidebarService.registerContent({
|
|
10058
|
-
id: 'users-info',
|
|
10059
|
-
title: 'Users Information',
|
|
10060
|
-
component: UsersInformationSidebarComponent
|
|
10061
|
-
});
|
|
10062
|
-
}
|
|
10063
10426
|
getTableState() {
|
|
10064
10427
|
this.localStorage.getItem$('user-list-state').subscribe(result => {
|
|
10065
10428
|
if (result) {
|
|
@@ -10088,20 +10451,21 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10088
10451
|
}
|
|
10089
10452
|
}
|
|
10090
10453
|
initializeCriteria(res) {
|
|
10091
|
-
|
|
10454
|
+
const val = res?.selectedOption?.value;
|
|
10455
|
+
if (val === 'feature_key')
|
|
10092
10456
|
this.criteria = res?.filterByFeatureKeys ?? [];
|
|
10093
|
-
|
|
10094
|
-
if (res?.selectedOption?.value === 'product') {
|
|
10457
|
+
if (val === 'product')
|
|
10095
10458
|
this.criteria = res?.filterByProducts ?? [];
|
|
10096
|
-
|
|
10097
|
-
if (res?.selectedOption?.value === 'role') {
|
|
10459
|
+
if (val === 'role')
|
|
10098
10460
|
this.criteria = res?.filterByRoles ?? [];
|
|
10099
|
-
|
|
10461
|
+
if (val === 'alpha_beta')
|
|
10462
|
+
this.criteria = res?.filterByAlphaBeta ?? [];
|
|
10100
10463
|
}
|
|
10101
10464
|
initializeFilters(res) {
|
|
10102
10465
|
this.filterByFeatureKeys = res?.filterByFeatureKeys ?? [];
|
|
10103
10466
|
this.filterByProducts = res?.filterByProducts ?? [];
|
|
10104
10467
|
this.filterByRoles = res?.filterByRoles ?? [];
|
|
10468
|
+
this.filterByAlphaBeta = res?.filterByAlphaBeta ?? [];
|
|
10105
10469
|
}
|
|
10106
10470
|
onLazyLoad(event) {
|
|
10107
10471
|
const pageDetails = HelperService.onTableLazyLoad(event);
|
|
@@ -10116,12 +10480,7 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10116
10480
|
}
|
|
10117
10481
|
getRoles() {
|
|
10118
10482
|
this.adminService.getRoles({}, this.subscriptionId).subscribe(response => {
|
|
10119
|
-
this.allRoles = response.roles.map(item => {
|
|
10120
|
-
return {
|
|
10121
|
-
label: item.name,
|
|
10122
|
-
value: item.id
|
|
10123
|
-
};
|
|
10124
|
-
});
|
|
10483
|
+
this.allRoles = response.roles.map(item => ({ label: item.name, value: item.id }));
|
|
10125
10484
|
this.cdr.markForCheck();
|
|
10126
10485
|
});
|
|
10127
10486
|
}
|
|
@@ -10133,22 +10492,15 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10133
10492
|
})
|
|
10134
10493
|
.subscribe(resp => {
|
|
10135
10494
|
if (resp.products) {
|
|
10136
|
-
this.productList = resp.products.map(
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
label: element.feature_key ?? ''
|
|
10146
|
-
};
|
|
10147
|
-
});
|
|
10148
|
-
this.productList.unshift({
|
|
10149
|
-
label: 'None',
|
|
10150
|
-
value: null
|
|
10151
|
-
});
|
|
10495
|
+
this.productList = resp.products.map(e => ({
|
|
10496
|
+
value: e.id,
|
|
10497
|
+
label: e.name ?? ''
|
|
10498
|
+
}));
|
|
10499
|
+
this.featureKeysList = resp.products.map(e => ({
|
|
10500
|
+
value: e.feature_key,
|
|
10501
|
+
label: e.feature_key ?? ''
|
|
10502
|
+
}));
|
|
10503
|
+
this.productList.unshift({ label: 'None', value: null });
|
|
10152
10504
|
}
|
|
10153
10505
|
this.cdr.markForCheck();
|
|
10154
10506
|
});
|
|
@@ -10161,6 +10513,10 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10161
10513
|
feature_keys: this.filterByFeatureKeys.join() || '',
|
|
10162
10514
|
subscription_id: this.subscriptionId
|
|
10163
10515
|
};
|
|
10516
|
+
if (this.filterByAlphaBeta.includes('alpha'))
|
|
10517
|
+
params.alpha = true;
|
|
10518
|
+
if (this.filterByAlphaBeta.includes('beta'))
|
|
10519
|
+
params.beta = true;
|
|
10164
10520
|
this.adminService.getUsers(paging, params).subscribe(response => {
|
|
10165
10521
|
this.users = response.users;
|
|
10166
10522
|
this.allUsers = [...this.users];
|
|
@@ -10171,70 +10527,86 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10171
10527
|
this.cdr.markForCheck();
|
|
10172
10528
|
});
|
|
10173
10529
|
}
|
|
10530
|
+
}
|
|
10531
|
+
|
|
10532
|
+
class UserListComponent extends UsersListBaseComponent {
|
|
10533
|
+
constructor(adminService, toaster, productService, injector, authService, cdr, windowService, sidebarService) {
|
|
10534
|
+
super(adminService, productService, injector, cdr, sidebarService);
|
|
10535
|
+
this.toaster = toaster;
|
|
10536
|
+
this.authService = authService;
|
|
10537
|
+
this.windowService = windowService;
|
|
10538
|
+
this.routers = ROUTERS;
|
|
10539
|
+
}
|
|
10540
|
+
ngOnInit() {
|
|
10541
|
+
this.subscriptionId = PermissionService.selectedSubscription?.id;
|
|
10542
|
+
this.chatPermission = `${PermissionService.selectedProduct?.permission}.${PermissionService.selectedProduct?.feature_key}`;
|
|
10543
|
+
this.getRoles();
|
|
10544
|
+
this.getProducts();
|
|
10545
|
+
this.userStore().subscribe(response => {
|
|
10546
|
+
this.currentUser = response;
|
|
10547
|
+
this.cdr.markForCheck();
|
|
10548
|
+
});
|
|
10549
|
+
this.sidebarService.registerContent({
|
|
10550
|
+
id: 'users-info',
|
|
10551
|
+
title: 'Users Information',
|
|
10552
|
+
component: UsersInformationSidebarComponent
|
|
10553
|
+
});
|
|
10554
|
+
}
|
|
10174
10555
|
onSearchOptionChange($event) {
|
|
10175
10556
|
this.criteria = [];
|
|
10176
10557
|
this.selectedOption = this.searchOptions.find(x => x.value === $event.value[0]);
|
|
10177
|
-
if (this.selectedOption
|
|
10558
|
+
if (this.selectedOption?.value === 'role')
|
|
10178
10559
|
this.searchDataSource = this.allRoles;
|
|
10179
|
-
|
|
10180
|
-
else if (this.selectedOption && this.selectedOption.value === 'product') {
|
|
10560
|
+
else if (this.selectedOption?.value === 'product')
|
|
10181
10561
|
this.searchDataSource = this.productList;
|
|
10182
|
-
|
|
10183
|
-
else if (this.selectedOption && this.selectedOption.value === 'feature_key') {
|
|
10562
|
+
else if (this.selectedOption?.value === 'feature_key')
|
|
10184
10563
|
this.searchDataSource = this.featureKeysList;
|
|
10185
|
-
|
|
10564
|
+
else if (this.selectedOption?.value === 'alpha_beta')
|
|
10565
|
+
this.searchDataSource = this.alphaBetaOptions;
|
|
10186
10566
|
else {
|
|
10187
10567
|
this.searchDataSource = [];
|
|
10188
10568
|
this.clearFilters();
|
|
10189
|
-
this.getUsers({
|
|
10190
|
-
page: this.page,
|
|
10191
|
-
page_size: PAGE_SIZE
|
|
10192
|
-
});
|
|
10569
|
+
this.getUsers({ page: this.page, page_size: PAGE_SIZE });
|
|
10193
10570
|
}
|
|
10194
10571
|
}
|
|
10195
10572
|
clearFilters() {
|
|
10196
10573
|
this.filterByProducts = [];
|
|
10197
10574
|
this.filterByFeatureKeys = [];
|
|
10198
10575
|
this.filterByRoles = [];
|
|
10576
|
+
this.filterByAlphaBeta = [];
|
|
10199
10577
|
}
|
|
10200
10578
|
onSearchCriteria(evt) {
|
|
10201
10579
|
this.clearFilters();
|
|
10202
|
-
if (this.selectedOption.value === 'role')
|
|
10580
|
+
if (this.selectedOption.value === 'role')
|
|
10203
10581
|
this.onRoleChange(evt);
|
|
10204
|
-
|
|
10205
|
-
else if (this.selectedOption.value === 'product') {
|
|
10582
|
+
else if (this.selectedOption.value === 'product')
|
|
10206
10583
|
this.onProductChange(evt);
|
|
10207
|
-
|
|
10208
|
-
else if (this.selectedOption.value === 'feature_key') {
|
|
10584
|
+
else if (this.selectedOption.value === 'feature_key')
|
|
10209
10585
|
this.onFeatureKeyChange(evt);
|
|
10210
|
-
|
|
10211
|
-
|
|
10586
|
+
else if (this.selectedOption.value === 'alpha_beta')
|
|
10587
|
+
this.onAlphaBetaChange(evt);
|
|
10588
|
+
else
|
|
10212
10589
|
this.searchDataSource = [];
|
|
10213
|
-
}
|
|
10214
10590
|
}
|
|
10215
10591
|
onRoleChange(event) {
|
|
10216
10592
|
this.clearFilters();
|
|
10217
10593
|
this.filterByRoles = event.value;
|
|
10218
|
-
this.getUsers({
|
|
10219
|
-
page: this.page,
|
|
10220
|
-
page_size: PAGE_SIZE
|
|
10221
|
-
});
|
|
10594
|
+
this.getUsers({ page: this.page, page_size: PAGE_SIZE });
|
|
10222
10595
|
}
|
|
10223
10596
|
onProductChange(event) {
|
|
10224
10597
|
this.clearFilters();
|
|
10225
10598
|
this.filterByProducts = event.value;
|
|
10226
|
-
this.getUsers({
|
|
10227
|
-
page: this.page,
|
|
10228
|
-
page_size: PAGE_SIZE
|
|
10229
|
-
});
|
|
10599
|
+
this.getUsers({ page: this.page, page_size: PAGE_SIZE });
|
|
10230
10600
|
}
|
|
10231
10601
|
onFeatureKeyChange(event) {
|
|
10232
10602
|
this.clearFilters();
|
|
10233
10603
|
this.filterByFeatureKeys = event.value;
|
|
10234
|
-
this.getUsers({
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10604
|
+
this.getUsers({ page: this.page, page_size: PAGE_SIZE });
|
|
10605
|
+
}
|
|
10606
|
+
onAlphaBetaChange(event) {
|
|
10607
|
+
this.clearFilters();
|
|
10608
|
+
this.filterByAlphaBeta = event.value;
|
|
10609
|
+
this.getUsers({ page: this.page, page_size: PAGE_SIZE });
|
|
10238
10610
|
}
|
|
10239
10611
|
onDelete(user) {
|
|
10240
10612
|
this.userToDelete = user;
|
|
@@ -10246,11 +10618,7 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10246
10618
|
.subscribe(() => {
|
|
10247
10619
|
this.users = this.users.filter(x => x.id !== this.userToDelete.id);
|
|
10248
10620
|
this.toaster.success(this.translation.translate('Admin.Users.DeletedMessage'));
|
|
10249
|
-
this.getUsers({
|
|
10250
|
-
page: this.page,
|
|
10251
|
-
page_size: PAGE_SIZE,
|
|
10252
|
-
search: this.searchText
|
|
10253
|
-
});
|
|
10621
|
+
this.getUsers({ page: this.page, page_size: PAGE_SIZE, search: this.searchText });
|
|
10254
10622
|
this.cdr.markForCheck();
|
|
10255
10623
|
})
|
|
10256
10624
|
.add(() => {
|
|
@@ -10273,10 +10641,7 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10273
10641
|
})
|
|
10274
10642
|
.subscribe(_ => {
|
|
10275
10643
|
this.toast.success(`You're now being logged in as another user...`);
|
|
10276
|
-
|
|
10277
|
-
setTimeout(() => {
|
|
10278
|
-
this.windowService.replaceUrl('/home');
|
|
10279
|
-
}, 2000);
|
|
10644
|
+
setTimeout(() => this.windowService.replaceUrl('/home'), 2000);
|
|
10280
10645
|
this.cdr.markForCheck();
|
|
10281
10646
|
});
|
|
10282
10647
|
this.cdr.markForCheck();
|
|
@@ -10289,6 +10654,7 @@ class UserListComponent extends AppBaseComponent {
|
|
|
10289
10654
|
filterByFeatureKeys: this.filterByFeatureKeys,
|
|
10290
10655
|
filterByRoles: this.filterByRoles,
|
|
10291
10656
|
filterByProducts: this.filterByProducts,
|
|
10657
|
+
filterByAlphaBeta: this.filterByAlphaBeta,
|
|
10292
10658
|
selectedOption: this.selectedOption,
|
|
10293
10659
|
searchDataSource: this.searchDataSource
|
|
10294
10660
|
};
|
|
@@ -10427,6 +10793,54 @@ const routes = [
|
|
|
10427
10793
|
}
|
|
10428
10794
|
]
|
|
10429
10795
|
},
|
|
10796
|
+
{
|
|
10797
|
+
path: 'feature-flags',
|
|
10798
|
+
data: {
|
|
10799
|
+
title: 'Feature Flags',
|
|
10800
|
+
permission: 'Pages.Admin'
|
|
10801
|
+
},
|
|
10802
|
+
children: [
|
|
10803
|
+
{
|
|
10804
|
+
path: '',
|
|
10805
|
+
component: FeatureFlagsTabsComponent,
|
|
10806
|
+
children: [
|
|
10807
|
+
{
|
|
10808
|
+
path: '',
|
|
10809
|
+
redirectTo: 'list',
|
|
10810
|
+
pathMatch: 'full'
|
|
10811
|
+
},
|
|
10812
|
+
{
|
|
10813
|
+
path: 'list',
|
|
10814
|
+
component: FeatureFlagsListComponent
|
|
10815
|
+
}
|
|
10816
|
+
]
|
|
10817
|
+
},
|
|
10818
|
+
{
|
|
10819
|
+
path: 'add',
|
|
10820
|
+
component: FeatureFlagDetailsComponent,
|
|
10821
|
+
data: {
|
|
10822
|
+
title: 'Add Feature Flag',
|
|
10823
|
+
permission: 'Pages.Admin'
|
|
10824
|
+
}
|
|
10825
|
+
},
|
|
10826
|
+
{
|
|
10827
|
+
path: ':id',
|
|
10828
|
+
component: FeatureFlagDetailsComponent,
|
|
10829
|
+
data: {
|
|
10830
|
+
title: 'Edit Feature Flag',
|
|
10831
|
+
permission: 'Pages.Admin'
|
|
10832
|
+
}
|
|
10833
|
+
},
|
|
10834
|
+
{
|
|
10835
|
+
path: 'rules/:id',
|
|
10836
|
+
component: FeatureFlagRulesComponent,
|
|
10837
|
+
data: {
|
|
10838
|
+
title: 'Feature Flag Rules',
|
|
10839
|
+
permission: 'Pages.Admin'
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
]
|
|
10843
|
+
},
|
|
10430
10844
|
{
|
|
10431
10845
|
path: 'faq',
|
|
10432
10846
|
data: {
|
|
@@ -11225,6 +11639,10 @@ class AdminModule {
|
|
|
11225
11639
|
LoginNotificationDetailsComponent,
|
|
11226
11640
|
FeedbackQuestionsListComponent,
|
|
11227
11641
|
FeedbackQuestionsDetailsComponent,
|
|
11642
|
+
FeatureFlagsListComponent,
|
|
11643
|
+
FeatureFlagDetailsComponent,
|
|
11644
|
+
FeatureFlagRulesComponent,
|
|
11645
|
+
FeatureFlagsTabsComponent,
|
|
11228
11646
|
UserDetailsComponent,
|
|
11229
11647
|
ProductsTabComponent,
|
|
11230
11648
|
SubscriptionDetailsComponent,
|
|
@@ -11404,6 +11822,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImpor
|
|
|
11404
11822
|
LoginNotificationDetailsComponent,
|
|
11405
11823
|
FeedbackQuestionsListComponent,
|
|
11406
11824
|
FeedbackQuestionsDetailsComponent,
|
|
11825
|
+
FeatureFlagsListComponent,
|
|
11826
|
+
FeatureFlagDetailsComponent,
|
|
11827
|
+
FeatureFlagRulesComponent,
|
|
11828
|
+
FeatureFlagsTabsComponent,
|
|
11407
11829
|
UserDetailsComponent,
|
|
11408
11830
|
ProductsTabComponent,
|
|
11409
11831
|
SubscriptionDetailsComponent,
|
|
@@ -11543,13 +11965,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImpor
|
|
|
11543
11965
|
}] });
|
|
11544
11966
|
|
|
11545
11967
|
class AdminGuardService {
|
|
11546
|
-
constructor(userService, router, permissionService) {
|
|
11968
|
+
constructor(userService, router, featureFlagService, permissionService) {
|
|
11547
11969
|
this.userService = userService;
|
|
11548
11970
|
this.router = router;
|
|
11971
|
+
this.featureFlagService = featureFlagService;
|
|
11549
11972
|
this.permissionService = permissionService;
|
|
11550
11973
|
}
|
|
11551
11974
|
canLoad(route) {
|
|
11552
11975
|
return this.userService.getUserInfo().pipe(take(1), concatMap(user => {
|
|
11976
|
+
this.featureFlagService.user = user;
|
|
11553
11977
|
this.permissionService.user = user;
|
|
11554
11978
|
if (route.data && this.permissionService.isGranted(route.data['permission'])) {
|
|
11555
11979
|
return of(true);
|
|
@@ -11570,12 +11994,12 @@ class AdminGuardService {
|
|
|
11570
11994
|
return of(false);
|
|
11571
11995
|
}));
|
|
11572
11996
|
}
|
|
11573
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: AdminGuardService, deps: [{ token: i1$2.UserService }, { token: i10.Router }, { token: i1$2.PermissionService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
11997
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: AdminGuardService, deps: [{ token: i1$2.UserService }, { token: i10.Router }, { token: i1$2.FeatureFlagService }, { token: i1$2.PermissionService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
11574
11998
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: AdminGuardService }); }
|
|
11575
11999
|
}
|
|
11576
12000
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: AdminGuardService, decorators: [{
|
|
11577
12001
|
type: Injectable
|
|
11578
|
-
}], ctorParameters: () => [{ type: i1$2.UserService }, { type: i10.Router }, { type: i1$2.PermissionService }] });
|
|
12002
|
+
}], ctorParameters: () => [{ type: i1$2.UserService }, { type: i10.Router }, { type: i1$2.FeatureFlagService }, { type: i1$2.PermissionService }] });
|
|
11579
12003
|
|
|
11580
12004
|
// Admin
|
|
11581
12005
|
|