@openziti/ziti-console-lib 1.5.2 → 1.5.4

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.
@@ -10,7 +10,7 @@ import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
10
10
  import * as i4 from '@angular/router';
11
11
  import { RouterModule, NavigationEnd } from '@angular/router';
12
12
  import { BehaviorSubject, Subject, firstValueFrom, tap, map, Subscription, fromEvent, of, interval } from 'rxjs';
13
- import _, { debounce, defer, isEmpty, isNumber, includes, isNil, isNaN as isNaN$1, cloneDeep, isString, forEach, isFunction, get, unset, isBoolean, keys, isEqual, isObject, isArray, forOwn, invert, map as map$2, some, filter, sortBy, sortedUniq, set, has, trim, delay, invoke } from 'lodash';
13
+ import _, { debounce, defer, isEmpty, isNumber, includes, isNil, isNaN as isNaN$1, cloneDeep, isBoolean, isArray, isString, forEach, isFunction, get, unset, keys, isEqual, isObject, forOwn, invert, map as map$2, some, filter, sortBy, sortedUniq, set, has, trim, delay, invoke } from 'lodash';
14
14
  import * as i3 from '@angular/material/tooltip';
15
15
  import { MatTooltipModule } from '@angular/material/tooltip';
16
16
  import * as i5$2 from 'angularx-qrcode';
@@ -30,6 +30,8 @@ import * as i5$3 from '@angular/material/select';
30
30
  import { MatSelect, MatSelectModule } from '@angular/material/select';
31
31
  import * as i1$4 from '@angular/cdk/overlay';
32
32
  import { OverlayModule } from '@angular/cdk/overlay';
33
+ import * as i6$1 from '@angular/cdk/drag-drop';
34
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
33
35
  import { catchError, map as map$1, distinctUntilChanged, debounceTime, skip, takeUntil, throttleTime, filter as filter$1 } from 'rxjs/operators';
34
36
  import * as i1$2 from 'ngx-logger';
35
37
  import { NgxLoggerLevel } from 'ngx-logger';
@@ -154,6 +156,9 @@ class ExtensionsNoopService {
154
156
  processTableColumns(tableColumns) {
155
157
  return tableColumns;
156
158
  }
159
+ processListTableColumns(tableColumns) {
160
+ return tableColumns;
161
+ }
157
162
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ExtensionsNoopService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
158
163
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ExtensionsNoopService, providedIn: 'root' }); }
159
164
  }
@@ -1753,6 +1758,168 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1753
1758
  type: Output
1754
1759
  }] } });
1755
1760
 
1761
+ /*
1762
+ Copyright NetFoundry Inc.
1763
+
1764
+ Licensed under the Apache License, Version 2.0 (the "License");
1765
+ you may not use this file except in compliance with the License.
1766
+ You may obtain a copy of the License at
1767
+
1768
+ https://www.apache.org/licenses/LICENSE-2.0
1769
+
1770
+ Unless required by applicable law or agreed to in writing, software
1771
+ distributed under the License is distributed on an "AS IS" BASIS,
1772
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1773
+ See the License for the specific language governing permissions and
1774
+ limitations under the License.
1775
+ */
1776
+ /**
1777
+ * Per-instance state + localStorage persistence for {@link ListTableComponent}.
1778
+ * Uses its own `lt_<tableId>_*` keys (separate from the legacy ag-grid table) so a
1779
+ * first visit starts from the column defs' default widths, not a carried-over ag layout.
1780
+ */
1781
+ class ListTableService {
1782
+ constructor() {
1783
+ this.tableId = '';
1784
+ /** Coordinates the toolbar dropdowns so only one (filter pill or column chooser)
1785
+ * is open at a time. Openers emit themselves; others close when they see an
1786
+ * owner that isn't them. */
1787
+ this.menuOpenedSubject = new Subject();
1788
+ this.menuOpened$ = this.menuOpenedSubject.asObservable();
1789
+ }
1790
+ notifyMenuOpened(owner) {
1791
+ this.menuOpenedSubject.next(owner);
1792
+ }
1793
+ stateKey() {
1794
+ return `lt_${this.tableId}_table_state`;
1795
+ }
1796
+ widthsKey() {
1797
+ return `lt_${this.tableId}_column_widths`;
1798
+ }
1799
+ pageSizeKey() {
1800
+ return `lt_${this.tableId}_page_size`;
1801
+ }
1802
+ static pageSizeKeyFor(tableId) {
1803
+ return `lt_${tableId}_page_size`;
1804
+ }
1805
+ restorePageSize(fallback) {
1806
+ return ListTableService.readPersistedPageSize(this.tableId, fallback);
1807
+ }
1808
+ savePageSize(size) {
1809
+ try {
1810
+ localStorage.setItem(this.pageSizeKey(), String(size));
1811
+ }
1812
+ catch {
1813
+ // ignore
1814
+ }
1815
+ }
1816
+ /** Reads a table's remembered rows-per-page without an instance (for host pre-seeding). */
1817
+ static readPersistedPageSize(tableId, fallback) {
1818
+ try {
1819
+ const raw = localStorage.getItem(ListTableService.pageSizeKeyFor(tableId));
1820
+ const n = raw ? parseInt(raw, 10) : NaN;
1821
+ return Number.isFinite(n) && n > 0 ? n : fallback;
1822
+ }
1823
+ catch {
1824
+ return fallback;
1825
+ }
1826
+ }
1827
+ /**
1828
+ * Returns a shallow-cloned copy of `columns` with any persisted width, visibility,
1829
+ * and order applied. Columns not present in the saved state keep their declared
1830
+ * position (appended after the saved ones).
1831
+ */
1832
+ applyPersistedState(columns) {
1833
+ let cols = columns.map((c) => ({ ...c }));
1834
+ // widths
1835
+ try {
1836
+ const raw = localStorage.getItem(this.widthsKey());
1837
+ if (raw) {
1838
+ const widths = JSON.parse(raw) || {};
1839
+ cols.forEach((c) => {
1840
+ if (widths[c.key] != null) {
1841
+ c.width = widths[c.key];
1842
+ }
1843
+ });
1844
+ }
1845
+ }
1846
+ catch {
1847
+ // ignore malformed / unavailable storage
1848
+ }
1849
+ // order + visibility
1850
+ try {
1851
+ const raw = localStorage.getItem(this.stateKey());
1852
+ if (raw) {
1853
+ const state = JSON.parse(raw) || [];
1854
+ const ordered = [];
1855
+ state.forEach((s) => {
1856
+ const found = cols.find((c) => c.key === s.colId);
1857
+ if (found && !ordered.includes(found)) {
1858
+ if (s.hide != null) {
1859
+ found.hidden = s.hide;
1860
+ }
1861
+ if (s.width != null) {
1862
+ found.width = s.width;
1863
+ }
1864
+ ordered.push(found);
1865
+ }
1866
+ });
1867
+ cols.forEach((c) => {
1868
+ if (!ordered.includes(c)) {
1869
+ ordered.push(c);
1870
+ }
1871
+ });
1872
+ if (ordered.length) {
1873
+ cols = ordered;
1874
+ }
1875
+ }
1876
+ }
1877
+ catch {
1878
+ // ignore malformed / unavailable storage
1879
+ }
1880
+ return cols;
1881
+ }
1882
+ /** Persists the current order + visibility (and widths embedded in each entry). */
1883
+ saveState(columns) {
1884
+ const state = columns.map((c) => ({ colId: c.key, hide: !!c.hidden, width: c.width }));
1885
+ try {
1886
+ localStorage.setItem(this.stateKey(), JSON.stringify(state));
1887
+ }
1888
+ catch {
1889
+ // ignore
1890
+ }
1891
+ }
1892
+ /** Persists a single column's width, then rewrites the ordered state. */
1893
+ saveWidth(colId, width, columns) {
1894
+ try {
1895
+ const raw = localStorage.getItem(this.widthsKey());
1896
+ const widths = raw ? JSON.parse(raw) : {};
1897
+ widths[colId] = width;
1898
+ localStorage.setItem(this.widthsKey(), JSON.stringify(widths));
1899
+ }
1900
+ catch {
1901
+ // ignore
1902
+ }
1903
+ this.saveState(columns);
1904
+ }
1905
+ /** Clears all persisted layout for this table ("Restore Default Table"). */
1906
+ reset() {
1907
+ try {
1908
+ localStorage.removeItem(this.stateKey());
1909
+ localStorage.removeItem(this.widthsKey());
1910
+ localStorage.removeItem(this.pageSizeKey());
1911
+ }
1912
+ catch {
1913
+ // ignore
1914
+ }
1915
+ }
1916
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1917
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableService }); }
1918
+ }
1919
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableService, decorators: [{
1920
+ type: Injectable
1921
+ }] });
1922
+
1756
1923
  /*
1757
1924
  Copyright NetFoundry Inc.
1758
1925
 
@@ -2561,6 +2728,20 @@ class ZitiDataService {
2561
2728
  }
2562
2729
  return filters;
2563
2730
  }
2731
+ // escape ziti-ql string contents so a value with a quote/backslash can't alter the query
2732
+ escapeQlString(v) {
2733
+ return String(v).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
2734
+ }
2735
+ // generic multi-select over a scalar field: `col = "a" or col = "b"` (vs anyOf() for collections)
2736
+ getMultiSelectFilter(val, columnId, semantic = 'AnyOf') {
2737
+ const join = semantic === 'AllOf' ? 'and' : 'or';
2738
+ const clause = (v) => `${columnId} = ${(isNumber(v) || isBoolean(v)) ? `${v}` : `"${this.escapeQlString(v)}"`}`;
2739
+ if (isArray(val)) {
2740
+ const parts = val.map(clause);
2741
+ return parts.length > 1 ? `(${parts.join(` ${join} `)})` : (parts[0] || '');
2742
+ }
2743
+ return clause(val);
2744
+ }
2564
2745
  getErrorMessage(resp) {
2565
2746
  let errorMessage;
2566
2747
  if (resp?.error?.error?.cause?.message) {
@@ -2908,15 +3089,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2908
3089
  }] }] });
2909
3090
 
2910
3091
  class DataTableFilterService {
3092
+ // filter params live under this prefix so unrelated params (e.g. ?tab=) aren't treated as filters
3093
+ static { this.URL_FILTER_PREFIX = 'f.'; }
2911
3094
  constructor(location, router, activatedRoute) {
2912
3095
  this.location = location;
2913
3096
  this.router = router;
2914
3097
  this.activatedRoute = activatedRoute;
2915
3098
  this.filters = [];
2916
3099
  this.currentPage = 1;
3100
+ /** Rows requested per page. Defaults to the legacy page size; the list-table's
3101
+ * rows-per-page control changes it via {@link changePageSize}. */
3102
+ this.pageSize = 50;
2917
3103
  this.filtersChanged = new BehaviorSubject(this.filters);
2918
3104
  this.filtering = new BehaviorSubject(false);
2919
3105
  this.pageChanged = new BehaviorSubject(this.currentPage);
3106
+ this.pageSizeChanged = new BehaviorSubject(this.pageSize);
2920
3107
  this.currentQueryParams = [];
2921
3108
  }
2922
3109
  updateFilter(filterObj) {
@@ -2962,6 +3149,11 @@ class DataTableFilterService {
2962
3149
  this.currentPage = page;
2963
3150
  this.pageChanged.next(page);
2964
3151
  }
3152
+ changePageSize(size) {
3153
+ this.pageSize = size;
3154
+ this.currentPage = 1;
3155
+ this.pageSizeChanged.next(size);
3156
+ }
2965
3157
  clearFilters() {
2966
3158
  this.filters = [];
2967
3159
  this.updateUrlParameters();
@@ -2976,18 +3168,25 @@ class DataTableFilterService {
2976
3168
  return filters;
2977
3169
  }
2978
3170
  updateUrlParameters(filtersToRemove = []) {
3171
+ const prefix = DataTableFilterService.URL_FILTER_PREFIX;
2979
3172
  const urlFiltersMap = this.getUrlFiltersMap();
2980
- let params = new HttpParams();
2981
3173
  this.filters.forEach(filter => {
2982
3174
  urlFiltersMap[filter.columnId] = filter;
2983
3175
  });
3176
+ const currentParamString = window.location.href.split("?")[1] || '';
3177
+ let params = new HttpParams({ fromString: currentParamString });
3178
+ params.keys()
3179
+ .filter((key) => key.startsWith(prefix))
3180
+ .forEach((key) => {
3181
+ params = params.delete(key);
3182
+ });
2984
3183
  const newFilters = [];
2985
3184
  forEach(urlFiltersMap, (value, key) => {
2986
3185
  const filterRemoved = filtersToRemove.some((removed) => {
2987
3186
  return removed.columnId === key;
2988
3187
  });
2989
3188
  if (!filterRemoved) {
2990
- params = params.append(key, value.value);
3189
+ params = params.append(prefix + key, value.value);
2991
3190
  newFilters.push(value);
2992
3191
  }
2993
3192
  });
@@ -2997,11 +3196,16 @@ class DataTableFilterService {
2997
3196
  this.storeFilters();
2998
3197
  }
2999
3198
  getUrlFiltersMap() {
3199
+ const prefix = DataTableFilterService.URL_FILTER_PREFIX;
3000
3200
  const paramString = window.location.href.split("?")[1];
3001
3201
  const params = new HttpParams({ fromString: paramString });
3002
3202
  const storedFilters = this.getStoredFilters();
3003
3203
  const appliedFiltersMap = {};
3004
- params.keys().forEach((key) => {
3204
+ params.keys().forEach((rawKey) => {
3205
+ if (!rawKey.startsWith(prefix)) {
3206
+ return;
3207
+ }
3208
+ const key = rawKey.slice(prefix.length);
3005
3209
  const storedFilter = storedFilters.find((filter) => {
3006
3210
  return filter.columnId === key;
3007
3211
  });
@@ -3009,7 +3213,7 @@ class DataTableFilterService {
3009
3213
  appliedFiltersMap[key] = storedFilter;
3010
3214
  }
3011
3215
  else {
3012
- let val = params.get(key);
3216
+ let val = params.get(rawKey);
3013
3217
  let type = 'TEXTINPUT';
3014
3218
  let label = val;
3015
3219
  if (val?.split(',')?.length > 1) {
@@ -3510,6 +3714,14 @@ let ListPageServiceClass = class ListPageServiceClass {
3510
3714
  this.basePath = pathSegments[0];
3511
3715
  }
3512
3716
  });
3717
+ // The subscription above misses the NavigationEnd that already fired before
3718
+ // this root-provided service was lazily constructed (first page load), which
3719
+ // would leave basePath empty and send name links / openEditForm to "/<id>".
3720
+ // Seed it from the current route immediately.
3721
+ const currentConfig = this.router?.routerState?.snapshot?.root?.firstChild?.routeConfig;
3722
+ if (currentConfig?.path) {
3723
+ this.basePath = currentConfig.path.split('/')[0];
3724
+ }
3513
3725
  }
3514
3726
  /**
3515
3727
  * Root-provided page services survive route changes; restore baseline menus before each table init
@@ -3702,17 +3914,23 @@ class ListPageComponent {
3702
3914
  this.filterApplied = false;
3703
3915
  this.modalOpen = false;
3704
3916
  this.gridObj = {};
3917
+ this.tableObj = null;
3705
3918
  this.subscription = new Subscription();
3706
3919
  this.growlerSvc = inject(GrowlerService);
3707
3920
  this.managementPermissions = inject(ManagementPermissionsService);
3708
3921
  }
3709
3922
  ngOnInit() {
3710
3923
  this.filterService.currentPage = 1;
3924
+ // new list-table pages: adopt this table's remembered rows-per-page before the
3925
+ // first fetch so the initial load honors it (consumers get this without copying)
3926
+ if (this.tableId) {
3927
+ this.filterService.pageSize = ListTableService.readPersistedPageSize(this.tableId, this.initialPageSize ?? this.filterService.pageSize);
3928
+ }
3711
3929
  this.svc.sideModalOpen = false;
3712
3930
  this.svc.refreshData = this.refreshData.bind(this);
3713
3931
  this.svc.resetMenusForInit();
3714
3932
  this.svc.initMenuActions();
3715
- this.columnDefs = this.svc.initTableColumns();
3933
+ this.columnDefs = this.loadTableColumns();
3716
3934
  this.svc.capturePermissionSnapshots();
3717
3935
  this.subscription.add(this.managementPermissions.stateVersion$.subscribe(() => {
3718
3936
  this.svc.rebuildMenusAndHeaders(this.managementPermissions);
@@ -3731,6 +3949,9 @@ class ListPageComponent {
3731
3949
  this.subscription.add(this.filterService.pageChanged.pipe(skip(1)).subscribe(page => {
3732
3950
  this.refreshData();
3733
3951
  }));
3952
+ this.subscription.add(this.filterService.pageSizeChanged.pipe(skip(1)).subscribe(() => {
3953
+ this.refreshData();
3954
+ }));
3734
3955
  this.subscription.add(this.consoleEvents.closeSideModal.subscribe((event) => {
3735
3956
  this.closeModal();
3736
3957
  }));
@@ -3751,7 +3972,8 @@ class ListPageComponent {
3751
3972
  const index = this.svc.tableHeaderActions.findIndex((a) => a.action === 'download-selected');
3752
3973
  const allow = this.managementPermissions.canRead(this.svc.resourceType);
3753
3974
  if (hasSelection && allow && index === -1) {
3754
- this.svc.tableHeaderActions.push({ name: 'Download Selected', action: 'download-selected' });
3975
+ // icon (SVG) is rendered by the new list-table's header menu; legacy ignores it
3976
+ this.svc.tableHeaderActions.push({ name: 'Download Selected', action: 'download-selected', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M8 2.5v7M5 6.6 8 9.6l3-3M3.2 12.6h9.6"/></svg>' });
3755
3977
  }
3756
3978
  if ((!hasSelection || !allow) && index !== -1) {
3757
3979
  this.svc.tableHeaderActions.splice(index, 1);
@@ -3782,7 +4004,7 @@ class ListPageComponent {
3782
4004
  }
3783
4005
  this.isLoading = true;
3784
4006
  sort = sort ? sort : this.svc.currentSort;
3785
- this.svc.getData(this.filterService.filters, sort, this.filterService.currentPage)
4007
+ this.svc.getData(this.filterService.filters, sort, this.filterService.currentPage, this.filterService.pageSize)
3786
4008
  .then((data) => {
3787
4009
  this.rowData = [];
3788
4010
  if (hardRefresh) {
@@ -3868,9 +4090,22 @@ class ListPageComponent {
3868
4090
  }
3869
4091
  return extensionFound;
3870
4092
  }
4093
+ /**
4094
+ * Column set loaded into the table on init. Defaults to the service's ag-grid
4095
+ * column defs (the legacy {@link DataTableComponent}). A page that renders the
4096
+ * new {@link ListTableComponent} overrides this to return the `ListColumn` set
4097
+ * instead, leaving every other page untouched.
4098
+ */
4099
+ loadTableColumns() {
4100
+ return this.svc.initTableColumns();
4101
+ }
3871
4102
  gridReady(event) {
3872
4103
  this.gridObj = event;
3873
4104
  }
4105
+ /** Ready hook for {@link ListTableComponent}; keeps the instance for extension use. */
4106
+ tableReady(event) {
4107
+ this.tableObj = event?.component;
4108
+ }
3874
4109
  refreshCells() {
3875
4110
  defer(() => {
3876
4111
  var params = {
@@ -6292,6 +6527,320 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
6292
6527
  args: [ZITI_DATA_SERVICE]
6293
6528
  }] }, { type: GrowlerService }] });
6294
6529
 
6530
+ /**
6531
+ * Name cell for {@link ListTableComponent}: a link to the entity's detail route,
6532
+ * with optional identity/router status circles and a prefix icon. Inputs come from
6533
+ * the column's `cellInputs(ctx)`: `{ row, cellValue, cellConfig }`, where
6534
+ * `cellConfig` carries `pathRoot`, `itemProp?`, `showIdentityIcons?`,
6535
+ * `showRouterIcons?`, `getNamePrefix?`, `cellNamePreCheck?`.
6536
+ */
6537
+ class NameCellComponent {
6538
+ set row(v) {
6539
+ this.item = v;
6540
+ }
6541
+ set cellValue(v) {
6542
+ this.value = v;
6543
+ }
6544
+ set cellConfig(cfg) {
6545
+ this.cellParams = cfg || {};
6546
+ }
6547
+ constructor(router) {
6548
+ this.router = router;
6549
+ this.cellParams = {};
6550
+ }
6551
+ get namePrefix() {
6552
+ const fn = this.cellParams?.getNamePrefix;
6553
+ if (typeof fn !== 'function') {
6554
+ return null;
6555
+ }
6556
+ const result = fn(this.item);
6557
+ if (!result?.show) {
6558
+ return null;
6559
+ }
6560
+ return result;
6561
+ }
6562
+ linkClicked(event) {
6563
+ event.stopPropagation();
6564
+ event.preventDefault();
6565
+ if (isFunction(this.cellParams?.cellNamePreCheck)) {
6566
+ this.cellParams?.cellNamePreCheck(this.item).then((result) => {
6567
+ if (!result?.confirmed) {
6568
+ return;
6569
+ }
6570
+ this.navigate();
6571
+ });
6572
+ return;
6573
+ }
6574
+ this.navigate();
6575
+ }
6576
+ navigate() {
6577
+ this.router.navigateByUrl(`${this.cellParams.pathRoot}/${get(this.item, this.cellParams.itemProp, this.item.id)}`);
6578
+ }
6579
+ get cellText() {
6580
+ let val = this.value;
6581
+ if (isEmpty(val)) {
6582
+ val = this.item?.name;
6583
+ }
6584
+ return val;
6585
+ }
6586
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: NameCellComponent, deps: [{ token: i4.Router }], target: i0.ɵɵFactoryTarget.Component }); }
6587
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: NameCellComponent, isStandalone: false, selector: "lib-name-cell", inputs: { row: "row", cellValue: "cellValue", cellConfig: "cellConfig" }, ngImport: i0, template: "<span class=\"cell-name-container\">\n <a href=\"./{{cellParams.pathRoot}}/{{item.id}}\" class=\"cell-name-link\" (click)=\"linkClicked($event)\">\n <div class=\"col cell-name-renderer\" data-id=\"{{item.id}}\">\n @if (cellParams.showIdentityIcons && item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.edgeRouterConnectionStatus}}\" title=\"Edge Router Connection Status\"></span>\n </div>\n }\n @if (cellParams.showIdentityIcons && !item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.hasApiSession}}\" title=\"Api Session\"></span>\n <span class=\"circle {{item.hasEdgeRouterConnection}}\" title=\"Edge Router Connected\"></span>\n </div>\n }\n @if (cellParams.showRouterIcons) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.isVerified}}\" title=\"{{item.isVerified ? 'Verified' : 'Unverified'}}\"></span>\n <span class=\"circle {{item.isOnline}}\" title=\"{{item.isOnline ? 'Online' : 'Offline'}}\"></span>\n </div>\n }\n @if (namePrefix) {\n <div class=\"cell-name-prefix-wrap\" [matTooltip]=\"namePrefix.tooltip\" [matTooltipShowDelay]=\"0\">\n <span class=\"cell-name-prefix-icon\" [ngClass]=\"namePrefix.iconClass\"></span>\n </div>\n }\n <strong>{{cellText}}</strong>\n </div>\n </a>\n</span>\n", styles: [".cell-name-renderer{display:flex;flex-direction:row;align-items:center;gap:.3125rem}.cell-name-container{display:flex;flex-direction:row;flex-wrap:nowrap}.cell-name-link{display:inline-flex;align-items:center}.cell-status-container{position:relative;display:flex;align-items:center;justify-content:center;gap:.25rem}.cell-name-prefix-wrap{display:flex;align-items:center;justify-content:center}.cell-name-prefix-icon{color:var(--highlighted);font-size:1rem}.circle.online{background-color:var(--green)}.circle.unknown{background-color:var(--highlighted)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
6588
+ }
6589
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: NameCellComponent, decorators: [{
6590
+ type: Component,
6591
+ args: [{ selector: 'lib-name-cell', standalone: false, template: "<span class=\"cell-name-container\">\n <a href=\"./{{cellParams.pathRoot}}/{{item.id}}\" class=\"cell-name-link\" (click)=\"linkClicked($event)\">\n <div class=\"col cell-name-renderer\" data-id=\"{{item.id}}\">\n @if (cellParams.showIdentityIcons && item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.edgeRouterConnectionStatus}}\" title=\"Edge Router Connection Status\"></span>\n </div>\n }\n @if (cellParams.showIdentityIcons && !item.edgeRouterConnectionStatus) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.hasApiSession}}\" title=\"Api Session\"></span>\n <span class=\"circle {{item.hasEdgeRouterConnection}}\" title=\"Edge Router Connected\"></span>\n </div>\n }\n @if (cellParams.showRouterIcons) {\n <div class=\"cell-status-container\">\n <span class=\"circle {{item.isVerified}}\" title=\"{{item.isVerified ? 'Verified' : 'Unverified'}}\"></span>\n <span class=\"circle {{item.isOnline}}\" title=\"{{item.isOnline ? 'Online' : 'Offline'}}\"></span>\n </div>\n }\n @if (namePrefix) {\n <div class=\"cell-name-prefix-wrap\" [matTooltip]=\"namePrefix.tooltip\" [matTooltipShowDelay]=\"0\">\n <span class=\"cell-name-prefix-icon\" [ngClass]=\"namePrefix.iconClass\"></span>\n </div>\n }\n <strong>{{cellText}}</strong>\n </div>\n </a>\n</span>\n", styles: [".cell-name-renderer{display:flex;flex-direction:row;align-items:center;gap:.3125rem}.cell-name-container{display:flex;flex-direction:row;flex-wrap:nowrap}.cell-name-link{display:inline-flex;align-items:center}.cell-status-container{position:relative;display:flex;align-items:center;justify-content:center;gap:.25rem}.cell-name-prefix-wrap{display:flex;align-items:center;justify-content:center}.cell-name-prefix-icon{color:var(--highlighted);font-size:1rem}.circle.online{background-color:var(--green)}.circle.unknown{background-color:var(--highlighted)}\n"] }]
6592
+ }], ctorParameters: () => [{ type: i4.Router }], propDecorators: { row: [{
6593
+ type: Input
6594
+ }], cellValue: [{
6595
+ type: Input
6596
+ }], cellConfig: [{
6597
+ type: Input
6598
+ }] } });
6599
+
6600
+ /*
6601
+ Copyright NetFoundry Inc.
6602
+
6603
+ Licensed under the Apache License, Version 2.0 (the "License");
6604
+ you may not use this file except in compliance with the License.
6605
+ You may obtain a copy of the License at
6606
+
6607
+ https://www.apache.org/licenses/LICENSE-2.0
6608
+
6609
+ Unless required by applicable law or agreed to in writing, software
6610
+ distributed under the License is distributed on an "AS IS" BASIS,
6611
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6612
+ See the License for the specific language governing permissions and
6613
+ limitations under the License.
6614
+ */
6615
+ /**
6616
+ * Modern hover card for the identities O/S cell — shows the environment info
6617
+ * (OS, version, release, arch) in a clean key/value layout. Rendered by
6618
+ * ListTableComponent's hover-tooltip mechanism (column `tooltip`).
6619
+ */
6620
+ class OsInfoTooltipComponent {
6621
+ get envInfo() {
6622
+ return this.row?.envInfo || this.row?.versionInfo || {};
6623
+ }
6624
+ get osClass() {
6625
+ const os = `${this.envInfo?.os || ''}`.toLowerCase();
6626
+ const ver = `${this.envInfo?.osVersion || ''}`.toLowerCase();
6627
+ if (ver.includes('windows') || os.includes('mingw') || os.includes('windows')) {
6628
+ return 'windows';
6629
+ }
6630
+ if (os.includes('darwin')) {
6631
+ return 'apple';
6632
+ }
6633
+ if (os.includes('linux')) {
6634
+ return 'linux';
6635
+ }
6636
+ if (os.includes('android')) {
6637
+ return 'android';
6638
+ }
6639
+ return 'other';
6640
+ }
6641
+ get osTitle() {
6642
+ switch (this.osClass) {
6643
+ case 'apple': return 'macOS';
6644
+ case 'windows': return 'Windows';
6645
+ case 'linux': return 'Linux';
6646
+ case 'android': return 'Android';
6647
+ default: return this.envInfo?.os || 'Unknown OS';
6648
+ }
6649
+ }
6650
+ /** Whether there is any environment info worth showing. */
6651
+ get hasInfo() {
6652
+ const e = this.envInfo;
6653
+ return !!(e && (e.os || e.osVersion || e.osRelease || e.arch));
6654
+ }
6655
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OsInfoTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6656
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: OsInfoTooltipComponent, isStandalone: false, selector: "lib-os-info-tooltip", inputs: { row: "row" }, ngImport: i0, template: "<div class=\"os-tip\">\n <div class=\"os-tip-head\">\n <span class=\"os-tip-icon {{ osClass }}\"></span>\n <span class=\"os-tip-title\">{{ osTitle }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"os-tip-body\">\n @if (envInfo?.osVersion) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Version</span><span class=\"os-tip-val\">{{ envInfo.osVersion }}</span></div>\n }\n @if (envInfo?.osRelease) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Release</span><span class=\"os-tip-val\">{{ envInfo.osRelease }}</span></div>\n }\n @if (envInfo?.arch) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Arch</span><span class=\"os-tip-val\">{{ envInfo.arch }}</span></div>\n }\n </div>\n } @else {\n <div class=\"os-tip-empty\">No environment info reported</div>\n }\n</div>\n", styles: [":host{display:block}.os-tip{min-width:11rem;max-width:16rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.os-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.os-tip-icon{flex:0 0 auto;width:1.25rem;height:1.25rem;background-size:contain;background-repeat:no-repeat;background-position:center}.os-tip-icon.apple{background-image:url(/assets/images/apple.png)}.os-tip-icon.windows{background-image:url(/assets/images/windows.png)}.os-tip-icon.linux{background-image:url(/assets/images/linux.png)}.os-tip-icon.android{background-image:url(/assets/images/android.png)}.os-tip-icon.other{background-image:url(/assets/images/other.png)}.os-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text)}.os-tip-body{display:flex;flex-direction:column;gap:.3125rem}.os-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.os-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.os-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.os-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }); }
6657
+ }
6658
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OsInfoTooltipComponent, decorators: [{
6659
+ type: Component,
6660
+ args: [{ selector: 'lib-os-info-tooltip', standalone: false, template: "<div class=\"os-tip\">\n <div class=\"os-tip-head\">\n <span class=\"os-tip-icon {{ osClass }}\"></span>\n <span class=\"os-tip-title\">{{ osTitle }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"os-tip-body\">\n @if (envInfo?.osVersion) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Version</span><span class=\"os-tip-val\">{{ envInfo.osVersion }}</span></div>\n }\n @if (envInfo?.osRelease) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Release</span><span class=\"os-tip-val\">{{ envInfo.osRelease }}</span></div>\n }\n @if (envInfo?.arch) {\n <div class=\"os-tip-row\"><span class=\"os-tip-key\">Arch</span><span class=\"os-tip-val\">{{ envInfo.arch }}</span></div>\n }\n </div>\n } @else {\n <div class=\"os-tip-empty\">No environment info reported</div>\n }\n</div>\n", styles: [":host{display:block}.os-tip{min-width:11rem;max-width:16rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.os-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.os-tip-icon{flex:0 0 auto;width:1.25rem;height:1.25rem;background-size:contain;background-repeat:no-repeat;background-position:center}.os-tip-icon.apple{background-image:url(/assets/images/apple.png)}.os-tip-icon.windows{background-image:url(/assets/images/windows.png)}.os-tip-icon.linux{background-image:url(/assets/images/linux.png)}.os-tip-icon.android{background-image:url(/assets/images/android.png)}.os-tip-icon.other{background-image:url(/assets/images/other.png)}.os-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text)}.os-tip-body{display:flex;flex-direction:column;gap:.3125rem}.os-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.os-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.os-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.os-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }]
6661
+ }], propDecorators: { row: [{
6662
+ type: Input
6663
+ }] } });
6664
+
6665
+ /*
6666
+ Copyright NetFoundry Inc.
6667
+
6668
+ Licensed under the Apache License, Version 2.0 (the "License");
6669
+ you may not use this file except in compliance with the License.
6670
+ You may obtain a copy of the License at
6671
+
6672
+ https://www.apache.org/licenses/LICENSE-2.0
6673
+
6674
+ Unless required by applicable law or agreed to in writing, software
6675
+ distributed under the License is distributed on an "AS IS" BASIS,
6676
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6677
+ See the License for the specific language governing permissions and
6678
+ limitations under the License.
6679
+ */
6680
+ /**
6681
+ * Hover card for the identities SDK cell — shows the reported SDK details
6682
+ * (app id/version, version, type, branch, revision), the same fields the legacy
6683
+ * ag-grid SDK tooltip surfaced. Rendered by ListTableComponent's hover-tooltip
6684
+ * mechanism (column `tooltip`).
6685
+ */
6686
+ class SdkInfoTooltipComponent {
6687
+ get sdkInfo() {
6688
+ return this.row?.sdkInfo || {};
6689
+ }
6690
+ get title() {
6691
+ const s = this.sdkInfo;
6692
+ return s?.appId || s?.type || 'SDK';
6693
+ }
6694
+ get hasInfo() {
6695
+ const s = this.sdkInfo;
6696
+ return !!(s && (s.appId || s.appVersion || s.version || s.type || s.branch || s.revision));
6697
+ }
6698
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SdkInfoTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6699
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SdkInfoTooltipComponent, isStandalone: false, selector: "lib-sdk-info-tooltip", inputs: { row: "row" }, ngImport: i0, template: "<div class=\"sdk-tip\">\n <div class=\"sdk-tip-head\">\n <span class=\"sdk-tip-title\">{{ title }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"sdk-tip-body\">\n @if (sdkInfo?.appId) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App ID</span><span class=\"sdk-tip-val\">{{ sdkInfo.appId }}</span></div>\n }\n @if (sdkInfo?.appVersion) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.appVersion }}</span></div>\n }\n @if (sdkInfo?.version) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.version }}</span></div>\n }\n @if (sdkInfo?.type) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Type</span><span class=\"sdk-tip-val\">{{ sdkInfo.type }}</span></div>\n }\n @if (sdkInfo?.branch) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Branch</span><span class=\"sdk-tip-val\">{{ sdkInfo.branch }}</span></div>\n }\n @if (sdkInfo?.revision) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Revision</span><span class=\"sdk-tip-val\">{{ sdkInfo.revision }}</span></div>\n }\n </div>\n } @else {\n <div class=\"sdk-tip-empty\">No SDK info reported</div>\n }\n</div>\n", styles: [":host{display:block}.sdk-tip{min-width:11rem;max-width:18rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.sdk-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.sdk-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-body{display:flex;flex-direction:column;gap:.3125rem}.sdk-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.sdk-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.sdk-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }); }
6700
+ }
6701
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SdkInfoTooltipComponent, decorators: [{
6702
+ type: Component,
6703
+ args: [{ selector: 'lib-sdk-info-tooltip', standalone: false, template: "<div class=\"sdk-tip\">\n <div class=\"sdk-tip-head\">\n <span class=\"sdk-tip-title\">{{ title }}</span>\n </div>\n @if (hasInfo) {\n <div class=\"sdk-tip-body\">\n @if (sdkInfo?.appId) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App ID</span><span class=\"sdk-tip-val\">{{ sdkInfo.appId }}</span></div>\n }\n @if (sdkInfo?.appVersion) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">App Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.appVersion }}</span></div>\n }\n @if (sdkInfo?.version) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Version</span><span class=\"sdk-tip-val\">{{ sdkInfo.version }}</span></div>\n }\n @if (sdkInfo?.type) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Type</span><span class=\"sdk-tip-val\">{{ sdkInfo.type }}</span></div>\n }\n @if (sdkInfo?.branch) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Branch</span><span class=\"sdk-tip-val\">{{ sdkInfo.branch }}</span></div>\n }\n @if (sdkInfo?.revision) {\n <div class=\"sdk-tip-row\"><span class=\"sdk-tip-key\">Revision</span><span class=\"sdk-tip-val\">{{ sdkInfo.revision }}</span></div>\n }\n </div>\n } @else {\n <div class=\"sdk-tip-empty\">No SDK info reported</div>\n }\n</div>\n", styles: [":host{display:block}.sdk-tip{min-width:11rem;max-width:18rem;padding:.625rem .75rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font)}.sdk-tip-head{display:flex;align-items:center;gap:.5rem;padding-bottom:.5rem;margin-bottom:.5rem;border-bottom:1px solid var(--lt-border)}.sdk-tip-title{font-size:.875rem;font-weight:700;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-body{display:flex;flex-direction:column;gap:.3125rem}.sdk-tip-row{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem}.sdk-tip-key{flex:0 0 auto;color:var(--groupText);font-size:.75rem}.sdk-tip-val{flex:1 1 auto;min-width:0;text-align:right;font-weight:600;color:var(--lt-cell-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sdk-tip-empty{font-size:.75rem;font-style:italic;color:var(--groupText)}\n"] }]
6704
+ }], propDecorators: { row: [{
6705
+ type: Input
6706
+ }] } });
6707
+
6708
+ /*
6709
+ Copyright NetFoundry Inc.
6710
+
6711
+ Licensed under the Apache License, Version 2.0 (the "License");
6712
+ you may not use this file except in compliance with the License.
6713
+ You may obtain a copy of the License at
6714
+
6715
+ https://www.apache.org/licenses/LICENSE-2.0
6716
+
6717
+ Unless required by applicable law or agreed to in writing, software
6718
+ distributed under the License is distributed on an "AS IS" BASIS,
6719
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6720
+ See the License for the specific language governing permissions and
6721
+ limitations under the License.
6722
+ */
6723
+ /**
6724
+ * Renders a truthy boolean as a green check + domain label (via `trueLabel`);
6725
+ * a false value renders nothing, so the column reads as a clean presence marker.
6726
+ */
6727
+ class BooleanCellComponent {
6728
+ constructor() {
6729
+ this.trueLabel = 'Yes';
6730
+ }
6731
+ get isTrue() {
6732
+ return this.value === true || this.value === 'true';
6733
+ }
6734
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BooleanCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6735
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: BooleanCellComponent, isStandalone: false, selector: "lib-boolean-cell", inputs: { value: "value", trueLabel: "trueLabel" }, ngImport: i0, template: "@if (isTrue) {\n <span class=\"bool bool-true\">\n <svg class=\"bool-check\" viewBox=\"0 0 14 14\" width=\"12\" height=\"12\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2.5 7.6 5.7 10.8 11.5 3.6\"/>\n </svg>\n <span class=\"bool-label\">{{ trueLabel }}</span>\n </span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.bool-true{display:inline-flex;align-items:center;gap:.3125rem;padding:.1875rem .5rem .1875rem .4375rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;line-height:1.2;color:#15803d;background:color-mix(in srgb,#16a34a 12%,transparent);border-radius:999px}.bool-check{flex:0 0 auto;display:block;color:#16a34a}\n"] }); }
6736
+ }
6737
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BooleanCellComponent, decorators: [{
6738
+ type: Component,
6739
+ args: [{ selector: 'lib-boolean-cell', standalone: false, template: "@if (isTrue) {\n <span class=\"bool bool-true\">\n <svg class=\"bool-check\" viewBox=\"0 0 14 14\" width=\"12\" height=\"12\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2.5 7.6 5.7 10.8 11.5 3.6\"/>\n </svg>\n <span class=\"bool-label\">{{ trueLabel }}</span>\n </span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.bool-true{display:inline-flex;align-items:center;gap:.3125rem;padding:.1875rem .5rem .1875rem .4375rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;line-height:1.2;color:#15803d;background:color-mix(in srgb,#16a34a 12%,transparent);border-radius:999px}.bool-check{flex:0 0 auto;display:block;color:#16a34a}\n"] }]
6740
+ }], propDecorators: { value: [{
6741
+ type: Input
6742
+ }], trueLabel: [{
6743
+ type: Input
6744
+ }] } });
6745
+
6746
+ /*
6747
+ Copyright NetFoundry Inc.
6748
+
6749
+ Licensed under the Apache License, Version 2.0 (the "License");
6750
+ you may not use this file except in compliance with the License.
6751
+ You may obtain a copy of the License at
6752
+
6753
+ https://www.apache.org/licenses/LICENSE-2.0
6754
+
6755
+ Unless required by applicable law or agreed to in writing, software
6756
+ distributed under the License is distributed on an "AS IS" BASIS,
6757
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6758
+ See the License for the specific language governing permissions and
6759
+ limitations under the License.
6760
+ */
6761
+ /**
6762
+ * Enrollment-token cell for the identities list. Shows a status chip (Available /
6763
+ * Expired / —) that doubles as a dropdown of enrollment actions (Download JWT, Show
6764
+ * QR, Re-issue). Actions are emitted via the cell context's `emitAction`, so they run
6765
+ * through the same page handlers as the row ⋯ menu.
6766
+ */
6767
+ class TokenCellComponent {
6768
+ constructor() {
6769
+ this.open = false;
6770
+ this.menuLeft = 0;
6771
+ this.menuTop = 0;
6772
+ }
6773
+ get status() {
6774
+ if (!this.hasEnrolmentToken) {
6775
+ return 'none';
6776
+ }
6777
+ return this.tokenExpired ? 'expired' : 'available';
6778
+ }
6779
+ get statusLabel() {
6780
+ return this.status === 'expired' ? 'Expired' : 'Available';
6781
+ }
6782
+ get actions() {
6783
+ if (this.status === 'available') {
6784
+ return [
6785
+ { label: 'Download JWT', action: 'download-enrollment', icon: 'icon-download-jwt' },
6786
+ { label: 'Show QR code', action: 'qr-code', icon: 'icon-qr' },
6787
+ { label: 'Re-issue enrollment', action: 'reissue-enrollment', icon: 'icon-refresh' },
6788
+ ];
6789
+ }
6790
+ if (this.status === 'expired') {
6791
+ return [{ label: 'Re-issue enrollment', action: 'reissue-enrollment', icon: 'icon-refresh' }];
6792
+ }
6793
+ return [];
6794
+ }
6795
+ toggleMenu(event) {
6796
+ event.stopPropagation();
6797
+ if (this.open) {
6798
+ this.open = false;
6799
+ return;
6800
+ }
6801
+ const rect = this.trigger?.nativeElement.getBoundingClientRect();
6802
+ if (rect) {
6803
+ this.menuLeft = rect.left;
6804
+ this.menuTop = rect.bottom + 4;
6805
+ }
6806
+ this.open = true;
6807
+ }
6808
+ closeMenu() {
6809
+ this.open = false;
6810
+ }
6811
+ runAction(action, event) {
6812
+ event.stopPropagation();
6813
+ this.open = false;
6814
+ this.emitAction?.(action, this.row);
6815
+ }
6816
+ get hasEnrolmentToken() {
6817
+ const token = this.row?.enrollment?.ott?.jwt ||
6818
+ this.row?.enrollment?.ottca?.jwt ||
6819
+ this.row?.enrollment?.updb?.jwt;
6820
+ return !!(this.row?.enrollmentToken || token);
6821
+ }
6822
+ get tokenExpired() {
6823
+ const expiration = this.row?.enrollmentExpiresAt ||
6824
+ this.row?.enrollment?.ott?.expiresAt ||
6825
+ this.row?.enrollment?.ottca?.expiresAt ||
6826
+ this.row?.enrollment?.updb?.expiresAt;
6827
+ return expiration ? moment$1(expiration).isBefore() : false;
6828
+ }
6829
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TokenCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6830
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TokenCellComponent, isStandalone: false, selector: "lib-token-cell", inputs: { row: "row", emitAction: "emitAction" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }], ngImport: i0, template: "@if (status !== 'none') {\n <button\n #trigger\n type=\"button\"\n class=\"token-chip {{ status }}\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"token-chip-label\">{{ statusLabel }}</span>\n <svg class=\"token-caret\" viewBox=\"0 0 10 6\" width=\"9\" height=\"6\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/>\n </svg>\n </button>\n\n @if (open) {\n <div class=\"token-menu\" [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px' }\" (clickOutside)=\"closeMenu()\">\n @for (a of actions; track a.action) {\n <button type=\"button\" class=\"token-menu-item\" (click)=\"runAction(a.action, $event)\">\n <span class=\"token-menu-icon {{ a.icon }}\" aria-hidden=\"true\"></span>\n <span class=\"token-menu-label\">{{ a.label }}</span>\n </button>\n }\n </div>\n }\n} @else {\n <span class=\"token-empty\">&#8212;</span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.token-chip{display:inline-flex;align-items:center;gap:.3125rem;height:1.5rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;border:none;border-radius:.75rem;cursor:pointer;white-space:nowrap;transition:filter .15s}.token-chip .token-caret{display:block;opacity:.75}.token-chip:hover,.token-chip.is-open{filter:brightness(.96)}.token-chip.available{color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,var(--lt-control-bg))}.token-chip.expired{color:color-mix(in srgb,var(--highlighted) 62%,#000);background:color-mix(in srgb,var(--highlighted) 26%,var(--lt-control-bg))}.token-empty{color:var(--groupText)}.token-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:token-in .1s ease-out}@keyframes token-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.token-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.token-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.token-menu-item:hover .token-menu-icon{color:var(--lt-link)}.token-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }] }); }
6831
+ }
6832
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TokenCellComponent, decorators: [{
6833
+ type: Component,
6834
+ args: [{ selector: 'lib-token-cell', standalone: false, template: "@if (status !== 'none') {\n <button\n #trigger\n type=\"button\"\n class=\"token-chip {{ status }}\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"token-chip-label\">{{ statusLabel }}</span>\n <svg class=\"token-caret\" viewBox=\"0 0 10 6\" width=\"9\" height=\"6\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/>\n </svg>\n </button>\n\n @if (open) {\n <div class=\"token-menu\" [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px' }\" (clickOutside)=\"closeMenu()\">\n @for (a of actions; track a.action) {\n <button type=\"button\" class=\"token-menu-item\" (click)=\"runAction(a.action, $event)\">\n <span class=\"token-menu-icon {{ a.icon }}\" aria-hidden=\"true\"></span>\n <span class=\"token-menu-label\">{{ a.label }}</span>\n </button>\n }\n </div>\n }\n} @else {\n <span class=\"token-empty\">&#8212;</span>\n}\n", styles: [":host{display:inline-flex;align-items:center}.token-chip{display:inline-flex;align-items:center;gap:.3125rem;height:1.5rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;font-weight:600;border:none;border-radius:.75rem;cursor:pointer;white-space:nowrap;transition:filter .15s}.token-chip .token-caret{display:block;opacity:.75}.token-chip:hover,.token-chip.is-open{filter:brightness(.96)}.token-chip.available{color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,var(--lt-control-bg))}.token-chip.expired{color:color-mix(in srgb,var(--highlighted) 62%,#000);background:color-mix(in srgb,var(--highlighted) 26%,var(--lt-control-bg))}.token-empty{color:var(--groupText)}.token-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:token-in .1s ease-out}@keyframes token-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.token-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.token-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.token-menu-item:hover .token-menu-icon{color:var(--lt-link)}.token-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}\n"] }]
6835
+ }], propDecorators: { row: [{
6836
+ type: Input
6837
+ }], emitAction: [{
6838
+ type: Input
6839
+ }], trigger: [{
6840
+ type: ViewChild,
6841
+ args: ['trigger']
6842
+ }] } });
6843
+
6295
6844
  /*
6296
6845
  Copyright NetFoundry Inc.
6297
6846
 
@@ -6343,19 +6892,21 @@ class IdentitiesPageService extends ListPageServiceClass {
6343
6892
  os: '',
6344
6893
  createdAt: '',
6345
6894
  };
6895
+ // `icon` (SVG markup) is rendered before the label in the new list-table's row kebab
6896
+ // (3-dot) menu. The legacy ag-grid menu ignores it.
6346
6897
  this.menuItems = [
6347
- { name: 'Edit', action: 'update' },
6348
- { name: 'Download JWT', action: 'download-enrollment' },
6349
- { name: 'View QR', action: 'qr-code' },
6350
- { name: 'Visualizer', action: 'identity-service-path' },
6351
- { name: 'Reset Enrollment', action: 'reset-enrollment' },
6352
- { name: 'Reissue Enrollment', action: 'reissue-enrollment' },
6353
- { name: 'Reset MFA', action: 'reset-mfa' },
6354
- { name: 'Override', action: 'override' },
6355
- { name: 'Delete', action: 'delete' },
6898
+ { name: 'Edit', action: 'update', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M11.2 2.6 13.4 4.8 6 12.2l-2.8.6.6-2.8z"/></svg>' },
6899
+ { name: 'Download JWT', action: 'download-enrollment', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M8 2.5v7M5 6.6 8 9.6l3-3M3.2 12.6h9.6"/></svg>' },
6900
+ { name: 'View QR', action: 'qr-code', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><g fill="none" stroke="currentColor" stroke-width="1.3"><rect x="2.5" y="2.5" width="4" height="4" rx="0.5"/><rect x="9.5" y="2.5" width="4" height="4" rx="0.5"/><rect x="2.5" y="9.5" width="4" height="4" rx="0.5"/></g><rect x="10" y="10" width="3.5" height="3.5" fill="currentColor"/></svg>' },
6901
+ { name: 'Visualizer', action: 'identity-service-path', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" d="M5.4 7.3 10.6 4.6M5.4 8.7l5.2 2.7"/><circle cx="4" cy="8" r="1.8" fill="currentColor"/><circle cx="12" cy="4" r="1.8" fill="currentColor"/><circle cx="12" cy="12" r="1.8" fill="currentColor"/></svg>' },
6902
+ { name: 'Reset Enrollment', action: 'reset-enrollment', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M3.2 8a4.8 4.8 0 1 0 1.3-3.3"/><path fill="currentColor" d="M2.8 2.8l-.3 2.9 2.8-.4z"/></svg>' },
6903
+ { name: 'Reissue Enrollment', action: 'reissue-enrollment', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M12.8 8a4.8 4.8 0 1 1-1.3-3.3"/><path fill="currentColor" d="M13.2 2.8l.3 2.9-2.8-.4z"/></svg>' },
6904
+ { name: 'Reset MFA', action: 'reset-mfa', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round" d="M8 2.3 12.8 4v3.8c0 3-2.1 4.9-4.8 5.9-2.7-1-4.8-2.9-4.8-5.9V4z"/></svg>' },
6905
+ { name: 'Override', action: 'override', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M2.8 5h10.4M2.8 11h10.4"/><circle cx="6" cy="5" r="1.7" fill="currentColor"/><circle cx="10" cy="11" r="1.7" fill="currentColor"/></svg>' },
6906
+ { name: 'Delete', action: 'delete', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M3.4 4.6h9.2M6.4 4.6V3.2h3.2v1.4M4.6 4.6 5 13h6l.4-8.4"/></svg>' },
6356
6907
  ];
6357
6908
  this.tableHeaderActions = [
6358
- { name: 'Download All', action: 'download-all' },
6909
+ { name: 'Download All', action: 'download-all', icon: '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" d="M8 2.5v7M5 6.6 8 9.6l3-3M3.2 12.6h9.6"/></svg>' },
6359
6910
  ];
6360
6911
  this.resourceType = 'identities';
6361
6912
  this.validate = (formData) => {
@@ -6611,15 +7162,280 @@ class IdentitiesPageService extends ListPageServiceClass {
6611
7162
  }
6612
7163
  return tableColumns;
6613
7164
  }
7165
+ /**
7166
+ * Column set for the new `lib-list-table` (the "beta" Identities page). Mirrors
7167
+ * {@link initTableColumns} but emits the declarative `ListColumn` shape instead of
7168
+ * ag-grid `ColDef`s: component cells via `cell`/`cellInputs`, legacy string
7169
+ * renderers via `cellHtml`, and header filters declared with `filterType`.
7170
+ * Loaded by {@link IdentitiesPageComponent} when the Beta Features toggle is on;
7171
+ * the legacy {@link initTableColumns} above is left untouched for the ag-grid table.
7172
+ */
7173
+ initListTableColumns() {
7174
+ this.initMenuActions();
7175
+ const identityRolesHeaderComponentParams = {
7176
+ filterType: 'ATTRIBUTE',
7177
+ enableSorting: true,
7178
+ appendAttributeHash: false,
7179
+ getRoleAttributes: () => this.identityRoleAttributes,
7180
+ getNamedAttributes: () => [],
7181
+ getSelectedRoleAttributes: () => this.selectedIdentityRoleAttributes,
7182
+ getSelectedNamedAttributes: () => [],
7183
+ setSelectedRoleAttributes: (attributes) => {
7184
+ this.selectedIdentityRoleAttributes = attributes;
7185
+ },
7186
+ setSelectedNamedAttributes: (attributes) => {
7187
+ // no-op
7188
+ },
7189
+ getNamedAttributesMap: () => ({}),
7190
+ };
7191
+ const osRenderer = (row) => {
7192
+ const os = (row?.data?.envInfo?.os || '').toLowerCase();
7193
+ const ver = (row?.data?.envInfo?.osVersion || '').toLowerCase();
7194
+ let cls = 'other';
7195
+ if (ver.includes('windows') || os.includes('windows') || os.includes('mingw'))
7196
+ cls = 'windows';
7197
+ else if (os.includes('darwin'))
7198
+ cls = 'apple';
7199
+ else if (os.includes('linux'))
7200
+ cls = 'linux';
7201
+ else if (os.includes('android'))
7202
+ cls = 'android';
7203
+ return `<span class="os ${cls}"></span>`;
7204
+ };
7205
+ const sdkRenderer = (row) => {
7206
+ // detail moves to the hover popover (SdkInfoTooltipComponent); the cell
7207
+ // just shows the version, so drop the legacy balloon attributes
7208
+ const version = row?.data?.sdkInfo?.version || '-';
7209
+ return `<span class="oneline">${version}</span>`;
7210
+ };
7211
+ const isAdminHeaderComponentParams = {
7212
+ filterType: 'SELECT',
7213
+ enableSorting: true,
7214
+ filterOptions: [
7215
+ { label: 'All', value: '' },
7216
+ { label: 'Admins', value: true },
7217
+ { label: 'Non-Admins', value: false },
7218
+ ]
7219
+ };
7220
+ // Online when any of the connection signals is live (the status icon is drawn
7221
+ // via CSS ::before, since [innerHTML] strips inline <svg>).
7222
+ const isOnline = (v) => v === true || /online|connected|true/i.test(String(v ?? ''));
7223
+ const statusRenderer = (row) => {
7224
+ const d = row?.data || {};
7225
+ const online = isOnline(d.edgeRouterConnectionStatus) || isOnline(d.hasEdgeRouterConnection) || isOnline(d.hasApiSession);
7226
+ return `<span class="lt-status lt-status-${online ? 'online' : 'offline'}">${online ? 'Online' : 'Offline'}</span>`;
7227
+ };
7228
+ // Color-coded Type pill with an icon: Default = blue, Router = green. Icon drawn
7229
+ // via CSS (::before) since [innerHTML] strips inline <svg>.
7230
+ const typeBadgeRenderer = (row) => {
7231
+ const t = row?.data?.type?.name || row?.data?.type || '';
7232
+ const key = String(t).toLowerCase();
7233
+ const cls = key === 'router' ? 'lt-type-router'
7234
+ : key === 'default' ? 'lt-type-default'
7235
+ : 'lt-type-other';
7236
+ return `<span class="lt-type-badge ${cls}">${t}</span>`;
7237
+ };
7238
+ // Light skinned role chips (the legacy rolesRenderer paints
7239
+ // heavy dark overlap chips; this stays scoped to the beta page).
7240
+ const rolesChipRenderer = (row) => {
7241
+ const colId = row?.column?.colId;
7242
+ const attrs = row?.data?.[colId + 'Display'] || row?.data?.[colId] || [];
7243
+ if (!attrs?.length) {
7244
+ return '';
7245
+ }
7246
+ return attrs.map((attr) => {
7247
+ const raw = String(attr?.name ?? attr ?? '').trim();
7248
+ if (!raw) {
7249
+ return '';
7250
+ }
7251
+ // named attributes (@) use the secondary color; role attributes (#)
7252
+ // the primary color, matching the tag selector's distinction
7253
+ const isNamed = raw.charAt(0) === '@';
7254
+ const label = isNamed ? raw : (raw.charAt(0) === '#' ? raw : '#' + raw);
7255
+ const cls = isNamed ? 'lt-chip lt-chip-at' : 'lt-chip';
7256
+ return `<span class="${cls}">${label}</span>`;
7257
+ }).join('');
7258
+ };
7259
+ let tableColumns = [
7260
+ {
7261
+ key: 'name',
7262
+ field: 'name',
7263
+ label: 'Name',
7264
+ filterType: 'TEXTINPUT',
7265
+ cell: NameCellComponent,
7266
+ cellInputs: (ctx) => ({
7267
+ row: ctx.row,
7268
+ cellValue: ctx.value,
7269
+ // status now lives in its own Status column (skinned), so the
7270
+ // name cell no longer carries the connection dots
7271
+ cellConfig: { pathRoot: this.basePath, showIdentityIcons: false },
7272
+ }),
7273
+ onCellClick: (ctx) => {
7274
+ if (this.hasSelectedText()) {
7275
+ return;
7276
+ }
7277
+ this.openEditForm(ctx.row?.id);
7278
+ },
7279
+ resizable: true,
7280
+ cellClass: 'nf-cell-vert-align',
7281
+ sortable: true,
7282
+ onSort: this.sort.bind(this),
7283
+ sortDir: 'asc',
7284
+ width: 300,
7285
+ },
7286
+ {
7287
+ key: 'status',
7288
+ field: 'hasEdgeRouterConnection',
7289
+ label: 'Status',
7290
+ cellHtml: statusRenderer,
7291
+ resizable: true,
7292
+ cellClass: 'nf-cell-vert-align',
7293
+ width: 120,
7294
+ },
7295
+ {
7296
+ key: 'roleAttributes',
7297
+ field: 'roleAttributes',
7298
+ label: 'Roles',
7299
+ filterType: 'ATTRIBUTE',
7300
+ headerParams: identityRolesHeaderComponentParams,
7301
+ cellHtml: rolesChipRenderer,
7302
+ onCellClick: (ctx) => {
7303
+ if (this.hasSelectedText()) {
7304
+ return;
7305
+ }
7306
+ this.openEditForm(ctx.row?.id);
7307
+ },
7308
+ resizable: true,
7309
+ cellClass: 'nf-cell-vert-align',
7310
+ sortable: false,
7311
+ },
7312
+ {
7313
+ key: 'os',
7314
+ field: 'os',
7315
+ label: 'O/S',
7316
+ width: 100,
7317
+ cellHtml: osRenderer,
7318
+ tooltip: OsInfoTooltipComponent,
7319
+ tooltipInputs: (ctx) => ({ row: ctx.row }),
7320
+ // envInfo.os is a JSON blob field the controller cannot filter on
7321
+ // ("unknown symbol") - no O/S filter control.
7322
+ resizable: true,
7323
+ cellClass: 'nf-cell-vert-align lt-cell-center',
7324
+ },
7325
+ {
7326
+ key: 'sdk',
7327
+ field: 'sdk',
7328
+ label: 'SDK',
7329
+ cellHtml: sdkRenderer,
7330
+ tooltip: SdkInfoTooltipComponent,
7331
+ tooltipInputs: (ctx) => ({ row: ctx.row }),
7332
+ resizable: true,
7333
+ cellClass: 'nf-cell-vert-align',
7334
+ width: 125,
7335
+ },
7336
+ {
7337
+ key: 'type',
7338
+ field: 'type.name',
7339
+ label: 'Type',
7340
+ filterType: 'SELECT',
7341
+ filterField: 'type',
7342
+ filterPlaceholder: 'Any type',
7343
+ // Only Default + Router exist as identity types in Ziti 2.x (confirmed
7344
+ // via /edge/management/v1/identity-types); Service/Device/User are legacy
7345
+ // and always return 0.
7346
+ filterOptions: [
7347
+ { label: 'Show All', value: '' },
7348
+ { label: 'Default', value: 'Default', chipClass: 'lt-type-badge lt-type-default' },
7349
+ { label: 'Router', value: 'Router', chipClass: 'lt-type-badge lt-type-router' },
7350
+ ],
7351
+ cellHtml: typeBadgeRenderer,
7352
+ resizable: true,
7353
+ sortable: true,
7354
+ cellClass: 'nf-cell-vert-align',
7355
+ onSort: this.sort.bind(this),
7356
+ },
7357
+ {
7358
+ key: 'isAdmin',
7359
+ field: 'isAdmin',
7360
+ label: 'Is Admin',
7361
+ filterType: 'SELECT',
7362
+ filterOptions: isAdminHeaderComponentParams.filterOptions,
7363
+ cell: BooleanCellComponent,
7364
+ cellInputs: (ctx) => ({ value: ctx.value, trueLabel: 'Admin' }),
7365
+ resizable: true,
7366
+ sortable: true,
7367
+ onSort: this.sort.bind(this),
7368
+ cellClass: 'nf-cell-vert-align',
7369
+ width: 100,
7370
+ },
7371
+ {
7372
+ key: 'createdAt',
7373
+ field: 'createdAt',
7374
+ label: 'Created At',
7375
+ valueFormatter: this.createdAtFormatter,
7376
+ filterType: 'DATETIME',
7377
+ filterPlaceholder: 'Any time',
7378
+ resizable: true,
7379
+ sortable: true,
7380
+ onSort: this.sort.bind(this),
7381
+ cellClass: 'nf-cell-vert-align',
7382
+ },
7383
+ {
7384
+ key: 'token',
7385
+ field: 'token',
7386
+ label: 'Token',
7387
+ cell: TokenCellComponent,
7388
+ cellInputs: (ctx) => ({ row: ctx.row, emitAction: ctx.emitAction }),
7389
+ resizable: true,
7390
+ cellClass: 'nf-cell-vert-align',
7391
+ },
7392
+ {
7393
+ key: 'isMfaEnabled',
7394
+ field: 'isMfaEnabled',
7395
+ label: 'MFA',
7396
+ // isMfaEnabled is a computed field the controller cannot filter on
7397
+ // ("unknown symbol") - no MFA filter control.
7398
+ cell: BooleanCellComponent,
7399
+ cellInputs: (ctx) => ({ value: ctx.value, trueLabel: 'Enabled' }),
7400
+ resizable: true,
7401
+ cellClass: 'nf-cell-vert-align',
7402
+ width: 100,
7403
+ },
7404
+ {
7405
+ key: 'id',
7406
+ field: 'id',
7407
+ label: 'ID',
7408
+ filterType: 'TEXTINPUT',
7409
+ cellClass: 'nf-cell-vert-align',
7410
+ resizable: true,
7411
+ sortable: true,
7412
+ onSort: this.sort.bind(this),
7413
+ onCellClick: (ctx) => {
7414
+ if (this.hasSelectedText()) {
7415
+ return;
7416
+ }
7417
+ this.openEditForm(ctx.row?.id);
7418
+ },
7419
+ hidden: true,
7420
+ },
7421
+ ];
7422
+ if (this.extService.processListTableColumns) {
7423
+ tableColumns = this.extService.processListTableColumns(tableColumns);
7424
+ }
7425
+ return tableColumns;
7426
+ }
6614
7427
  getIdentityRoleAttributes() {
6615
7428
  return this.zitiService.get('identity-role-attributes', {}, []).then((result) => {
6616
7429
  this.identityRoleAttributes = result.data;
6617
7430
  return result;
6618
7431
  });
6619
7432
  }
6620
- getData(filters, sort, page) {
7433
+ getData(filters, sort, page, pageSize) {
6621
7434
  // we can customize filters or sorting here before moving on...
6622
7435
  this.paging.page = page || this.paging.page;
7436
+ if (pageSize) {
7437
+ this.paging.total = pageSize;
7438
+ }
6623
7439
  return super.getTableData('identities', this.paging, filters, sort)
6624
7440
  .then((results) => {
6625
7441
  return this.processData(results);
@@ -9289,6 +10105,124 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
9289
10105
  args: [SHAREDZ_EXTENSION]
9290
10106
  }] }] });
9291
10107
 
10108
+ /*
10109
+ Copyright NetFoundry Inc.
10110
+
10111
+ Licensed under the Apache License, Version 2.0 (the "License");
10112
+ you may not use this file except in compliance with the License.
10113
+ You may obtain a copy of the License at
10114
+
10115
+ https://www.apache.org/licenses/LICENSE-2.0
10116
+
10117
+ Unless required by applicable law or agreed to in writing, software
10118
+ distributed under the License is distributed on an "AS IS" BASIS,
10119
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10120
+ See the License for the specific language governing permissions and
10121
+ limitations under the License.
10122
+ */
10123
+ /**
10124
+ * Header for the new list-table pages: an icon tile + title + one-line subtitle
10125
+ * (with an optional info affordance) on the left, and a primary "Create ..." action
10126
+ * on the right, over an optional tab strip. Emits the same `add` / `delete` actions
10127
+ * as {@link ListPageHeaderComponent} so pages can swap headers without changing their
10128
+ * handlers. Project the page's icon into the tile with a `header-icon` attribute.
10129
+ */
10130
+ class ListTableHeaderComponent {
10131
+ constructor() {
10132
+ this.title = '';
10133
+ this.subtitle = '';
10134
+ /** Label for the primary create button, e.g. "Create Identity". */
10135
+ this.createLabel = 'Create';
10136
+ /** Optional short helper text shown behind the (i) affordance next to the title. */
10137
+ this.infoText = '';
10138
+ this.tabs = [];
10139
+ this.showAdd = true;
10140
+ /** Hide the create control even when showAdd is true. */
10141
+ this.blockCreate = false;
10142
+ /** Hide the bulk-delete control shown when rows are selected. */
10143
+ this.blockDelete = false;
10144
+ /** When true (default) tabs navigate via routerLink and highlight by matching the
10145
+ * URL path. Set false for a consumer that owns navigation (e.g. query-param tabs):
10146
+ * tabs then only emit `tabClicked`, and the active one is driven by `activeTab`. */
10147
+ this.tabsUseRouter = true;
10148
+ /** Active tab when `tabsUseRouter` is false. Matched by identity or by the tab's
10149
+ * `id` / `value` / `url` / `label`. */
10150
+ this.activeTab = null;
10151
+ this.actionClicked = new EventEmitter();
10152
+ this.tabClicked = new EventEmitter();
10153
+ }
10154
+ clickAction(value) {
10155
+ this.actionClicked.emit(value);
10156
+ }
10157
+ onTabClick(tab) {
10158
+ this.tabClicked.emit(tab);
10159
+ }
10160
+ tabSelected(tab) {
10161
+ if (!this.tabsUseRouter) {
10162
+ return this.tabMatchesActive(tab);
10163
+ }
10164
+ if (!tab?.url) {
10165
+ return false;
10166
+ }
10167
+ const parsedUrl = new URL(window.location.href);
10168
+ let path = parsedUrl.pathname;
10169
+ if (document.baseURI) {
10170
+ const base = new URL(document.baseURI).pathname;
10171
+ if (path.startsWith(base)) {
10172
+ path = path.slice(base.length);
10173
+ }
10174
+ }
10175
+ let tabUrl = tab.url;
10176
+ if (tab.url.startsWith('/')) {
10177
+ tabUrl = tab.url.slice(1);
10178
+ }
10179
+ if (path.startsWith('/')) {
10180
+ path = path.slice(1);
10181
+ }
10182
+ return tabUrl === path;
10183
+ }
10184
+ tabMatchesActive(tab) {
10185
+ const active = this.activeTab;
10186
+ if (active == null) {
10187
+ return false;
10188
+ }
10189
+ if (active === tab) {
10190
+ return true;
10191
+ }
10192
+ return active === tab?.id || active === tab?.value || active === tab?.url || active === tab?.label;
10193
+ }
10194
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10195
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListTableHeaderComponent, isStandalone: false, selector: "lib-list-table-header", inputs: { title: "title", subtitle: "subtitle", createLabel: "createLabel", infoText: "infoText", tabs: "tabs", showAdd: "showAdd", blockCreate: "blockCreate", blockDelete: "blockDelete", tabsUseRouter: "tabsUseRouter", activeTab: "activeTab" }, outputs: { actionClicked: "actionClicked", tabClicked: "tabClicked" }, ngImport: i0, template: "<div class=\"lth\">\n <div class=\"lth-main\">\n <div class=\"lth-icon\" aria-hidden=\"true\">\n <ng-content select=\"[header-icon]\"></ng-content>\n </div>\n <div class=\"lth-titles\">\n <div class=\"lth-title-row\">\n <h1 class=\"lth-title\">{{ title }}</h1>\n @if (infoText) {\n <span class=\"lth-info\" [title]=\"infoText\" tabindex=\"0\" role=\"img\" [attr.aria-label]=\"infoText\">\n <span class=\"lth-info-badge\" aria-hidden=\"true\">i</span>\n </span>\n }\n </div>\n @if (subtitle) {\n <p class=\"lth-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <div class=\"lth-actions\">\n @if (showAdd && !blockCreate) {\n <button type=\"button\" class=\"lth-create\" (click)=\"clickAction('add')\" data-action=\"add\" id=\"CreateButton\">\n <span class=\"lth-create-plus\" aria-hidden=\"true\">+</span>\n <span>{{ createLabel }}</span>\n </button>\n }\n @if (!showAdd && !blockDelete) {\n <button type=\"button\" class=\"lth-delete\" (click)=\"clickAction('delete')\" data-action=\"delete\" id=\"DeleteButton\">\n <span class=\"lth-delete-icon icon-minus\" aria-hidden=\"true\"></span>\n <span>Delete</span>\n </button>\n }\n </div>\n </div>\n @if (tabs?.length) {\n <div class=\"lth-tabs\">\n @for (tab of tabs; track tab) {\n <a\n class=\"lth-tab\"\n [ngClass]=\"{ selected: tabSelected(tab) }\"\n [routerLink]=\"tabsUseRouter ? tab.url : null\"\n [hidden]=\"tab.hidden\"\n [attr.role]=\"tabsUseRouter ? null : 'button'\"\n [attr.tabindex]=\"tabsUseRouter ? null : 0\"\n (click)=\"onTabClick(tab)\"\n (keydown.enter)=\"tabsUseRouter ? null : onTabClick(tab)\"\n >{{ tab.label }}</a>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.lth{display:flex;flex-direction:column;gap:.75rem;padding-bottom:.5rem}.lth-main{display:flex;align-items:center;gap:.875rem}.lth-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:.75rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 12%,var(--background));border:1px solid color-mix(in srgb,var(--primary) 22%,var(--background))}.lth-icon svg,.lth-icon img{display:block;width:1.5rem;height:1.5rem}:host ::ng-deep .lth-icon [class^=icon-],:host ::ng-deep .lth-icon [class*=\" icon-\"]{font-size:1.5rem;line-height:1}:host ::ng-deep .lth-icon [class^=icon-]:before,:host ::ng-deep .lth-icon [class*=\" icon-\"]:before{color:var(--primary)}.lth-titles{flex:1 1 auto;min-width:0}.lth-title-row{display:flex;align-items:center;gap:.5rem}.lth-title{margin:0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:1.5rem;font-weight:700;line-height:1.15;color:var(--tableText, var(--text))}.lth-info{display:inline-flex;align-items:center;justify-content:center;padding:.25rem;background:var(--background);border:1px solid var(--stroke);border-radius:.375rem;box-shadow:0 1px 2px #0000000f;cursor:help}.lth-info:hover{border-color:var(--groupText)}.lth-info-badge{display:inline-flex;align-items:center;justify-content:center;width:.9375rem;height:.9375rem;font-size:.625rem;font-weight:700;font-style:italic;font-family:Georgia,Times New Roman,serif;line-height:1;color:#fff;background:var(--dark);border-radius:50%}.lth-subtitle{margin:.125rem 0 0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;color:var(--groupText)}.lth-actions{flex:0 0 auto;display:flex;align-items:center;gap:.5rem}.lth-create{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:#fff;background:var(--primary);border:none;border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:filter .15s}.lth-create:hover{filter:brightness(1.06)}.lth-create-plus{font-size:1.125rem;line-height:1}.lth-delete{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--redText, #9c0027);background:transparent;border:1px solid var(--redText, #9c0027);border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:background .15s}.lth-delete:hover{background:color-mix(in srgb,var(--redText, #9c0027) 8%,transparent)}.lth-tabs{display:flex;align-items:center;gap:1.25rem;border-bottom:1px solid var(--stroke)}.lth-tab{padding:.4375rem .125rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--groupText);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;transition:color .15s,border-color .15s}.lth-tab:hover{color:var(--text)}.lth-tab.selected{color:var(--primary);border-bottom-color:var(--primary)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
10196
+ }
10197
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableHeaderComponent, decorators: [{
10198
+ type: Component,
10199
+ args: [{ selector: 'lib-list-table-header', standalone: false, template: "<div class=\"lth\">\n <div class=\"lth-main\">\n <div class=\"lth-icon\" aria-hidden=\"true\">\n <ng-content select=\"[header-icon]\"></ng-content>\n </div>\n <div class=\"lth-titles\">\n <div class=\"lth-title-row\">\n <h1 class=\"lth-title\">{{ title }}</h1>\n @if (infoText) {\n <span class=\"lth-info\" [title]=\"infoText\" tabindex=\"0\" role=\"img\" [attr.aria-label]=\"infoText\">\n <span class=\"lth-info-badge\" aria-hidden=\"true\">i</span>\n </span>\n }\n </div>\n @if (subtitle) {\n <p class=\"lth-subtitle\">{{ subtitle }}</p>\n }\n </div>\n <div class=\"lth-actions\">\n @if (showAdd && !blockCreate) {\n <button type=\"button\" class=\"lth-create\" (click)=\"clickAction('add')\" data-action=\"add\" id=\"CreateButton\">\n <span class=\"lth-create-plus\" aria-hidden=\"true\">+</span>\n <span>{{ createLabel }}</span>\n </button>\n }\n @if (!showAdd && !blockDelete) {\n <button type=\"button\" class=\"lth-delete\" (click)=\"clickAction('delete')\" data-action=\"delete\" id=\"DeleteButton\">\n <span class=\"lth-delete-icon icon-minus\" aria-hidden=\"true\"></span>\n <span>Delete</span>\n </button>\n }\n </div>\n </div>\n @if (tabs?.length) {\n <div class=\"lth-tabs\">\n @for (tab of tabs; track tab) {\n <a\n class=\"lth-tab\"\n [ngClass]=\"{ selected: tabSelected(tab) }\"\n [routerLink]=\"tabsUseRouter ? tab.url : null\"\n [hidden]=\"tab.hidden\"\n [attr.role]=\"tabsUseRouter ? null : 'button'\"\n [attr.tabindex]=\"tabsUseRouter ? null : 0\"\n (click)=\"onTabClick(tab)\"\n (keydown.enter)=\"tabsUseRouter ? null : onTabClick(tab)\"\n >{{ tab.label }}</a>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.lth{display:flex;flex-direction:column;gap:.75rem;padding-bottom:.5rem}.lth-main{display:flex;align-items:center;gap:.875rem}.lth-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:2.75rem;height:2.75rem;border-radius:.75rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 12%,var(--background));border:1px solid color-mix(in srgb,var(--primary) 22%,var(--background))}.lth-icon svg,.lth-icon img{display:block;width:1.5rem;height:1.5rem}:host ::ng-deep .lth-icon [class^=icon-],:host ::ng-deep .lth-icon [class*=\" icon-\"]{font-size:1.5rem;line-height:1}:host ::ng-deep .lth-icon [class^=icon-]:before,:host ::ng-deep .lth-icon [class*=\" icon-\"]:before{color:var(--primary)}.lth-titles{flex:1 1 auto;min-width:0}.lth-title-row{display:flex;align-items:center;gap:.5rem}.lth-title{margin:0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:1.5rem;font-weight:700;line-height:1.15;color:var(--tableText, var(--text))}.lth-info{display:inline-flex;align-items:center;justify-content:center;padding:.25rem;background:var(--background);border:1px solid var(--stroke);border-radius:.375rem;box-shadow:0 1px 2px #0000000f;cursor:help}.lth-info:hover{border-color:var(--groupText)}.lth-info-badge{display:inline-flex;align-items:center;justify-content:center;width:.9375rem;height:.9375rem;font-size:.625rem;font-weight:700;font-style:italic;font-family:Georgia,Times New Roman,serif;line-height:1;color:#fff;background:var(--dark);border-radius:50%}.lth-subtitle{margin:.125rem 0 0;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;color:var(--groupText)}.lth-actions{flex:0 0 auto;display:flex;align-items:center;gap:.5rem}.lth-create{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:#fff;background:var(--primary);border:none;border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:filter .15s}.lth-create:hover{filter:brightness(1.06)}.lth-create-plus{font-size:1.125rem;line-height:1}.lth-delete{display:inline-flex;align-items:center;gap:.4375rem;height:2.5rem;padding:0 1rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--redText, #9c0027);background:transparent;border:1px solid var(--redText, #9c0027);border-radius:var(--inputBorderRadius, .5rem);cursor:pointer;transition:background .15s}.lth-delete:hover{background:color-mix(in srgb,var(--redText, #9c0027) 8%,transparent)}.lth-tabs{display:flex;align-items:center;gap:1.25rem;border-bottom:1px solid var(--stroke)}.lth-tab{padding:.4375rem .125rem;font-family:var(--lt-font, \"Open Sans\", sans-serif);font-size:.8125rem;font-weight:600;color:var(--groupText);text-decoration:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;transition:color .15s,border-color .15s}.lth-tab:hover{color:var(--text)}.lth-tab.selected{color:var(--primary);border-bottom-color:var(--primary)}\n"] }]
10200
+ }], propDecorators: { title: [{
10201
+ type: Input
10202
+ }], subtitle: [{
10203
+ type: Input
10204
+ }], createLabel: [{
10205
+ type: Input
10206
+ }], infoText: [{
10207
+ type: Input
10208
+ }], tabs: [{
10209
+ type: Input
10210
+ }], showAdd: [{
10211
+ type: Input
10212
+ }], blockCreate: [{
10213
+ type: Input
10214
+ }], blockDelete: [{
10215
+ type: Input
10216
+ }], tabsUseRouter: [{
10217
+ type: Input
10218
+ }], activeTab: [{
10219
+ type: Input
10220
+ }], actionClicked: [{
10221
+ type: Output
10222
+ }], tabClicked: [{
10223
+ type: Output
10224
+ }] } });
10225
+
9292
10226
  /*
9293
10227
  Copyright NetFoundry Inc.
9294
10228
 
@@ -10769,6 +11703,1318 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
10769
11703
  args: ['document:keydown.escape', ['$event']]
10770
11704
  }] } });
10771
11705
 
11706
+ /**
11707
+ * Generic, theme-driven action menu popover. Presentational only: the host owns
11708
+ * open state and position and handles `(itemClick)` / `(closed)`.
11709
+ */
11710
+ class ListMenuComponent {
11711
+ constructor() {
11712
+ /** Whether the menu is rendered. */
11713
+ this.open = false;
11714
+ /** Rows to render. */
11715
+ this.items = [];
11716
+ /** Fixed-position left offset (px). */
11717
+ this.left = 0;
11718
+ /** Fixed-position top offset (px). */
11719
+ this.top = 0;
11720
+ /** Emitted when a clickable row is selected. */
11721
+ this.itemClick = new EventEmitter();
11722
+ /** Emitted when the user clicks outside the open menu. */
11723
+ this.closed = new EventEmitter();
11724
+ this.trackItem = (_, item) => item.action ?? item.label;
11725
+ }
11726
+ onItem(item, event) {
11727
+ if (item.disabled) {
11728
+ return;
11729
+ }
11730
+ this.itemClick.emit({ item, event });
11731
+ }
11732
+ onClickOutside(event) {
11733
+ this.closed.emit(event);
11734
+ }
11735
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11736
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListMenuComponent, isStandalone: false, selector: "lib-list-menu", inputs: { open: "open", items: "items", left: "left", top: "top", menuId: "menuId" }, outputs: { itemClick: "itemClick", closed: "closed" }, ngImport: i0, template: "@if (open) {\n <div\n class=\"lt-menu\"\n [attr.id]=\"menuId || null\"\n [ngStyle]=\"{ left: left + 'px', top: top + 'px' }\"\n (clickOutside)=\"onClickOutside($event)\"\n >\n @for (item of items; track trackItem($index, item)) {\n <button\n type=\"button\"\n class=\"lt-menu-item\"\n [class.has-icon]=\"!!item.icon || !!item.iconSvg\"\n [class.is-hidden]=\"item.hidden\"\n [disabled]=\"!!item.disabled\"\n [attr.id]=\"item.id || null\"\n (click)=\"onItem(item, $event)\"\n >\n @if (item.iconSvg) {\n <span class=\"lt-menu-icon\" aria-hidden=\"true\" [innerHTML]=\"item.iconSvg\"></span>\n } @else if (item.icon) {\n <span class=\"lt-menu-icon {{ item.icon }}\" aria-hidden=\"true\"></span>\n }\n <span class=\"lt-menu-label\">{{ item.label }}</span>\n </button>\n }\n </div>\n}\n", styles: [":host{display:contents}.lt-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font);animation:lt-menu-in .1s ease-out}@keyframes lt-menu-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.lt-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.lt-menu-item:hover .lt-menu-icon{color:var(--lt-link)}.lt-menu-item.is-hidden{display:none}.lt-menu-item:disabled{opacity:.45;cursor:default}.lt-menu-item:disabled:hover{background:transparent;color:var(--lt-cell-text)}.lt-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}.lt-menu-icon ::ng-deep svg{display:block;width:.9375rem;height:.9375rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }] }); }
11737
+ }
11738
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListMenuComponent, decorators: [{
11739
+ type: Component,
11740
+ args: [{ selector: 'lib-list-menu', standalone: false, template: "@if (open) {\n <div\n class=\"lt-menu\"\n [attr.id]=\"menuId || null\"\n [ngStyle]=\"{ left: left + 'px', top: top + 'px' }\"\n (clickOutside)=\"onClickOutside($event)\"\n >\n @for (item of items; track trackItem($index, item)) {\n <button\n type=\"button\"\n class=\"lt-menu-item\"\n [class.has-icon]=\"!!item.icon || !!item.iconSvg\"\n [class.is-hidden]=\"item.hidden\"\n [disabled]=\"!!item.disabled\"\n [attr.id]=\"item.id || null\"\n (click)=\"onItem(item, $event)\"\n >\n @if (item.iconSvg) {\n <span class=\"lt-menu-icon\" aria-hidden=\"true\" [innerHTML]=\"item.iconSvg\"></span>\n } @else if (item.icon) {\n <span class=\"lt-menu-icon {{ item.icon }}\" aria-hidden=\"true\"></span>\n }\n <span class=\"lt-menu-label\">{{ item.label }}</span>\n </button>\n }\n </div>\n}\n", styles: [":host{display:contents}.lt-menu{position:fixed;z-index:1000;min-width:11rem;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);font-family:var(--lt-font);animation:lt-menu-in .1s ease-out}@keyframes lt-menu-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-menu-item{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);text-align:left;background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer}.lt-menu-item:hover{background:var(--lt-row-hover);color:var(--lt-link)}.lt-menu-item:hover .lt-menu-icon{color:var(--lt-link)}.lt-menu-item.is-hidden{display:none}.lt-menu-item:disabled{opacity:.45;cursor:default}.lt-menu-item:disabled:hover{background:transparent;color:var(--lt-cell-text)}.lt-menu-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;font-size:.9375rem;color:var(--lt-icon)}.lt-menu-icon ::ng-deep svg{display:block;width:.9375rem;height:.9375rem}\n"] }]
11741
+ }], propDecorators: { open: [{
11742
+ type: Input
11743
+ }], items: [{
11744
+ type: Input
11745
+ }], left: [{
11746
+ type: Input
11747
+ }], top: [{
11748
+ type: Input
11749
+ }], menuId: [{
11750
+ type: Input
11751
+ }], itemClick: [{
11752
+ type: Output
11753
+ }], closed: [{
11754
+ type: Output
11755
+ }] } });
11756
+
11757
+ /**
11758
+ * Row selection toggle for {@link ListTableComponent}. Router / exclusive /
11759
+ * reserved / system rows cannot be toggled.
11760
+ */
11761
+ class SelectCellComponent {
11762
+ constructor() {
11763
+ this.item = {
11764
+ id: '',
11765
+ name: '',
11766
+ selected: false,
11767
+ hideSelect: false,
11768
+ };
11769
+ this.disableToggle = false;
11770
+ }
11771
+ set row(v) {
11772
+ this.item = v || this.item;
11773
+ this.disableToggle =
11774
+ _.get(this.item, 'typeId') === 'Router' ||
11775
+ _.get(this.item, 'exclusiveTo') ||
11776
+ _.get(this.item, 'reserved') === true ||
11777
+ _.get(this.item, 'isSystem') === true;
11778
+ }
11779
+ toggle(item) {
11780
+ if (this.disableToggle || !this.toggleItem) {
11781
+ return;
11782
+ }
11783
+ this.toggleItem(item);
11784
+ }
11785
+ get tooltip() {
11786
+ let tooltip = '';
11787
+ if (this.item?.isSystem) {
11788
+ tooltip = 'Deleting "system" entities is not allowed';
11789
+ }
11790
+ else if (this.item?.typeId === 'Router') {
11791
+ tooltip = 'Unable to delete "Router" Identities. Please delete the associated Router instead.';
11792
+ }
11793
+ return tooltip;
11794
+ }
11795
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SelectCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11796
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SelectCellComponent, isStandalone: false, selector: "lib-select-cell", inputs: { row: "row", toggleItem: "toggleItem" }, ngImport: i0, template: "@if (!item.hideSelect) {\n <div\n #toggleButton\n (click)=\"toggle(item)\"\n [ngClass]=\"{ selected: item.selected, disabled: disableToggle }\"\n attr.name=\"RowSelect0_{{ item.name }}\"\n class=\"row-toggle tSelect\"\n id=\"RowSelect0_{{ item.id }}\"\n attr.data-id=\"{{item.id}}\"\n [matTooltip]=\"tooltip\"\n ></div>\n}\n", styles: [".row-toggle.tSelect{box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-border);border-radius:.25rem;cursor:pointer;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;transition:border-color .12s,background-color .12s}.row-toggle.tSelect:hover{border-color:var(--lt-link)}.row-toggle.tSelect.selected{border-color:var(--lt-link);background-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.row-toggle.tSelect.disabled{opacity:.3;cursor:not-allowed;transform:translate(-50%,-50%)!important}.row-toggle.tSelect.disabled:hover{border-color:var(--lt-border);background-color:var(--lt-control-bg)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
11797
+ }
11798
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SelectCellComponent, decorators: [{
11799
+ type: Component,
11800
+ args: [{ selector: 'lib-select-cell', standalone: false, template: "@if (!item.hideSelect) {\n <div\n #toggleButton\n (click)=\"toggle(item)\"\n [ngClass]=\"{ selected: item.selected, disabled: disableToggle }\"\n attr.name=\"RowSelect0_{{ item.name }}\"\n class=\"row-toggle tSelect\"\n id=\"RowSelect0_{{ item.id }}\"\n attr.data-id=\"{{item.id}}\"\n [matTooltip]=\"tooltip\"\n ></div>\n}\n", styles: [".row-toggle.tSelect{box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-border);border-radius:.25rem;cursor:pointer;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;transition:border-color .12s,background-color .12s}.row-toggle.tSelect:hover{border-color:var(--lt-link)}.row-toggle.tSelect.selected{border-color:var(--lt-link);background-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.row-toggle.tSelect.disabled{opacity:.3;cursor:not-allowed;transform:translate(-50%,-50%)!important}.row-toggle.tSelect.disabled:hover{border-color:var(--lt-border);background-color:var(--lt-control-bg)}\n"] }]
11801
+ }], propDecorators: { row: [{
11802
+ type: Input
11803
+ }], toggleItem: [{
11804
+ type: Input
11805
+ }] } });
11806
+
11807
+ /*
11808
+ Copyright NetFoundry Inc.
11809
+
11810
+ Licensed under the Apache License, Version 2.0 (the "License");
11811
+ you may not use this file except in compliance with the License.
11812
+ You may obtain a copy of the License at
11813
+
11814
+ https://www.apache.org/licenses/LICENSE-2.0
11815
+
11816
+ Unless required by applicable law or agreed to in writing, software
11817
+ distributed under the License is distributed on an "AS IS" BASIS,
11818
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11819
+ See the License for the specific language governing permissions and
11820
+ limitations under the License.
11821
+ */
11822
+ /** Header cell for {@link ListTableComponent}: label, 3-state sort (asc/desc), and hide. */
11823
+ class ListHeaderCellComponent {
11824
+ constructor() {
11825
+ this.sortChanged = new EventEmitter();
11826
+ this.hideColumn = new EventEmitter();
11827
+ }
11828
+ get enableSorting() {
11829
+ return !!(this.column?.sortable || this.column?.enableSorting);
11830
+ }
11831
+ sort() {
11832
+ if (!this.enableSorting) {
11833
+ return;
11834
+ }
11835
+ const dir = !this.sortDir || this.sortDir === 'desc' ? 'asc' : 'desc';
11836
+ this.sortDir = dir;
11837
+ this.column.onSort?.(this.column.key, dir);
11838
+ this.sortChanged.emit({ colId: this.column.key, dir });
11839
+ }
11840
+ hide(event) {
11841
+ event.stopPropagation();
11842
+ this.hideColumn.emit(this.column);
11843
+ }
11844
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListHeaderCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11845
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListHeaderCellComponent, isStandalone: false, selector: "lib-list-header-cell", inputs: { column: "column", sortDir: "sortDir" }, outputs: { sortChanged: "sortChanged", hideColumn: "hideColumn" }, ngImport: i0, template: "<div\n (click)=\"sort()\"\n [ngClass]=\"{ 'nf-can-sort': enableSorting }\"\n class=\"lt-header-container\"\n id=\"TableHeaderContainer_{{ column.key }}\"\n>\n @if (column.hideable !== false) {\n <span\n (click)=\"hide($event)\"\n class=\"lt-header-icon lt-col-hide-button\"\n id=\"HeaderHide_{{ column.key }}\"\n title=\"Hide Column\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"\n d=\"M1 8s2.7-4.5 7-4.5S15 8 15 8s-2.7 4.5-7 4.5S1 8 1 8Z\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1.9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n <line x1=\"2.5\" y1=\"13.5\" x2=\"13.5\" y2=\"2.5\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n </span>\n }\n <div class=\"lt-header-label\">\n <span\n [title]=\"column.label\"\n class=\"lt-header-text\"\n [class.is-sorted]=\"sortDir === 'asc' || sortDir === 'desc'\"\n id=\"HeaderName_{{ column.key }}\"\n unselectable=\"on\"\n >{{ column.label }}</span>\n @if (enableSorting) {\n <span class=\"lt-sort\" [class.asc]=\"sortDir === 'asc'\" [class.desc]=\"sortDir === 'desc'\">\n <svg viewBox=\"0 0 8 14\" width=\"8\" height=\"13\" aria-hidden=\"true\">\n <path class=\"lt-sort-up\" d=\"M4 0 7.5 4.5H0.5z\"/>\n <path class=\"lt-sort-down\" d=\"M4 14 0.5 9.5H7.5z\"/>\n </svg>\n </span>\n }\n </div>\n</div>\n", styles: [":host{display:flex;width:100%;align-items:center}.lt-header-container{display:flex;align-items:center;width:100%;height:100%;cursor:default;gap:.25rem}.lt-header-container.nf-can-sort{cursor:pointer}.lt-header-label{display:flex;align-items:center;flex:1 1 auto;min-width:0;gap:.25rem}.lt-header-text{color:var(--lt-header-text);font-family:var(--lt-font);font-size:.6875rem;font-weight:600;letter-spacing:.045em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .15s}.lt-header-text.is-sorted{color:var(--lt-header-sorted-text, var(--lt-icon-active));font-weight:var(--lt-header-sorted-weight, 700)}.lt-header-text-container{display:flex;align-items:center;min-width:0;flex:1 1 auto}.lt-header-text-container.show-input-filter{width:100%}.lt-header-icon{display:inline-flex;align-items:center;justify-content:center;color:var(--lt-icon);cursor:pointer;transition:opacity .2s,color .2s}.lt-header-icon svg{display:block}.lt-header-icon:hover{color:var(--lt-icon-active)}.lt-col-hide-button,.lt-col-filter-button{opacity:0}.lt-header-container:hover .lt-col-hide-button,.lt-header-container:hover .lt-col-filter-button{opacity:1}.lt-col-filter-button.nf-filter-applied{opacity:1;color:var(--lt-icon-active)}.lt-sort{display:inline-flex;align-items:center;flex:0 0 auto;margin-left:auto;margin-right:.375rem}.lt-sort svg{display:block}.lt-sort .lt-sort-up,.lt-sort .lt-sort-down{fill:var(--lt-icon);opacity:.35;transition:opacity .15s,fill .15s}.lt-sort.asc .lt-sort-up,.lt-sort.desc .lt-sort-down{fill:var(--lt-icon-active);opacity:1}.nf-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
11846
+ }
11847
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListHeaderCellComponent, decorators: [{
11848
+ type: Component,
11849
+ args: [{ selector: 'lib-list-header-cell', standalone: false, template: "<div\n (click)=\"sort()\"\n [ngClass]=\"{ 'nf-can-sort': enableSorting }\"\n class=\"lt-header-container\"\n id=\"TableHeaderContainer_{{ column.key }}\"\n>\n @if (column.hideable !== false) {\n <span\n (click)=\"hide($event)\"\n class=\"lt-header-icon lt-col-hide-button\"\n id=\"HeaderHide_{{ column.key }}\"\n title=\"Hide Column\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"\n d=\"M1 8s2.7-4.5 7-4.5S15 8 15 8s-2.7 4.5-7 4.5S1 8 1 8Z\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1.9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n <line x1=\"2.5\" y1=\"13.5\" x2=\"13.5\" y2=\"2.5\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n </span>\n }\n <div class=\"lt-header-label\">\n <span\n [title]=\"column.label\"\n class=\"lt-header-text\"\n [class.is-sorted]=\"sortDir === 'asc' || sortDir === 'desc'\"\n id=\"HeaderName_{{ column.key }}\"\n unselectable=\"on\"\n >{{ column.label }}</span>\n @if (enableSorting) {\n <span class=\"lt-sort\" [class.asc]=\"sortDir === 'asc'\" [class.desc]=\"sortDir === 'desc'\">\n <svg viewBox=\"0 0 8 14\" width=\"8\" height=\"13\" aria-hidden=\"true\">\n <path class=\"lt-sort-up\" d=\"M4 0 7.5 4.5H0.5z\"/>\n <path class=\"lt-sort-down\" d=\"M4 14 0.5 9.5H7.5z\"/>\n </svg>\n </span>\n }\n </div>\n</div>\n", styles: [":host{display:flex;width:100%;align-items:center}.lt-header-container{display:flex;align-items:center;width:100%;height:100%;cursor:default;gap:.25rem}.lt-header-container.nf-can-sort{cursor:pointer}.lt-header-label{display:flex;align-items:center;flex:1 1 auto;min-width:0;gap:.25rem}.lt-header-text{color:var(--lt-header-text);font-family:var(--lt-font);font-size:.6875rem;font-weight:600;letter-spacing:.045em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .15s}.lt-header-text.is-sorted{color:var(--lt-header-sorted-text, var(--lt-icon-active));font-weight:var(--lt-header-sorted-weight, 700)}.lt-header-text-container{display:flex;align-items:center;min-width:0;flex:1 1 auto}.lt-header-text-container.show-input-filter{width:100%}.lt-header-icon{display:inline-flex;align-items:center;justify-content:center;color:var(--lt-icon);cursor:pointer;transition:opacity .2s,color .2s}.lt-header-icon svg{display:block}.lt-header-icon:hover{color:var(--lt-icon-active)}.lt-col-hide-button,.lt-col-filter-button{opacity:0}.lt-header-container:hover .lt-col-hide-button,.lt-header-container:hover .lt-col-filter-button{opacity:1}.lt-col-filter-button.nf-filter-applied{opacity:1;color:var(--lt-icon-active)}.lt-sort{display:inline-flex;align-items:center;flex:0 0 auto;margin-left:auto;margin-right:.375rem}.lt-sort svg{display:block}.lt-sort .lt-sort-up,.lt-sort .lt-sort-down{fill:var(--lt-icon);opacity:.35;transition:opacity .15s,fill .15s}.lt-sort.asc .lt-sort-up,.lt-sort.desc .lt-sort-down{fill:var(--lt-icon-active);opacity:1}.nf-hidden{display:none}\n"] }]
11850
+ }], propDecorators: { column: [{
11851
+ type: Input
11852
+ }], sortDir: [{
11853
+ type: Input
11854
+ }], sortChanged: [{
11855
+ type: Output
11856
+ }], hideColumn: [{
11857
+ type: Output
11858
+ }] } });
11859
+
11860
+ /*
11861
+ Copyright NetFoundry Inc.
11862
+
11863
+ Licensed under the Apache License, Version 2.0 (the "License");
11864
+ you may not use this file except in compliance with the License.
11865
+ You may obtain a copy of the License at
11866
+
11867
+ https://www.apache.org/licenses/LICENSE-2.0
11868
+
11869
+ Unless required by applicable law or agreed to in writing, software
11870
+ distributed under the License is distributed on an "AS IS" BASIS,
11871
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11872
+ See the License for the specific language governing permissions and
11873
+ limitations under the License.
11874
+ */
11875
+ const DATE_PRESETS = [
11876
+ { label: 'Any time', value: '' },
11877
+ { label: 'Last Hour', value: 'hour' },
11878
+ { label: 'Last Day', value: 'day' },
11879
+ { label: 'Last Week', value: 'week' },
11880
+ { label: 'Last Month', value: 'month' },
11881
+ ];
11882
+ /**
11883
+ * A single control in the table's filter row. Renders the right widget for the
11884
+ * column's `filterType` (text input / single-select / multi-select / date-range)
11885
+ * and pushes changes through {@link DataTableFilterService}, whose value contract the
11886
+ * controller query-builder already understands (icontains / = / anyOf() / datetime range).
11887
+ */
11888
+ class ListFilterCellComponent {
11889
+ constructor(filterService, listSvc) {
11890
+ this.filterService = filterService;
11891
+ this.listSvc = listSvc;
11892
+ this.mode = 'text';
11893
+ this.options = [];
11894
+ this.textValue = '';
11895
+ this.selectedValue = '';
11896
+ this.selectedValues = [];
11897
+ /** In-dropdown search text for the multi-select attribute combo. */
11898
+ this.attrSearch = '';
11899
+ this.open = false;
11900
+ this.menuLeft = 0;
11901
+ this.menuTop = 0;
11902
+ this.menuWidth = 0;
11903
+ // custom date range (daterange mode)
11904
+ this.dateRangeStart = null;
11905
+ this.dateRangeEnd = null;
11906
+ this.sub = new Subscription();
11907
+ this.textDebounced = _.debounce(() => this.emitText(), 400);
11908
+ }
11909
+ ngOnInit() {
11910
+ this.mode = this.resolveMode();
11911
+ this.options = this.resolveOptions();
11912
+ this.sub.add(this.filterService.filtersChanged.subscribe((filters) => this.syncFromFilters(filters)));
11913
+ // close this menu whenever another dropdown opens
11914
+ this.sub.add(this.listSvc.menuOpened$.subscribe((owner) => {
11915
+ if (owner !== this) {
11916
+ this.open = false;
11917
+ }
11918
+ }));
11919
+ }
11920
+ ngOnDestroy() {
11921
+ this.sub.unsubscribe();
11922
+ }
11923
+ // ------------------------------------------------------------------ config
11924
+ get field() {
11925
+ return this.column.filterField || this.column.field || this.column.key;
11926
+ }
11927
+ get placeholder() {
11928
+ return this.column.filterPlaceholder || 'All';
11929
+ }
11930
+ /** True only for the Ziti role-attribute flavor of multi-select (adds #/@ prefixing). */
11931
+ get isAttributeMode() {
11932
+ return this.column.filterType === 'ATTRIBUTE';
11933
+ }
11934
+ resolveMode() {
11935
+ switch (this.column.filterType) {
11936
+ case 'TEXTINPUT':
11937
+ return 'text';
11938
+ case 'ATTRIBUTE':
11939
+ case 'MULTISELECT':
11940
+ return 'multi';
11941
+ case 'DATETIME':
11942
+ return 'daterange';
11943
+ default:
11944
+ return 'single';
11945
+ }
11946
+ }
11947
+ resolveOptions() {
11948
+ if (this.mode === 'daterange') {
11949
+ return DATE_PRESETS;
11950
+ }
11951
+ if (this.mode === 'multi') {
11952
+ let raw;
11953
+ if (this.isAttributeMode) {
11954
+ const getAttrs = this.column.headerParams?.['getRoleAttributes'];
11955
+ raw = (typeof getAttrs === 'function' ? getAttrs() : null) || this.column.filterOptions || [];
11956
+ }
11957
+ else {
11958
+ raw = this.column.getFilterOptions ? this.column.getFilterOptions() : (this.column.filterOptions || []);
11959
+ }
11960
+ return raw.map((a) => (typeof a === 'string' ? { label: a, value: a } : a));
11961
+ }
11962
+ const raw = this.column.getFilterOptions ? this.column.getFilterOptions() : this.column.filterOptions || [];
11963
+ return raw;
11964
+ }
11965
+ // ------------------------------------------------------------------ display
11966
+ /** Short display of the current selection, shown after the field label when set. */
11967
+ get valueLabel() {
11968
+ if (this.mode === 'multi') {
11969
+ if (this.selectedValues.length === 1) {
11970
+ return this.selectedValues[0];
11971
+ }
11972
+ return `${this.selectedValues.length}`;
11973
+ }
11974
+ if (this.mode === 'daterange') {
11975
+ if (this.selectedValue === 'custom' && this.dateRangeStart) {
11976
+ const start = moment$1(this.dateRangeStart).format('M/D/YY');
11977
+ const end = this.dateRangeEnd ? moment$1(this.dateRangeEnd).format('M/D/YY') : '';
11978
+ return end ? `${start} – ${end}` : start;
11979
+ }
11980
+ const found = this.options.find((o) => o.value === this.selectedValue);
11981
+ return found && found.value ? found.label : '';
11982
+ }
11983
+ const match = this.options.find((o) => `${o.value}` === `${this.selectedValue}`);
11984
+ return match && match.value !== '' && match.value != null ? match.label : '';
11985
+ }
11986
+ get hasValue() {
11987
+ if (this.mode === 'multi') {
11988
+ return this.selectedValues.length > 0;
11989
+ }
11990
+ return this.selectedValue !== '' && this.selectedValue != null;
11991
+ }
11992
+ isOptionSelected(opt) {
11993
+ if (this.mode === 'multi') {
11994
+ return this.selectedValues.includes(opt.value);
11995
+ }
11996
+ return `${opt.value}` === `${this.selectedValue}`;
11997
+ }
11998
+ /** Attribute options filtered by the in-dropdown search box (multi mode). */
11999
+ get filteredOptions() {
12000
+ if (this.mode !== 'multi' || !this.attrSearch) {
12001
+ return this.options;
12002
+ }
12003
+ const q = this.attrSearch.toLowerCase();
12004
+ return this.options.filter((o) => `${o.label ?? o.value}`.toLowerCase().includes(q));
12005
+ }
12006
+ /** A named attribute (rendered with @ + secondary color) vs a role attribute (# + primary). */
12007
+ isNamedOption(opt) {
12008
+ return this.isAttributeMode && String(opt?.value ?? opt?.label ?? '').charAt(0) === '@';
12009
+ }
12010
+ /** Option label. The attribute flavor prefixes # (role) / keeps @ (named); others render plain. */
12011
+ optionLabel(opt) {
12012
+ const raw = String(opt?.label ?? opt?.value ?? '');
12013
+ if (!this.isAttributeMode) {
12014
+ return raw;
12015
+ }
12016
+ if (raw.charAt(0) === '@' || raw.charAt(0) === '#') {
12017
+ return raw;
12018
+ }
12019
+ return '#' + raw;
12020
+ }
12021
+ // ------------------------------------------------------------------ open/close
12022
+ toggleMenu(event) {
12023
+ event.stopPropagation();
12024
+ if (this.open) {
12025
+ this.open = false;
12026
+ return;
12027
+ }
12028
+ const rect = this.trigger?.nativeElement.getBoundingClientRect();
12029
+ if (rect) {
12030
+ this.menuLeft = rect.left;
12031
+ this.menuTop = rect.bottom + 4;
12032
+ this.menuWidth = Math.max(rect.width, this.mode === 'multi' ? 220 : 160);
12033
+ }
12034
+ // refresh dynamic option sources (e.g. role attributes) each open
12035
+ this.options = this.resolveOptions();
12036
+ this.attrSearch = '';
12037
+ this.open = true;
12038
+ this.listSvc.notifyMenuOpened(this);
12039
+ }
12040
+ closeMenu() {
12041
+ this.open = false;
12042
+ }
12043
+ // ------------------------------------------------------------------ text
12044
+ onTextInput() {
12045
+ this.textDebounced();
12046
+ }
12047
+ emitText() {
12048
+ this.filterService.updateFilter({
12049
+ filterName: this.column.label,
12050
+ columnId: this.field,
12051
+ value: this.textValue,
12052
+ label: this.textValue,
12053
+ type: 'TEXTINPUT',
12054
+ });
12055
+ }
12056
+ // ------------------------------------------------------------------ single
12057
+ selectOption(opt) {
12058
+ this.selectedValue = opt.value;
12059
+ this.open = false;
12060
+ if (opt.value === '' || opt.value == null) {
12061
+ this.removeThis();
12062
+ return;
12063
+ }
12064
+ this.filterService.updateFilter({
12065
+ filterName: this.column.label,
12066
+ columnId: this.field,
12067
+ value: opt.value,
12068
+ label: opt.label,
12069
+ type: this.column.filterType || 'SELECT',
12070
+ });
12071
+ }
12072
+ // ------------------------------------------------------------------ multi
12073
+ toggleOption(opt) {
12074
+ const idx = this.selectedValues.indexOf(opt.value);
12075
+ if (idx >= 0) {
12076
+ this.selectedValues.splice(idx, 1);
12077
+ }
12078
+ else {
12079
+ this.selectedValues.push(opt.value);
12080
+ }
12081
+ this.emitMulti();
12082
+ }
12083
+ emitMulti() {
12084
+ if (!this.selectedValues.length) {
12085
+ this.removeThis();
12086
+ return;
12087
+ }
12088
+ const filter = {
12089
+ filterName: this.column.label,
12090
+ columnId: this.field,
12091
+ value: [...this.selectedValues],
12092
+ label: this.selectedValues.join(', '),
12093
+ type: this.isAttributeMode ? 'ATTRIBUTE' : 'MULTISELECT',
12094
+ semantic: 'AnyOf',
12095
+ };
12096
+ this.filterService.updateFilter(filter);
12097
+ }
12098
+ // ------------------------------------------------------------------ date range
12099
+ selectDatePreset(opt) {
12100
+ this.selectedValue = opt.value;
12101
+ this.dateRangeStart = null;
12102
+ this.dateRangeEnd = null;
12103
+ this.open = false;
12104
+ if (!opt.value) {
12105
+ this.removeThis();
12106
+ return;
12107
+ }
12108
+ const end = moment$1();
12109
+ let start;
12110
+ switch (opt.value) {
12111
+ case 'hour':
12112
+ start = moment$1().subtract(1, 'hours');
12113
+ break;
12114
+ case 'week':
12115
+ start = moment$1().subtract(7, 'days');
12116
+ break;
12117
+ case 'month':
12118
+ start = moment$1().subtract(1, 'month');
12119
+ break;
12120
+ case 'day':
12121
+ default:
12122
+ start = moment$1().subtract(24, 'hours');
12123
+ break;
12124
+ }
12125
+ this.filterService.updateFilter({
12126
+ filterName: this.column.label,
12127
+ columnId: this.field,
12128
+ value: [encodeURIComponent(start.toISOString()), encodeURIComponent(end.toISOString())],
12129
+ label: opt.label,
12130
+ type: 'DATETIME',
12131
+ });
12132
+ }
12133
+ openCustomRange(event) {
12134
+ event.stopPropagation();
12135
+ this.open = false;
12136
+ // open the Material calendar on the next tick (after the menu closes)
12137
+ _.delay(() => this.rangePicker?.open(), 0);
12138
+ }
12139
+ onRangeStartChanged(date) {
12140
+ this.dateRangeStart = date;
12141
+ this.emitCustomRange();
12142
+ }
12143
+ onRangeEndChanged(date) {
12144
+ this.dateRangeEnd = date;
12145
+ this.emitCustomRange();
12146
+ }
12147
+ emitCustomRange() {
12148
+ if (!this.dateRangeStart || !this.dateRangeEnd) {
12149
+ return;
12150
+ }
12151
+ this.selectedValue = 'custom';
12152
+ const start = moment$1(this.dateRangeStart).startOf('day');
12153
+ const end = moment$1(this.dateRangeEnd).endOf('day');
12154
+ this.filterService.updateFilter({
12155
+ filterName: this.column.label,
12156
+ columnId: this.field,
12157
+ value: [encodeURIComponent(start.toISOString()), encodeURIComponent(end.toISOString())],
12158
+ label: `${start.format('M/D/YY')} – ${end.format('M/D/YY')}`,
12159
+ type: 'DATETIME',
12160
+ });
12161
+ }
12162
+ // ------------------------------------------------------------------ clear
12163
+ clear(event) {
12164
+ event.stopPropagation();
12165
+ this.selectedValue = '';
12166
+ this.selectedValues = [];
12167
+ this.textValue = '';
12168
+ this.dateRangeStart = null;
12169
+ this.dateRangeEnd = null;
12170
+ this.removeThis();
12171
+ }
12172
+ removeThis() {
12173
+ this.filterService.removeFilter({
12174
+ filterName: this.column.label,
12175
+ columnId: this.field,
12176
+ value: '',
12177
+ label: '',
12178
+ });
12179
+ }
12180
+ // ------------------------------------------------------------------ sync
12181
+ syncFromFilters(filters) {
12182
+ const match = filters.find((f) => f.columnId === this.field);
12183
+ if (!match) {
12184
+ this.textValue = '';
12185
+ this.selectedValue = '';
12186
+ this.selectedValues = [];
12187
+ this.dateRangeStart = null;
12188
+ this.dateRangeEnd = null;
12189
+ return;
12190
+ }
12191
+ if (this.mode === 'text') {
12192
+ this.textValue = match.value ?? '';
12193
+ }
12194
+ else if (this.mode === 'multi') {
12195
+ this.selectedValues = _.isArray(match.value) ? [...match.value] : [match.value];
12196
+ }
12197
+ else if (this.mode === 'daterange') {
12198
+ // keep the locally chosen preset label; match.value is the raw [start,end] range
12199
+ }
12200
+ else {
12201
+ this.selectedValue = match.value;
12202
+ }
12203
+ }
12204
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListFilterCellComponent, deps: [{ token: DataTableFilterService }, { token: ListTableService }], target: i0.ɵɵFactoryTarget.Component }); }
12205
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListFilterCellComponent, isStandalone: false, selector: "lib-list-filter-cell", inputs: { column: "column" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "rangePicker", first: true, predicate: ["rangePicker"], descendants: true }], ngImport: i0, template: "@switch (mode) {\n @case ('text') {\n <div class=\"fc-text\">\n <input\n class=\"fc-input\"\n type=\"text\"\n [placeholder]=\"column.filterPlaceholder || 'Filter'\"\n [(ngModel)]=\"textValue\"\n (keyup)=\"onTextInput()\"\n />\n @if (textValue) {\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">&times;</span>\n }\n </div>\n }\n @default {\n <button\n #trigger\n type=\"button\"\n class=\"fc-pill\"\n [class.has-value]=\"hasValue\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"fc-pill-field\">{{ column.label }}</span>\n @if (hasValue) {\n <span class=\"fc-pill-value\" [title]=\"valueLabel\">{{ valueLabel }}</span>\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">&times;</span>\n } @else {\n <span class=\"fc-caret\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 10 6\" width=\"9\" height=\"6\"><path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/></svg>\n </span>\n }\n </button>\n\n @if (open) {\n <div\n class=\"fc-menu\"\n (clickOutside)=\"closeMenu()\"\n [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px', 'min-width.px': menuWidth }\"\n >\n @if (mode === 'multi') {\n <div class=\"fc-search\">\n <svg class=\"fc-search-icon\" viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"fc-search-input\"\n type=\"text\"\n [(ngModel)]=\"attrSearch\"\n [placeholder]=\"isAttributeMode ? 'Search attributes' : 'Search'\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\"\n />\n </div>\n @if (!filteredOptions.length) {\n <div class=\"fc-empty\">{{ options.length ? 'No matches' : (isAttributeMode ? 'No attributes available' : 'No options available') }}</div>\n }\n <div class=\"fc-opt-list\">\n @for (opt of filteredOptions; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"toggleOption(opt)\">\n <span class=\"fc-check\" aria-hidden=\"true\">\n @if (isOptionSelected(opt)) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\"><path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/></svg>\n }\n </span>\n @if (isAttributeMode) {\n <span class=\"fc-attr\" [class.at]=\"isNamedOption(opt)\">{{ optionLabel(opt) }}</span>\n } @else if (opt.chipClass) {\n <span [ngClass]=\"opt.chipClass\">{{ opt.label }}</span>\n } @else {\n <span class=\"fc-opt-label\">{{ optionLabel(opt) }}</span>\n }\n </button>\n }\n </div>\n } @else if (mode === 'daterange') {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectDatePreset(opt)\">\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n </button>\n }\n <div class=\"fc-divider\"></div>\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"selectedValue === 'custom'\" (click)=\"openCustomRange($event)\">\n <span class=\"fc-opt-label\">Custom range\u2026</span>\n </button>\n } @else {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectOption(opt)\">\n @if (opt.chipClass) {\n <span [ngClass]=\"opt.chipClass\">{{ opt.label }}</span>\n } @else {\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n }\n}\n\n@if (mode === 'daterange') {\n <!-- hidden Material range input; the calendar is opened from the \"Custom range\u2026\" option -->\n <mat-date-range-input class=\"fc-hidden-range\" [rangePicker]=\"rangePicker\">\n <input matStartDate [(ngModel)]=\"dateRangeStart\" (dateChange)=\"onRangeStartChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n <input matEndDate [(ngModel)]=\"dateRangeEnd\" (dateChange)=\"onRangeEndChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n </mat-date-range-input>\n <mat-date-range-picker #rangePicker xPosition=\"start\" yPosition=\"below\"></mat-date-range-picker>\n}\n", styles: [":host{display:inline-flex;position:relative}.fc-hidden-range{position:absolute;left:0;top:100%;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden}.fc-divider{height:1px;margin:.25rem;background:var(--lt-border)}.fc-text{position:relative;display:flex;align-items:center}.fc-input{width:100%;height:1.875rem;padding:0 1.5rem 0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-input::placeholder{color:var(--placeholder)}.fc-input:focus{border-color:var(--lt-link)}.fc-pill{display:inline-flex;align-items:center;gap:.375rem;height:var(--lt-toolbar-height);max-width:14rem;padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.fc-pill:hover,.fc-pill.is-open{border-color:var(--lt-link)}.fc-pill.has-value{border-color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 8%,var(--lt-control-bg))}.fc-pill-field{color:var(--lt-cell-text)}.fc-pill.has-value .fc-pill-field{color:var(--groupText)}.fc-pill.has-value .fc-pill-field:after{content:\":\"}.fc-pill-value{color:var(--lt-link);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-caret{flex:0 0 auto;display:inline-flex;color:var(--lt-icon)}.fc-clear{flex:0 0 auto;font-size:1rem;line-height:1;color:var(--lt-icon);cursor:pointer}.fc-clear:hover{color:var(--lt-icon-active)}.fc-text .fc-clear{position:absolute;right:.5rem}.fc-menu{position:fixed;z-index:1000;max-height:18rem;overflow-y:auto;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:fc-in .1s ease-out}@keyframes fc-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.fc-opt{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;text-align:left}.fc-opt:hover{background:var(--lt-row-hover)}.fc-opt.selected{color:var(--lt-link);font-weight:600}.fc-opt-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-check{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;box-sizing:border-box;border:.09375rem solid var(--lt-border);border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg)}.fc-opt.selected .fc-check{background:var(--lt-link);border-color:var(--lt-link)}.fc-empty{padding:.5rem;font-size:.75rem;color:var(--groupText)}.fc-search{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:.375rem;padding:.125rem .25rem .375rem;background:var(--lt-control-bg)}.fc-search-icon{flex:0 0 auto;margin-left:.25rem;color:var(--lt-icon)}.fc-search-input{flex:1 1 auto;min-width:0;height:1.75rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-search-input::placeholder{color:var(--placeholder)}.fc-search-input:focus{border-color:var(--lt-link)}.fc-opt-list{display:flex;flex-direction:column}.fc-attr{display:inline-flex;align-items:center;padding:.0625rem .375rem;font-size:.75rem;font-weight:600;line-height:1.4;border-radius:.25rem;color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-attr.at{color:var(--secondary);background:color-mix(in srgb,var(--secondary) 14%,transparent)}.fc-opt.selected .fc-attr{color:var(--lt-link)}.fc-opt.selected .fc-attr.at{color:var(--secondary)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }] }); }
12206
+ }
12207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListFilterCellComponent, decorators: [{
12208
+ type: Component,
12209
+ args: [{ selector: 'lib-list-filter-cell', standalone: false, template: "@switch (mode) {\n @case ('text') {\n <div class=\"fc-text\">\n <input\n class=\"fc-input\"\n type=\"text\"\n [placeholder]=\"column.filterPlaceholder || 'Filter'\"\n [(ngModel)]=\"textValue\"\n (keyup)=\"onTextInput()\"\n />\n @if (textValue) {\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">&times;</span>\n }\n </div>\n }\n @default {\n <button\n #trigger\n type=\"button\"\n class=\"fc-pill\"\n [class.has-value]=\"hasValue\"\n [class.is-open]=\"open\"\n (click)=\"toggleMenu($event)\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open\"\n >\n <span class=\"fc-pill-field\">{{ column.label }}</span>\n @if (hasValue) {\n <span class=\"fc-pill-value\" [title]=\"valueLabel\">{{ valueLabel }}</span>\n <span class=\"fc-clear\" (click)=\"clear($event)\" title=\"Clear\" aria-label=\"Clear filter\">&times;</span>\n } @else {\n <span class=\"fc-caret\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 10 6\" width=\"9\" height=\"6\"><path fill=\"currentColor\" d=\"M0 0h10L5 6z\"/></svg>\n </span>\n }\n </button>\n\n @if (open) {\n <div\n class=\"fc-menu\"\n (clickOutside)=\"closeMenu()\"\n [ngStyle]=\"{ left: menuLeft + 'px', top: menuTop + 'px', 'min-width.px': menuWidth }\"\n >\n @if (mode === 'multi') {\n <div class=\"fc-search\">\n <svg class=\"fc-search-icon\" viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"fc-search-input\"\n type=\"text\"\n [(ngModel)]=\"attrSearch\"\n [placeholder]=\"isAttributeMode ? 'Search attributes' : 'Search'\"\n (click)=\"$event.stopPropagation()\"\n autocomplete=\"off\"\n />\n </div>\n @if (!filteredOptions.length) {\n <div class=\"fc-empty\">{{ options.length ? 'No matches' : (isAttributeMode ? 'No attributes available' : 'No options available') }}</div>\n }\n <div class=\"fc-opt-list\">\n @for (opt of filteredOptions; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"toggleOption(opt)\">\n <span class=\"fc-check\" aria-hidden=\"true\">\n @if (isOptionSelected(opt)) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\"><path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/></svg>\n }\n </span>\n @if (isAttributeMode) {\n <span class=\"fc-attr\" [class.at]=\"isNamedOption(opt)\">{{ optionLabel(opt) }}</span>\n } @else if (opt.chipClass) {\n <span [ngClass]=\"opt.chipClass\">{{ opt.label }}</span>\n } @else {\n <span class=\"fc-opt-label\">{{ optionLabel(opt) }}</span>\n }\n </button>\n }\n </div>\n } @else if (mode === 'daterange') {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectDatePreset(opt)\">\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n </button>\n }\n <div class=\"fc-divider\"></div>\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"selectedValue === 'custom'\" (click)=\"openCustomRange($event)\">\n <span class=\"fc-opt-label\">Custom range\u2026</span>\n </button>\n } @else {\n @for (opt of options; track opt.value) {\n <button type=\"button\" class=\"fc-opt\" [class.selected]=\"isOptionSelected(opt)\" (click)=\"selectOption(opt)\">\n @if (opt.chipClass) {\n <span [ngClass]=\"opt.chipClass\">{{ opt.label }}</span>\n } @else {\n <span class=\"fc-opt-label\">{{ opt.label }}</span>\n }\n </button>\n }\n }\n </div>\n }\n }\n}\n\n@if (mode === 'daterange') {\n <!-- hidden Material range input; the calendar is opened from the \"Custom range\u2026\" option -->\n <mat-date-range-input class=\"fc-hidden-range\" [rangePicker]=\"rangePicker\">\n <input matStartDate [(ngModel)]=\"dateRangeStart\" (dateChange)=\"onRangeStartChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n <input matEndDate [(ngModel)]=\"dateRangeEnd\" (dateChange)=\"onRangeEndChanged($event.value)\" tabindex=\"-1\" aria-hidden=\"true\" />\n </mat-date-range-input>\n <mat-date-range-picker #rangePicker xPosition=\"start\" yPosition=\"below\"></mat-date-range-picker>\n}\n", styles: [":host{display:inline-flex;position:relative}.fc-hidden-range{position:absolute;left:0;top:100%;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden}.fc-divider{height:1px;margin:.25rem;background:var(--lt-border)}.fc-text{position:relative;display:flex;align-items:center}.fc-input{width:100%;height:1.875rem;padding:0 1.5rem 0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-input::placeholder{color:var(--placeholder)}.fc-input:focus{border-color:var(--lt-link)}.fc-pill{display:inline-flex;align-items:center;gap:.375rem;height:var(--lt-toolbar-height);max-width:14rem;padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.fc-pill:hover,.fc-pill.is-open{border-color:var(--lt-link)}.fc-pill.has-value{border-color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 8%,var(--lt-control-bg))}.fc-pill-field{color:var(--lt-cell-text)}.fc-pill.has-value .fc-pill-field{color:var(--groupText)}.fc-pill.has-value .fc-pill-field:after{content:\":\"}.fc-pill-value{color:var(--lt-link);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-caret{flex:0 0 auto;display:inline-flex;color:var(--lt-icon)}.fc-clear{flex:0 0 auto;font-size:1rem;line-height:1;color:var(--lt-icon);cursor:pointer}.fc-clear:hover{color:var(--lt-icon-active)}.fc-text .fc-clear{position:absolute;right:.5rem}.fc-menu{position:fixed;z-index:1000;max-height:18rem;overflow-y:auto;padding:.25rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:fc-in .1s ease-out}@keyframes fc-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.fc-opt{display:flex;align-items:center;gap:.5rem;width:100%;padding:.4375rem .5rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;text-align:left}.fc-opt:hover{background:var(--lt-row-hover)}.fc-opt.selected{color:var(--lt-link);font-weight:600}.fc-opt-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-check{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;box-sizing:border-box;border:.09375rem solid var(--lt-border);border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg)}.fc-opt.selected .fc-check{background:var(--lt-link);border-color:var(--lt-link)}.fc-empty{padding:.5rem;font-size:.75rem;color:var(--groupText)}.fc-search{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:.375rem;padding:.125rem .25rem .375rem;background:var(--lt-control-bg)}.fc-search-icon{flex:0 0 auto;margin-left:.25rem;color:var(--lt-icon)}.fc-search-input{flex:1 1 auto;min-width:0;height:1.75rem;padding:0 .5rem;font-family:var(--lt-font);font-size:.75rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);outline:none}.fc-search-input::placeholder{color:var(--placeholder)}.fc-search-input:focus{border-color:var(--lt-link)}.fc-opt-list{display:flex;flex-direction:column}.fc-attr{display:inline-flex;align-items:center;padding:.0625rem .375rem;font-size:.75rem;font-weight:600;line-height:1.4;border-radius:.25rem;color:var(--lt-link);background:color-mix(in srgb,var(--lt-link) 12%,transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-attr.at{color:var(--secondary);background:color-mix(in srgb,var(--secondary) 14%,transparent)}.fc-opt.selected .fc-attr{color:var(--lt-link)}.fc-opt.selected .fc-attr.at{color:var(--secondary)}\n"] }]
12210
+ }], ctorParameters: () => [{ type: DataTableFilterService }, { type: ListTableService }], propDecorators: { column: [{
12211
+ type: Input
12212
+ }], trigger: [{
12213
+ type: ViewChild,
12214
+ args: ['trigger']
12215
+ }], rangePicker: [{
12216
+ type: ViewChild,
12217
+ args: ['rangePicker']
12218
+ }] } });
12219
+
12220
+ /*
12221
+ Copyright NetFoundry Inc.
12222
+
12223
+ Licensed under the Apache License, Version 2.0 (the "License");
12224
+ you may not use this file except in compliance with the License.
12225
+ You may obtain a copy of the License at
12226
+
12227
+ https://www.apache.org/licenses/LICENSE-2.0
12228
+
12229
+ Unless required by applicable law or agreed to in writing, software
12230
+ distributed under the License is distributed on an "AS IS" BASIS,
12231
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12232
+ See the License for the specific language governing permissions and
12233
+ limitations under the License.
12234
+ */
12235
+ /**
12236
+ * Custom, token-skinnable list table. Columns are declared with {@link ListColumn};
12237
+ * sort, filter, and pagination flow through {@link DataTableFilterService}.
12238
+ */
12239
+ class ListTableComponent {
12240
+ set tableId(id) {
12241
+ this._tableId = id;
12242
+ this.svc.tableId = id;
12243
+ }
12244
+ get tableId() {
12245
+ return this._tableId;
12246
+ }
12247
+ set columns(value) {
12248
+ this._rawColumns = value || [];
12249
+ if (this._initialized) {
12250
+ this.buildColumns();
12251
+ }
12252
+ }
12253
+ constructor(svc, tableFilterService, el, ngZone, sanitizer) {
12254
+ this.svc = svc;
12255
+ this.tableFilterService = tableFilterService;
12256
+ this.el = el;
12257
+ this.ngZone = ngZone;
12258
+ this.sanitizer = sanitizer;
12259
+ this._tableId = '';
12260
+ this.rowData = [];
12261
+ this.isLoading = false;
12262
+ this.options = { noSelect: false, noMenu: false };
12263
+ this.startCount = '-';
12264
+ this.endCount = '-';
12265
+ this.totalCount = '-';
12266
+ this.filterApplied = false;
12267
+ this.menuItems = [];
12268
+ this.headerActions = [];
12269
+ this.noItemsImage = 'nodata';
12270
+ this.showNoItemsAdd = true;
12271
+ this.noItemsReadonly = false;
12272
+ this.accessDenied = false;
12273
+ this.entityTypeName = '';
12274
+ this.filterName = 'name';
12275
+ this.filterColumn = 'name';
12276
+ this.actionRequested = new EventEmitter();
12277
+ this.tableReady = new EventEmitter();
12278
+ /** Full column list (includes hidden), after persisted state. */
12279
+ this.allColumns = [];
12280
+ /** Currently rendered columns (visible + ordered). */
12281
+ this.visibleColumns = [];
12282
+ /** Hidden columns, shaped for the shared hidden-columns bar (`headerName`). */
12283
+ this.hiddenColumns = [];
12284
+ this.entityTypeLabel = '';
12285
+ // ---- action menus ----
12286
+ this.openMenu = false;
12287
+ this.openHeaderMenu = false;
12288
+ this.menuLeft = 0;
12289
+ this.menuTop = 0;
12290
+ this.selectedItem = { actionList: [] };
12291
+ // ---- selection ----
12292
+ this.allToggled = false;
12293
+ // ---- column chooser (skinned show/hide + reorder panel) ----
12294
+ this.showColumnChooser = false;
12295
+ // ---- integrated toolbar: search + filter chips + pagination ----
12296
+ this.searchString = '';
12297
+ this.activeFilters = [];
12298
+ this.filtering = false;
12299
+ this.inputChangedDebounced = _.debounce(() => this.applySearch(), 400);
12300
+ // ---- responsive filter overflow: controls that don't fit collapse into "More" ----
12301
+ this.visibleFilterColumns = [];
12302
+ this.overflowFilterColumns = [];
12303
+ this.showFilterOverflow = false;
12304
+ this.filterWidths = new Map();
12305
+ this.filtersDirty = true;
12306
+ this.overflowRaf = 0;
12307
+ this.FILTER_GAP = 8; // px, matches the .lt-filter-zone/.lt-filters gap
12308
+ this.MORE_BTN_W = 92; // px reserved for the "More" trigger when overflowing
12309
+ this.onPointerMove = (e) => this.handleResizeMove(e);
12310
+ this.onPointerUp = () => this.handleResizeEnd();
12311
+ this._rawColumns = [];
12312
+ this._initialColumns = [];
12313
+ this._initialized = false;
12314
+ this.subscription = new Subscription();
12315
+ this.trackRow = (index, row) => row?.id ?? row?.name ?? index;
12316
+ this.trackCol = (index, col) => col.key;
12317
+ // ---------------------------------------------------------------- hover tooltip
12318
+ this.hoverTooltip = null;
12319
+ // ---------------------------------------------------------------- selection
12320
+ this.toggleItem = (item) => {
12321
+ item.selected = !item.selected;
12322
+ this.allToggled = !_.isEmpty(this.rowData) && _.every(this.rowData, { selected: true });
12323
+ this.actionRequested.emit({ action: 'toggleItem', item });
12324
+ };
12325
+ // ---------------------------------------------------------------- action menus
12326
+ this.openActionMenu = (event, item) => {
12327
+ this.selectedItem = item;
12328
+ this.openMenu = true;
12329
+ _.delay(() => {
12330
+ const height = this.contextMenu?.nativeElement?.offsetHeight || 120;
12331
+ const windowOffset = window.innerHeight - event.clientY;
12332
+ const menuOffset = windowOffset <= height ? height - windowOffset : 0;
12333
+ this.menuLeft = event.clientX - 100;
12334
+ this.menuTop = event.clientY + 5 - menuOffset;
12335
+ }, 10);
12336
+ };
12337
+ this.closeActionMenu = () => {
12338
+ this.selectedItem = { actionList: [] };
12339
+ this.openMenu = false;
12340
+ this.openHeaderMenu = false;
12341
+ };
12342
+ this.closeHeaderActionMenu = () => {
12343
+ this.openHeaderMenu = false;
12344
+ };
12345
+ /** Restore-default (rotate) icon for the built-in header action. */
12346
+ this.RESET_TABLE_ICON = '<svg viewBox="0 0 16 16" width="15" height="15"><path fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" d="M3.2 8a4.8 4.8 0 1 0 1.3-3.3"/><path fill="currentColor" d="M2.8 2.8l-.3 2.9 2.8-.4z"/></svg>';
12347
+ // ---- rows-per-page + numbered pagination (the skin footer) ----
12348
+ this.pageSizeOptions = [25, 50, 100];
12349
+ /** Default rows-per-page when nothing is remembered (the persisted choice wins). */
12350
+ this.initialPageSize = 50;
12351
+ // ---- auto-refresh (the skin toolbar toggle) ----
12352
+ this.autoRefresh = false;
12353
+ this.autoRefreshIntervalMs = 30000;
12354
+ // ---- row action icons ----
12355
+ // A menu item's host-supplied `icon` (SVG markup) is rendered before its label in
12356
+ // the row's kebab menu. Sanitized once per unique SVG and cached.
12357
+ this.iconCache = new Map();
12358
+ }
12359
+ ngOnInit() {
12360
+ this._initialized = true;
12361
+ // set directly (no emit) to avoid forcing a second fetch when a host pre-seeded it
12362
+ this.tableFilterService.pageSize = this.svc.restorePageSize(this.initialPageSize);
12363
+ this.buildColumns();
12364
+ this.updateEntityTypeLabel();
12365
+ this.subscription.add(this.tableFilterService.filtersChanged.subscribe((filters) => {
12366
+ // chips: every non-hidden filter except the free-text search (shown in the box)
12367
+ this.activeFilters = filters.filter((f) => f.hidden !== true && f.columnId !== this.filterColumn);
12368
+ const match = filters.find((f) => f.columnId === this.filterColumn);
12369
+ this.searchString = match ? match.value : '';
12370
+ // an activated filter changes which controls are pinned and how wide they render
12371
+ this.filtersDirty = true;
12372
+ this.scheduleFilterOverflow();
12373
+ }));
12374
+ this.subscription.add(this.tableFilterService.filtering.subscribe((f) => (this.filtering = f)));
12375
+ // close the column chooser whenever a filter dropdown (or anything else) opens
12376
+ this.subscription.add(this.svc.menuOpened$.subscribe((owner) => {
12377
+ if (owner !== this) {
12378
+ this.showColumnChooser = false;
12379
+ }
12380
+ }));
12381
+ this.tableReady.emit({ component: this });
12382
+ }
12383
+ ngAfterViewInit() {
12384
+ this.ensureFilterResizeObserver();
12385
+ }
12386
+ ngAfterViewChecked() {
12387
+ // the whole table sits behind @if(showTable), so the toolbar may not exist
12388
+ // until row data arrives; (re)attach the observer once/whenever it appears
12389
+ this.ensureFilterResizeObserver();
12390
+ }
12391
+ ngOnDestroy() {
12392
+ this.subscription.unsubscribe();
12393
+ this.detachResizeListeners();
12394
+ this.filterRO?.disconnect();
12395
+ if (this.overflowRaf) {
12396
+ cancelAnimationFrame(this.overflowRaf);
12397
+ }
12398
+ if (this.autoRefreshTimer) {
12399
+ clearInterval(this.autoRefreshTimer);
12400
+ }
12401
+ }
12402
+ get showTable() {
12403
+ return this.isLoading || this.filterApplied || this.rowData?.length > 0;
12404
+ }
12405
+ /** Columns surfaced as inline filter controls in the toolbar. Text filters are
12406
+ * excluded because the free-text search box already covers the primary text field. */
12407
+ get filterableColumns() {
12408
+ return this.allColumns.filter((c) => c.filterType && c.filterType !== 'TEXTINPUT');
12409
+ }
12410
+ // -------------------------------------------------- responsive filter overflow
12411
+ /** A filter column is "active" when it has an applied filter. */
12412
+ isFilterActive(col) {
12413
+ return this.activeFilters.some((f) => f.columnId === col.key);
12414
+ }
12415
+ /** Applied filters that are currently hidden in the overflow dropdown. */
12416
+ get hiddenActiveCount() {
12417
+ return this.overflowFilterColumns.filter((c) => this.isFilterActive(c)).length;
12418
+ }
12419
+ toggleFilterOverflow(event) {
12420
+ event.stopPropagation();
12421
+ this.showFilterOverflow = !this.showFilterOverflow;
12422
+ if (this.showFilterOverflow) {
12423
+ this.svc.notifyMenuOpened(this);
12424
+ }
12425
+ }
12426
+ closeFilterOverflow(event) {
12427
+ // keep open when the click lands in a filter's own Material overlay (e.g. the
12428
+ // date-range calendar), which renders outside this popover's DOM subtree
12429
+ const target = event?.target;
12430
+ if (target?.closest?.('.cdk-overlay-container')) {
12431
+ return;
12432
+ }
12433
+ this.showFilterOverflow = false;
12434
+ }
12435
+ ensureFilterResizeObserver() {
12436
+ // observe the toolbar row (not the content-sized zone) so a window resize,
12437
+ // which changes the row's available width, actually fires the observer
12438
+ const target = this.filterZone?.nativeElement?.parentElement ?? undefined;
12439
+ if (!target || typeof ResizeObserver === 'undefined' || target === this.observedToolbar) {
12440
+ return;
12441
+ }
12442
+ // ResizeObserver fires outside Angular's zone; re-enter so the recomputed
12443
+ // visible/overflow split actually triggers change detection.
12444
+ if (!this.filterRO) {
12445
+ this.filterRO = new ResizeObserver(() => this.ngZone.run(() => this.scheduleFilterOverflow()));
12446
+ }
12447
+ else {
12448
+ this.filterRO.disconnect();
12449
+ }
12450
+ this.filterRO.observe(target);
12451
+ this.observedToolbar = target;
12452
+ // compute the split for the newly-observed row (deferred, so it never mutates
12453
+ // bound state inside the change-detection pass that called us)
12454
+ this.scheduleFilterOverflow();
12455
+ }
12456
+ scheduleFilterOverflow() {
12457
+ if (this.overflowRaf) {
12458
+ cancelAnimationFrame(this.overflowRaf);
12459
+ }
12460
+ this.overflowRaf = requestAnimationFrame(() => {
12461
+ this.overflowRaf = 0;
12462
+ this.ensureFilterResizeObserver();
12463
+ this.recomputeFilterOverflow();
12464
+ });
12465
+ }
12466
+ recomputeFilterOverflow() {
12467
+ const cols = this.filterableColumns;
12468
+ if (!cols.length) {
12469
+ this.visibleFilterColumns = [];
12470
+ this.overflowFilterColumns = [];
12471
+ return;
12472
+ }
12473
+ const needMeasure = this.filtersDirty || cols.some((c) => !this.filterWidths.has(c.key));
12474
+ if (!needMeasure) {
12475
+ this.applyFilterSplit();
12476
+ return;
12477
+ }
12478
+ // render every control inline for one frame so each can be measured, then collapse
12479
+ this.visibleFilterColumns = cols;
12480
+ this.overflowFilterColumns = [];
12481
+ requestAnimationFrame(() => {
12482
+ const row = this.filtersRow?.nativeElement;
12483
+ if (row) {
12484
+ row.querySelectorAll('[data-filter-key]').forEach((el) => {
12485
+ const key = el.getAttribute('data-filter-key');
12486
+ if (key) {
12487
+ this.filterWidths.set(key, Math.ceil(el.getBoundingClientRect().width));
12488
+ }
12489
+ });
12490
+ }
12491
+ this.filtersDirty = false;
12492
+ this.applyFilterSplit();
12493
+ });
12494
+ }
12495
+ /** Split filter controls into inline vs. overflow, pinning active filters and
12496
+ * dropping inactive (then, only if forced, active) from the right until they fit. */
12497
+ applyFilterSplit() {
12498
+ const zone = this.filterZone?.nativeElement;
12499
+ const cols = this.filterableColumns;
12500
+ if (!zone || !cols.length) {
12501
+ return;
12502
+ }
12503
+ // budget = the toolbar row's width minus its fixed siblings (search / divider /
12504
+ // Columns) and the gaps between them, i.e. the space filters may take before
12505
+ // they'd push the Columns control off the row
12506
+ const toolbarLeft = zone.parentElement;
12507
+ const siblingWidth = (sel) => {
12508
+ const node = toolbarLeft?.querySelector(sel);
12509
+ return node ? Math.ceil(node.getBoundingClientRect().width) : 0;
12510
+ };
12511
+ const available = (toolbarLeft?.clientWidth ?? zone.clientWidth) -
12512
+ siblingWidth('.lt-search') -
12513
+ siblingWidth('.lt-divider') -
12514
+ siblingWidth('.lt-columns') -
12515
+ 3 * this.FILTER_GAP;
12516
+ const widthOf = (c) => this.filterWidths.get(c.key) ?? 0;
12517
+ const totalOf = (list, withMore) => {
12518
+ let w = 0;
12519
+ list.forEach((c, i) => (w += widthOf(c) + (i > 0 ? this.FILTER_GAP : 0)));
12520
+ if (withMore) {
12521
+ w += (list.length ? this.FILTER_GAP : 0) + this.MORE_BTN_W;
12522
+ }
12523
+ return w;
12524
+ };
12525
+ if (totalOf(cols, false) <= available) {
12526
+ this.visibleFilterColumns = cols;
12527
+ this.overflowFilterColumns = [];
12528
+ return;
12529
+ }
12530
+ // removal priority: inactive right-to-left first, then active right-to-left
12531
+ const inactive = cols.filter((c) => !this.isFilterActive(c)).reverse();
12532
+ const active = cols.filter((c) => this.isFilterActive(c)).reverse();
12533
+ const removalOrder = [...inactive, ...active];
12534
+ const overflow = new Set();
12535
+ for (const c of removalOrder) {
12536
+ overflow.add(c.key);
12537
+ const visible = cols.filter((x) => !overflow.has(x.key));
12538
+ if (totalOf(visible, true) <= available) {
12539
+ break;
12540
+ }
12541
+ }
12542
+ this.visibleFilterColumns = cols.filter((c) => !overflow.has(c.key));
12543
+ this.overflowFilterColumns = cols.filter((c) => overflow.has(c.key));
12544
+ if (!this.overflowFilterColumns.length) {
12545
+ this.showFilterOverflow = false;
12546
+ }
12547
+ }
12548
+ // ---------------------------------------------------------------- columns
12549
+ buildColumns() {
12550
+ this._initialColumns = this._rawColumns.map((c) => ({ ...c }));
12551
+ this.allColumns = this.svc.applyPersistedState(this._rawColumns);
12552
+ this.updateVisible();
12553
+ // start with every filter control visible; the overflow pass collapses to fit
12554
+ this.visibleFilterColumns = this.filterableColumns;
12555
+ this.overflowFilterColumns = [];
12556
+ this.filtersDirty = true;
12557
+ this.scheduleFilterOverflow();
12558
+ }
12559
+ updateVisible() {
12560
+ this.visibleColumns = this.allColumns.filter((c) => !c.hidden);
12561
+ this.hiddenColumns = this.allColumns
12562
+ .filter((c) => c.hidden)
12563
+ .map((c) => ({ ...c, headerName: c.label }));
12564
+ }
12565
+ colWidth(col) {
12566
+ return col.width != null ? `${col.width}px` : null;
12567
+ }
12568
+ // ---------------------------------------------------------------- cells
12569
+ rawValue(col, row) {
12570
+ return _.get(row, col.field || col.key);
12571
+ }
12572
+ cellContext(col, row, i) {
12573
+ return {
12574
+ row,
12575
+ value: this.rawValue(col, row),
12576
+ colId: col.key,
12577
+ rowIndex: i,
12578
+ emitAction: (action, item = row) => this.actionRequested.emit({ action, item }),
12579
+ };
12580
+ }
12581
+ legacyShim(col, row, i) {
12582
+ return { data: row, value: this.rawValue(col, row), column: { colId: col.key }, rowIndex: i };
12583
+ }
12584
+ renderMode(col) {
12585
+ if (col.cell) {
12586
+ return 'component';
12587
+ }
12588
+ if (col.cellHtml) {
12589
+ return 'html';
12590
+ }
12591
+ return 'text';
12592
+ }
12593
+ cellInputs(col, row, i) {
12594
+ if (col.cellInputs) {
12595
+ return col.cellInputs(this.cellContext(col, row, i));
12596
+ }
12597
+ return { row, value: this.rawValue(col, row) };
12598
+ }
12599
+ cellHtml(col, row, i) {
12600
+ return col.cellHtml ? col.cellHtml(this.legacyShim(col, row, i)) : '';
12601
+ }
12602
+ cellText(col, row, i) {
12603
+ if (col.valueFormatter) {
12604
+ return col.valueFormatter(this.legacyShim(col, row, i));
12605
+ }
12606
+ return this.rawValue(col, row);
12607
+ }
12608
+ cellClass(col) {
12609
+ return col.cellClass || '';
12610
+ }
12611
+ onCellClick(col, row, i) {
12612
+ col.onCellClick?.(this.cellContext(col, row, i));
12613
+ }
12614
+ tooltipInputs(col, row, i) {
12615
+ return col.tooltipInputs ? col.tooltipInputs(this.cellContext(col, row, i)) : { row };
12616
+ }
12617
+ onCellHover(col, row, i, event) {
12618
+ if (!col.tooltip) {
12619
+ return;
12620
+ }
12621
+ const rect = event.currentTarget.getBoundingClientRect();
12622
+ this.hoverTooltip = {
12623
+ component: col.tooltip,
12624
+ inputs: this.tooltipInputs(col, row, i),
12625
+ left: rect.left,
12626
+ top: rect.bottom + 6,
12627
+ };
12628
+ }
12629
+ onCellLeave() {
12630
+ this.hoverTooltip = null;
12631
+ }
12632
+ /** Some rows selected but the header select-all isn't fully on (indeterminate). */
12633
+ get someSelected() {
12634
+ return !this.allToggled && _.some(this.rowData, { selected: true });
12635
+ }
12636
+ toggleAll() {
12637
+ const newState = !this.allToggled;
12638
+ this.allToggled = newState;
12639
+ _.forEach(this.rowData, (row) => {
12640
+ row.selected = newState;
12641
+ });
12642
+ this.actionRequested.emit({ action: 'toggleAll' });
12643
+ }
12644
+ // ---------------------------------------------------------------- sort
12645
+ onHeaderSort(event) {
12646
+ this.allColumns.forEach((c) => {
12647
+ c.sortDir = c.key === event.colId ? event.dir : undefined;
12648
+ });
12649
+ }
12650
+ // ---------------------------------------------------------------- hide / show / reorder
12651
+ hideColumn(col) {
12652
+ const target = this.allColumns.find((c) => c.key === col.key);
12653
+ if (!target) {
12654
+ return;
12655
+ }
12656
+ target.hidden = true;
12657
+ this.updateVisible();
12658
+ this.svc.saveState(this.allColumns);
12659
+ }
12660
+ showColumn(event) {
12661
+ const target = this.allColumns.find((c) => c.key === event.column?.key);
12662
+ if (!target) {
12663
+ return;
12664
+ }
12665
+ target.hidden = false;
12666
+ this.updateVisible();
12667
+ this.svc.saveState(this.allColumns);
12668
+ }
12669
+ onReorder(event) {
12670
+ if (event.previousIndex === event.currentIndex) {
12671
+ return;
12672
+ }
12673
+ moveItemInArray(this.visibleColumns, event.previousIndex, event.currentIndex);
12674
+ const nextVisible = [...this.visibleColumns];
12675
+ let vi = 0;
12676
+ this.allColumns = this.allColumns.map((c) => (c.hidden ? c : nextVisible[vi++]));
12677
+ this.updateVisible();
12678
+ this.svc.saveState(this.allColumns);
12679
+ }
12680
+ resetTableColumns() {
12681
+ this.svc.reset();
12682
+ this.allColumns = this._initialColumns.map((c) => ({ ...c }));
12683
+ this.updateVisible();
12684
+ this.closeActionMenu();
12685
+ }
12686
+ // ---------------------------------------------------------------- column chooser
12687
+ /** Count of columns the user has hidden beyond the defaults (drives the badge).
12688
+ * Columns hidden by default don't count, so a fresh/default load shows no badge. */
12689
+ get hiddenColumnCount() {
12690
+ return this.allColumns.filter((c) => c.hidden && !this.isDefaultHidden(c)).length;
12691
+ }
12692
+ /** True when visibility + order match the originally declared columns. */
12693
+ get columnsAtDefault() {
12694
+ if (this.allColumns.length !== this._initialColumns.length) {
12695
+ return false;
12696
+ }
12697
+ return this.allColumns.every((c, i) => {
12698
+ const init = this._initialColumns[i];
12699
+ return init && init.key === c.key && !!init.hidden === !!c.hidden;
12700
+ });
12701
+ }
12702
+ /** True when the column is hidden in the originally declared defaults. */
12703
+ isDefaultHidden(col) {
12704
+ const init = this._initialColumns.find((c) => c.key === col.key);
12705
+ return !!init?.hidden;
12706
+ }
12707
+ toggleColumnChooser(event) {
12708
+ event.stopPropagation();
12709
+ this.showColumnChooser = !this.showColumnChooser;
12710
+ if (this.showColumnChooser) {
12711
+ this.svc.notifyMenuOpened(this);
12712
+ }
12713
+ }
12714
+ closeColumnChooser() {
12715
+ this.showColumnChooser = false;
12716
+ }
12717
+ toggleColumnVisible(col) {
12718
+ if (col.hideable === false) {
12719
+ return;
12720
+ }
12721
+ const target = this.allColumns.find((c) => c.key === col.key);
12722
+ if (!target) {
12723
+ return;
12724
+ }
12725
+ target.hidden = !target.hidden;
12726
+ this.updateVisible();
12727
+ this.svc.saveState(this.allColumns);
12728
+ }
12729
+ chooserReorder(event) {
12730
+ if (event.previousIndex === event.currentIndex) {
12731
+ return;
12732
+ }
12733
+ moveItemInArray(this.allColumns, event.previousIndex, event.currentIndex);
12734
+ this.updateVisible();
12735
+ this.svc.saveState(this.allColumns);
12736
+ }
12737
+ // ---------------------------------------------------------------- resize
12738
+ startResize(col, event) {
12739
+ if (col.resizable === false) {
12740
+ return;
12741
+ }
12742
+ event.preventDefault();
12743
+ event.stopPropagation();
12744
+ const th = event.target?.closest('th');
12745
+ const startWidth = col.width ?? th?.offsetWidth ?? 150;
12746
+ this.resizing = { col, startX: event.clientX, startWidth };
12747
+ document.addEventListener('pointermove', this.onPointerMove);
12748
+ document.addEventListener('pointerup', this.onPointerUp);
12749
+ }
12750
+ handleResizeMove(event) {
12751
+ if (!this.resizing) {
12752
+ return;
12753
+ }
12754
+ const delta = event.clientX - this.resizing.startX;
12755
+ const min = this.resizing.col.minWidth ?? 40;
12756
+ this.resizing.col.width = Math.max(min, Math.round(this.resizing.startWidth + delta));
12757
+ }
12758
+ handleResizeEnd() {
12759
+ if (this.resizing) {
12760
+ this.svc.saveWidth(this.resizing.col.key, this.resizing.col.width, this.allColumns);
12761
+ }
12762
+ this.resizing = undefined;
12763
+ this.detachResizeListeners();
12764
+ }
12765
+ detachResizeListeners() {
12766
+ document.removeEventListener('pointermove', this.onPointerMove);
12767
+ document.removeEventListener('pointerup', this.onPointerUp);
12768
+ }
12769
+ openHeaderActionMenu(event) {
12770
+ this.menuLeft = event.clientX - 150;
12771
+ this.menuTop = event.clientY + 5;
12772
+ _.delay(() => {
12773
+ this.openHeaderMenu = true;
12774
+ }, 100);
12775
+ }
12776
+ hideMenuItem(menuItem, selectedItem) {
12777
+ if (_.isFunction(menuItem.hidden)) {
12778
+ return menuItem.hidden(selectedItem);
12779
+ }
12780
+ else if (_.isBoolean(menuItem.hidden)) {
12781
+ return menuItem.hidden;
12782
+ }
12783
+ return !selectedItem.actionList?.includes(menuItem.action);
12784
+ }
12785
+ /** Header action menu rows: the reset action followed by host-provided header actions.
12786
+ * A host action's `icon` (SVG markup) is rendered before its label, like the row menu. */
12787
+ get headerMenuItems() {
12788
+ const items = [
12789
+ { label: 'Restore Default Table', action: '__reset_table__', id: 'ResetTableButton', iconSvg: this.safeIcon(this.RESET_TABLE_ICON) },
12790
+ ];
12791
+ _.forEach(this.headerActions, (ha) => {
12792
+ items.push({
12793
+ label: ha.name || ha.label,
12794
+ action: ha.action,
12795
+ id: 'HeaderAction_' + ha.action,
12796
+ iconSvg: ha.icon ? this.safeIcon(ha.icon) : undefined,
12797
+ data: ha,
12798
+ });
12799
+ });
12800
+ return items;
12801
+ }
12802
+ onHeaderMenuClick(evt) {
12803
+ if (evt.item.action === '__reset_table__') {
12804
+ this.resetTableColumns();
12805
+ }
12806
+ else {
12807
+ this.actionRequested.emit({ action: evt.item.action });
12808
+ }
12809
+ this.closeHeaderActionMenu();
12810
+ }
12811
+ /** Row action menu rows, built from `menuItems` against the currently selected row.
12812
+ * A menu item's `icon` (SVG markup) is rendered before its label in the menu. */
12813
+ get rowMenuItems() {
12814
+ return _.map(this.menuItems, (mi) => ({
12815
+ label: mi.name || mi.label,
12816
+ action: mi.action,
12817
+ id: 'TableActionButton_' + mi.action,
12818
+ hidden: this.hideMenuItem(mi, this.selectedItem),
12819
+ iconSvg: mi.icon ? this.safeIcon(mi.icon) : undefined,
12820
+ data: mi,
12821
+ }));
12822
+ }
12823
+ onRowMenuClick(evt) {
12824
+ this.actionRequested.emit({ action: evt.item.action, item: this.selectedItem });
12825
+ this.closeActionMenu();
12826
+ }
12827
+ openCreate() {
12828
+ this.actionRequested.emit({ action: 'create' });
12829
+ }
12830
+ // ---------------------------------------------------------------- toolbar search + pagination
12831
+ onSearchInput() {
12832
+ this.inputChangedDebounced();
12833
+ }
12834
+ applySearch() {
12835
+ this.tableFilterService.currentPage = 1;
12836
+ this.tableFilterService.updateFilter({
12837
+ filterName: this.filterName,
12838
+ columnId: this.filterColumn,
12839
+ value: this.searchString,
12840
+ label: this.searchString,
12841
+ });
12842
+ }
12843
+ nextPage() {
12844
+ if (this.nextDisabled) {
12845
+ return;
12846
+ }
12847
+ this.tableFilterService.changePage((this.tableFilterService.currentPage || 1) + 1);
12848
+ }
12849
+ prevPage() {
12850
+ if (this.prevDisabled) {
12851
+ return;
12852
+ }
12853
+ this.tableFilterService.changePage((this.tableFilterService.currentPage || 1) - 1);
12854
+ }
12855
+ get nextDisabled() {
12856
+ if (!_.isNumber(this.totalCount) || !_.isNumber(this.endCount) || this.filtering) {
12857
+ return true;
12858
+ }
12859
+ return Number(this.endCount) >= Number(this.totalCount);
12860
+ }
12861
+ get prevDisabled() {
12862
+ if (!_.isNumber(this.startCount) || !_.isNumber(this.totalCount) || this.filtering) {
12863
+ return true;
12864
+ }
12865
+ return Number(this.startCount) <= 1;
12866
+ }
12867
+ get pageSize() {
12868
+ return this.tableFilterService.pageSize;
12869
+ }
12870
+ get currentPageNum() {
12871
+ return this.tableFilterService.currentPage || 1;
12872
+ }
12873
+ get totalPages() {
12874
+ if (!_.isNumber(this.totalCount) || Number(this.totalCount) <= 0) {
12875
+ return 1;
12876
+ }
12877
+ return Math.max(1, Math.ceil(Number(this.totalCount) / this.pageSize));
12878
+ }
12879
+ /** Page numbers to render, with `0` marking a gap ("..."). Shows the first and
12880
+ * last page plus a window around the current page so long lists stay compact. */
12881
+ get pageWindow() {
12882
+ const total = this.totalPages;
12883
+ const cur = this.currentPageNum;
12884
+ if (total <= 7) {
12885
+ return Array.from({ length: total }, (_v, i) => i + 1);
12886
+ }
12887
+ const pages = new Set([1, total, cur, cur - 1, cur + 1]);
12888
+ const sorted = [...pages].filter((p) => p >= 1 && p <= total).sort((a, b) => a - b);
12889
+ const out = [];
12890
+ let prev = 0;
12891
+ for (const p of sorted) {
12892
+ if (prev && p - prev > 1) {
12893
+ out.push(0); // gap sentinel
12894
+ }
12895
+ out.push(p);
12896
+ prev = p;
12897
+ }
12898
+ return out;
12899
+ }
12900
+ goToPage(page) {
12901
+ if (page < 1 || page > this.totalPages || page === this.currentPageNum || this.filtering) {
12902
+ return;
12903
+ }
12904
+ this.tableFilterService.changePage(page);
12905
+ }
12906
+ onPageSizeChange(size) {
12907
+ if (!size || size === this.pageSize) {
12908
+ return;
12909
+ }
12910
+ this.svc.savePageSize(size);
12911
+ this.tableFilterService.changePageSize(size);
12912
+ }
12913
+ /** Re-fetch the current page (re-emits the page so the host reloads its data). */
12914
+ refresh() {
12915
+ if (this.filtering) {
12916
+ return;
12917
+ }
12918
+ this.tableFilterService.changePage(this.currentPageNum);
12919
+ }
12920
+ toggleAutoRefresh() {
12921
+ this.autoRefresh = !this.autoRefresh;
12922
+ if (this.autoRefresh) {
12923
+ this.autoRefreshTimer = setInterval(() => this.ngZone.run(() => this.refresh()), this.autoRefreshIntervalMs);
12924
+ }
12925
+ else if (this.autoRefreshTimer) {
12926
+ clearInterval(this.autoRefreshTimer);
12927
+ this.autoRefreshTimer = undefined;
12928
+ }
12929
+ }
12930
+ safeIcon(svg) {
12931
+ let safe = this.iconCache.get(svg);
12932
+ if (!safe) {
12933
+ safe = this.sanitizer.bypassSecurityTrustHtml(svg);
12934
+ this.iconCache.set(svg, safe);
12935
+ }
12936
+ return safe;
12937
+ }
12938
+ updateEntityTypeLabel() {
12939
+ const map = {
12940
+ 'api-sessions': 'API Sessions',
12941
+ 'auth-policies': 'Auth Policies',
12942
+ 'certificate-authorities': 'Certificate Authorities',
12943
+ 'ext-jwt-signers': 'External JWT Signers',
12944
+ identities: 'Identities',
12945
+ 'posture-checks': 'Posture Checks',
12946
+ 'edge-routers': 'Edge Routers',
12947
+ services: 'Services',
12948
+ configurations: 'Configs',
12949
+ 'config-types': 'Config Types',
12950
+ 'service-policies': 'Service Policies',
12951
+ 'edge-router-policies': 'Edge Router Policies',
12952
+ 'service-edge-router-policies': 'Service Edge Router Policies',
12953
+ sessions: 'Sessions',
12954
+ terminators: 'Terminators',
12955
+ 'transit-routers': 'Transit Routers',
12956
+ };
12957
+ this.entityTypeLabel = map[this._tableId] || this.entityTypeName || '';
12958
+ }
12959
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableComponent, deps: [{ token: ListTableService }, { token: DataTableFilterService }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
12960
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ListTableComponent, isStandalone: false, selector: "lib-list-table", inputs: { tableId: "tableId", columns: "columns", rowData: "rowData", isLoading: "isLoading", options: "options", startCount: "startCount", endCount: "endCount", totalCount: "totalCount", filterApplied: "filterApplied", menuItems: "menuItems", headerActions: "headerActions", noItemsImage: "noItemsImage", showNoItemsAdd: "showNoItemsAdd", noItemsReadonly: "noItemsReadonly", accessDenied: "accessDenied", entityTypeName: "entityTypeName", filterName: "filterName", filterColumn: "filterColumn", initialPageSize: "initialPageSize" }, outputs: { actionRequested: "actionRequested", tableReady: "tableReady" }, providers: [ListTableService], viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true }, { propertyName: "filterZone", first: true, predicate: ["filterZone"], descendants: true }, { propertyName: "filtersRow", first: true, predicate: ["filtersRow"], descendants: true }], ngImport: i0, template: "@if (showTable && !accessDenied) {\n <div class=\"lt-container\">\n <div class=\"lt-toolbar\">\n <div class=\"lt-toolbar-left\">\n <div class=\"lt-search\">\n <svg class=\"lt-search-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"lt-search-input\"\n id=\"SearchFilter\"\n type=\"text\"\n [placeholder]=\"'Search ' + (entityTypeLabel || 'items')\"\n [(ngModel)]=\"searchString\"\n (keyup)=\"onSearchInput()\"\n />\n </div>\n @if (filterableColumns.length) {\n <div class=\"lt-filter-zone\" #filterZone>\n <div class=\"lt-filters\" #filtersRow>\n @for (col of visibleFilterColumns; track col.key) {\n <span class=\"lt-filter-item\" [attr.data-filter-key]=\"col.key\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </span>\n }\n </div>\n @if (overflowFilterColumns.length) {\n <div class=\"lt-filter-more\">\n <button\n type=\"button\"\n class=\"lt-filter-more-btn\"\n [class.is-on]=\"hiddenActiveCount > 0\"\n [class.is-open]=\"showFilterOverflow\"\n (click)=\"toggleFilterOverflow($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showFilterOverflow\"\n [attr.aria-label]=\"overflowFilterColumns.length + ' more filters'\"\n id=\"FilterOverflowButton\"\n >\n <span class=\"lt-filter-more-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"><path fill=\"currentColor\" d=\"M2 3h12l-4.6 5.6v4l-2.8 1.4v-5.4z\"/></svg>\n </span>\n <span class=\"lt-filter-more-label\">More</span>\n <span class=\"lt-filter-more-count\">{{ overflowFilterColumns.length }}</span>\n </button>\n @if (showFilterOverflow) {\n <div class=\"lt-filter-overflow\" (clickOutside)=\"closeFilterOverflow($event)\">\n <div class=\"lt-filter-overflow-title\">Filters</div>\n @for (col of overflowFilterColumns; track col.key) {\n <div class=\"lt-filter-overflow-row\" [class.is-active]=\"isFilterActive(col)\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <div class=\"lt-toolbar-right\">\n <ng-template #refreshIcon>\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" d=\"M13.4 8a5.4 5.4 0 1 1-1.5-3.8\"/>\n <path fill=\"currentColor\" d=\"M13.9 2.1l.5 3.2-3.2-.5z\"/>\n </svg>\n </ng-template>\n <button\n type=\"button\"\n class=\"lt-refresh-btn\"\n (click)=\"refresh()\"\n [disabled]=\"filtering\"\n aria-label=\"Refresh\"\n title=\"Refresh\"\n id=\"RefreshTableButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n </button>\n <button\n type=\"button\"\n class=\"lt-autorefresh-btn\"\n [class.is-on]=\"autoRefresh\"\n (click)=\"toggleAutoRefresh()\"\n [attr.aria-pressed]=\"autoRefresh\"\n id=\"AutoRefreshButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n <span>Auto-refresh: {{ autoRefresh ? 'On' : 'Off' }}</span>\n </button>\n <div class=\"lt-columns\">\n <button\n type=\"button\"\n class=\"lt-columns-btn\"\n [class.is-on]=\"hiddenColumnCount > 0\"\n [class.is-open]=\"showColumnChooser\"\n (click)=\"toggleColumnChooser($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showColumnChooser\"\n id=\"ColumnChooserButton\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M2 3.5h9v1.4H2zM2 7.3h6v1.4H2zM2 11.1h11v1.4H2z\"/>\n <circle cx=\"13\" cy=\"4.2\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"10\" cy=\"8\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"14.4\" cy=\"11.8\" r=\"1.7\" fill=\"currentColor\"/>\n </svg>\n <span>Columns</span>\n @if (hiddenColumnCount > 0) {\n <span class=\"lt-columns-count\">{{ hiddenColumnCount }}</span>\n }\n </button>\n @if (showColumnChooser) {\n <div class=\"lt-column-chooser\" (clickOutside)=\"closeColumnChooser()\">\n <div class=\"lt-chooser-header\">\n <span class=\"lt-chooser-title\">Columns</span>\n <span class=\"lt-chooser-subtitle\">Customize order and visibility</span>\n </div>\n <ul class=\"lt-chooser-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"chooserReorder($event)\">\n @for (col of allColumns; track col.key) {\n <li class=\"lt-chooser-row\" cdkDrag cdkDragLockAxis=\"y\" [cdkDragDisabled]=\"col.reorderable === false\">\n <button\n type=\"button\"\n class=\"lt-chooser-toggle\"\n [class.checked]=\"!col.hidden\"\n [disabled]=\"col.hideable === false\"\n (click)=\"toggleColumnVisible(col)\"\n [attr.aria-pressed]=\"!col.hidden\"\n >\n <span class=\"lt-chooser-box\" aria-hidden=\"true\">\n @if (!col.hidden) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/>\n </svg>\n }\n </span>\n <span class=\"lt-chooser-text\">{{ col.label }}</span>\n @if (isDefaultHidden(col)) {\n <span class=\"lt-chooser-default-tag\">hidden by default</span>\n }\n </button>\n @if (col.reorderable !== false) {\n <span class=\"lt-chooser-handle\" cdkDragHandle aria-label=\"Drag to reorder\" title=\"Drag to reorder\">\n <svg viewBox=\"0 0 10 16\" width=\"10\" height=\"15\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"3\" cy=\"3\" r=\"1.25\"/><circle cx=\"7\" cy=\"3\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"8\" r=\"1.25\"/><circle cx=\"7\" cy=\"8\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"13\" r=\"1.25\"/><circle cx=\"7\" cy=\"13\" r=\"1.25\"/>\n </svg>\n </span>\n }\n </li>\n }\n </ul>\n <div class=\"lt-chooser-footer\">\n <button\n type=\"button\"\n class=\"lt-chooser-reset\"\n [disabled]=\"columnsAtDefault\"\n (click)=\"resetTableColumns()\"\n id=\"ResetColumnsButton\"\n >\n Reset to default\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"lt-scroll\" id=\"ItemTable\">\n <table class=\"lt-table\">\n <colgroup>\n @if (!options.noSelect) { <col class=\"lt-col-select\" /> }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <col [style.width]=\"colWidth(col)\" />\n }\n @if (!options.noMenu) { <col class=\"lt-col-menu\" /> }\n </colgroup>\n <thead class=\"lt-head\">\n <tr cdkDropList cdkDropListOrientation=\"horizontal\" (cdkDropListDropped)=\"onReorder($event)\">\n @if (!options.noSelect) {\n <th class=\"lt-select-cell\">\n <div\n class=\"lt-select-all\"\n [class.checked]=\"allToggled\"\n [class.indeterminate]=\"someSelected\"\n (click)=\"toggleAll()\"\n id=\"SelectAllRows\"\n ></div>\n </th>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <th\n cdkDrag\n [cdkDragDisabled]=\"col.reorderable === false\"\n class=\"lt-th\"\n [ngClass]=\"col.cellClass\"\n id=\"Header_{{ col.key }}\"\n >\n <lib-list-header-cell\n [column]=\"col\"\n [sortDir]=\"col.sortDir\"\n (sortChanged)=\"onHeaderSort($event)\"\n (hideColumn)=\"hideColumn($event)\"\n ></lib-list-header-cell>\n @if (col.resizable !== false) {\n <span class=\"lt-resize\" (pointerdown)=\"startResize(col, $event)\"></span>\n }\n </th>\n }\n @if (!options.noMenu) {\n <th class=\"lt-menu-cell\">\n <div class=\"lt-header-menu-trigger\" (click)=\"openHeaderActionMenu($event)\" id=\"ColumnMenuHeader\">\n &#8942;\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rowData; track trackRow($index, row); let i = $index) {\n <tr\n class=\"lt-row\"\n [class.lt-row-odd]=\"i % 2 === 1\"\n [class.lt-row-selected]=\"row.selected\"\n [class.row-invalid]=\"row.invalid\"\n [class.row-copied]=\"row.copied\"\n >\n @if (!options.noSelect) {\n <td class=\"lt-select-cell\">\n <lib-select-cell [row]=\"row\" [toggleItem]=\"toggleItem\"></lib-select-cell>\n </td>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <td\n class=\"lt-cell\"\n [ngClass]=\"cellClass(col)\"\n (click)=\"onCellClick(col, row, i)\"\n (mouseenter)=\"onCellHover(col, row, i, $event)\"\n (mouseleave)=\"onCellLeave()\"\n >\n @switch (renderMode(col)) {\n @case ('component') {\n <ng-container *ngComponentOutlet=\"col.cell; inputs: cellInputs(col, row, i)\"></ng-container>\n }\n @case ('html') {\n <span class=\"lt-cell-html\" [innerHTML]=\"cellHtml(col, row, i)\"></span>\n }\n @default {\n <span class=\"lt-cell-text\">{{ cellText(col, row, i) }}</span>\n }\n }\n </td>\n }\n @if (!options.noMenu) {\n <td class=\"lt-menu-cell\">\n @if (row.actionList?.length) {\n <button\n type=\"button\"\n class=\"lt-row-menu-trigger\"\n (click)=\"$event.stopPropagation(); openActionMenu($event, row)\"\n attr.id=\"RowMenu_{{ row.id }}\"\n aria-label=\"Row actions\"\n >&#8942;</button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n @if (!isLoading && filterApplied && (!rowData || rowData.length === 0)) {\n <div class=\"no-rows-message\">No Rows To Show</div>\n }\n </div>\n\n <div class=\"lt-footer\">\n <div class=\"lt-footer-count\">\n Showing <span id=\"Start\">{{ startCount }}</span>&#8211;<span id=\"End\">{{ endCount }}</span>\n of <span id=\"Total\">{{ totalCount }}</span>\n </div>\n <div class=\"lt-footer-controls\">\n <div class=\"lt-rpp\">\n <span class=\"lt-rpp-label\">Rows per page</span>\n <select\n class=\"lt-rpp-select\"\n [disabled]=\"filtering\"\n [value]=\"pageSize\"\n (change)=\"onPageSizeChange(+$any($event.target).value)\"\n aria-label=\"Rows per page\"\n >\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n <div class=\"lt-pager\">\n <button type=\"button\" class=\"lt-page-btn icon-prev\" [disabled]=\"prevDisabled\" (click)=\"prevPage()\" aria-label=\"Previous page\"></button>\n @for (p of pageWindow; track $index) {\n @if (p === 0) {\n <span class=\"lt-page-gap\">&#8230;</span>\n } @else {\n <button\n type=\"button\"\n class=\"lt-page-num\"\n [class.is-current]=\"p === currentPageNum\"\n (click)=\"goToPage(p)\"\n >{{ p }}</button>\n }\n }\n <button type=\"button\" class=\"lt-page-btn icon-next\" [disabled]=\"nextDisabled\" (click)=\"nextPage()\" aria-label=\"Next page\"></button>\n </div>\n </div>\n </div>\n\n <!-- header action menu -->\n <lib-list-menu\n menuId=\"HeaderActionMenu\"\n [open]=\"openHeaderMenu\"\n [items]=\"headerMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onHeaderMenuClick($event)\"\n (closed)=\"closeHeaderActionMenu()\"\n ></lib-list-menu>\n\n <!-- row action menu -->\n <lib-list-menu\n menuId=\"RowActionMenu\"\n [open]=\"openMenu\"\n [items]=\"rowMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onRowMenuClick($event)\"\n (closed)=\"closeActionMenu()\"\n ></lib-list-menu>\n </div>\n} @else {\n <lib-no-items\n (clickEmit)=\"openCreate()\"\n [image]=\"noItemsImage\"\n [isEmpty]=\"!rowData || rowData.length === 0\"\n [isLoading]=\"isLoading\"\n [typeName]=\"entityTypeLabel\"\n [hasAdd]=\"showNoItemsAdd\"\n [readonly]=\"noItemsReadonly\"\n [accessDenied]=\"accessDenied\"\n ></lib-no-items>\n}\n\n@if (hoverTooltip) {\n <div class=\"lt-tooltip\" [ngStyle]=\"{ left: hoverTooltip.left + 'px', top: hoverTooltip.top + 'px' }\">\n <ng-container *ngComponentOutlet=\"hoverTooltip.component; inputs: hoverTooltip.inputs\"></ng-container>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}.lt-columns-btn,.lt-autorefresh-btn,.lt-refresh-btn,.lt-filter-more-btn{display:inline-flex;align-items:center;gap:.4375rem;height:var(--lt-toolbar-height);padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.lt-columns-btn svg,.lt-autorefresh-btn svg,.lt-refresh-btn svg,.lt-filter-more-btn svg{display:block}.lt-columns-btn:hover,.lt-autorefresh-btn:hover,.lt-refresh-btn:hover,.lt-filter-more-btn:hover,.is-open.lt-columns-btn,.is-open.lt-autorefresh-btn,.is-open.lt-refresh-btn,.is-open.lt-filter-more-btn,.is-on.lt-columns-btn,.is-on.lt-autorefresh-btn,.is-on.lt-refresh-btn,.is-on.lt-filter-more-btn{border-color:var(--lt-link);color:var(--lt-link)}.lt-columns-count,.lt-filter-more-count{display:inline-flex;align-items:center;justify-content:center;min-width:1.125rem;height:1.125rem;padding:0 .3125rem;font-size:.6875rem;font-weight:700;color:var(--lt-control-bg);background:var(--lt-link);border-radius:.5625rem}.lt-container{display:flex;position:relative;height:calc(100% - var(--lt-container-reserve, 5.625rem));width:100%;flex-direction:column}.lt-scroll{position:relative;z-index:1;width:100%;flex:0 1 auto;min-height:0;overflow:auto;background-color:var(--lt-bg)}.lt-scroll .no-rows-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--lt-cell-text);font-size:.875rem;pointer-events:none}.lt-table{width:100%;border-collapse:collapse;table-layout:fixed;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text)}.lt-head{position:sticky;top:0;z-index:10}.lt-head th{position:relative;box-sizing:border-box;height:2.75rem;padding:0 .3125rem 0 .625rem;text-align:left;vertical-align:middle;background-color:var(--lt-header-bg);border-bottom:1px solid var(--lt-header-underline);white-space:nowrap;-webkit-user-select:none;user-select:none}.lt-head th.lt-th{border-right:1px solid var(--lt-border)}.lt-head .lt-select-cell,.lt-head .lt-menu-cell{width:3.75rem;padding:0}.lt-resize{position:absolute;right:0;top:50%;transform:translateY(-50%);width:.625rem;height:.875rem;cursor:col-resize}.lt-resize:before,.lt-resize:after{content:\"\";position:absolute;top:0;height:100%;width:1px;background:transparent;transition:background .15s}.lt-resize:before{right:.25rem}.lt-resize:after{right:.125rem}.lt-resize:hover:before,.lt-resize:hover:after{background:var(--lt-icon)}.lt-head th:hover .lt-resize:before,.lt-head th:hover .lt-resize:after{background:var(--lt-border)}.lt-select-all{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-control-border, var(--lt-border));border-radius:.25rem;background:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;cursor:pointer;transition:border-color .12s,background-color .12s}.lt-select-all:hover{border-color:var(--lt-link)}.lt-select-all.checked{background-color:var(--lt-link);border-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.lt-select-all.indeterminate{background-color:var(--lt-link);border-color:var(--lt-link);background-image:linear-gradient(#fff,#fff);background-size:.5rem .125rem}.lt-header-menu-trigger{display:flex;align-items:center;justify-content:center;height:100%;font-size:1.25rem;color:var(--lt-icon);cursor:pointer}.lt-header-menu-trigger:hover{color:var(--lt-icon-active)}.lt-row-menu-trigger{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;font-size:1.25rem;line-height:1;color:var(--lt-icon);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;transition:color .15s,background .15s}.lt-row-menu-trigger:hover{color:var(--lt-icon-active);background:var(--lt-row-hover)}.cdk-drag-preview{box-sizing:border-box;background-color:var(--lt-header-bg);color:var(--lt-header-text);box-shadow:var(--shadowLarge);padding:0 .625rem;display:flex;align-items:center;border-radius:var(--lt-radius)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.lt-row{height:var(--lt-row-height);background-color:var(--lt-row-bg);color:var(--lt-cell-text)}.lt-row td{border-bottom:1px solid var(--lt-border)}.lt-row.lt-row-odd{background-color:var(--lt-row-alt-bg)}.lt-row:hover{background-color:var(--lt-row-hover)}.lt-row.lt-row-selected{background-color:var(--lt-row-selected)}.lt-row.lt-row-selected:hover{background-color:var(--lt-row-hover)}.lt-row.row-invalid{background-color:var(--invalid)!important}.lt-row.row-copied{background-color:var(--copiedRow)!important}.lt-cell{height:var(--lt-row-height);padding:0 .625rem 0 1.25rem;text-align:left;vertical-align:middle;border:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-user-select:text;user-select:text}.lt-cell-text{display:inline-block;overflow:hidden;text-overflow:ellipsis;max-width:100%;vertical-align:middle}.lt-cell.lt-cell-center{text-align:center}.lt-badge-cell .lt-cell-text{padding:.1875rem .5rem;font-size:.75rem;font-weight:600;line-height:1.2;color:var(--lt-cell-text);background:color-mix(in srgb,var(--lt-border) 45%,transparent);border:1px solid var(--lt-border);border-radius:999px}.lt-badge-cell .lt-cell-text:empty{display:none}.lt-select-cell,.lt-menu-cell{width:3.75rem;padding:0;text-align:center;vertical-align:middle;overflow:visible;position:relative}.lt-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin:var(--lt-toolbar-margin-y, 0) 0;padding:0 0 var(--lt-toolbar-pad-bottom, .625rem);min-height:var(--lt-toolbar-height);position:relative;z-index:30}.lt-toolbar-left{display:flex;align-items:center;gap:.5rem;flex:1 1 auto;min-width:0;flex-wrap:nowrap}.lt-filter-zone{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;flex:0 1 auto;min-width:0}.lt-filters{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;min-width:0;flex:0 1 auto;overflow:hidden}.lt-filter-item{display:inline-flex;flex:0 0 auto}.lt-filter-more{position:relative;flex:0 0 auto}.lt-filter-more-btn{gap:.375rem;padding:0 .625rem}.lt-filter-more-btn .lt-filter-more-icon{display:inline-flex}.lt-filter-overflow{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;min-width:15rem;max-width:min(22rem,90vw);display:flex;flex-direction:column;gap:.5rem;padding:.625rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:lt-chooser-in .12s ease-out}.lt-filter-overflow-title{font-family:var(--lt-font);font-size:.6875rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--groupText)}.lt-filter-overflow-row{display:flex}.lt-filter-overflow-row lib-list-filter-cell,.lt-filter-overflow-row .lt-filter-item{flex:1 1 auto;min-width:0}.lt-toolbar-right{display:flex;align-items:center;gap:.75rem;flex:0 0 auto}.lt-divider{flex:0 0 auto;width:1px;height:1.5rem;background:var(--lt-border)}.lt-search{position:relative;display:flex;align-items:center;width:18rem;max-width:40vw;flex:0 1 18rem}.lt-search-icon{position:absolute;left:.625rem;display:block;color:var(--lt-icon);pointer-events:none}.lt-search-input{width:100%;height:var(--lt-toolbar-height);padding:0 .75rem 0 1.75rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);outline:none;transition:border-color .15s,box-shadow .15s}.lt-search-input::placeholder{color:var(--placeholder)}.lt-search-input:focus{border-color:var(--lt-link);box-shadow:0 0 0 .1875rem color-mix(in srgb,var(--lt-link) 18%,transparent)}.lt-count{font-size:.8125rem;color:var(--groupText);white-space:nowrap}.lt-count .lt-count-of{color:var(--groupText)}.lt-pager{display:flex;align-items:center;gap:.25rem}.lt-page-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);color:var(--lt-icon);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;font-size:.9375rem;transition:border-color .15s,color .15s}.lt-page-btn:not(:disabled):hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-btn:disabled{opacity:.4;cursor:default}.lt-refresh-btn{padding:0;width:var(--lt-toolbar-height);justify-content:center}.lt-refresh-btn:disabled{opacity:.5;cursor:default;border-color:var(--lt-border);color:var(--lt-cell-text)}.lt-columns{position:relative;flex:0 0 auto}.lt-column-chooser{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;width:15rem;max-height:calc(100vh - 9rem);display:flex;flex-direction:column;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);overflow:hidden;animation:lt-chooser-in .12s ease-out}@keyframes lt-chooser-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-chooser-header{flex:0 0 auto;display:flex;flex-direction:column;padding:.625rem .75rem .5rem;border-bottom:1px solid var(--lt-border)}.lt-chooser-title{font-size:.8125rem;font-weight:700;color:var(--lt-cell-text)}.lt-chooser-subtitle{font-size:.6875rem;color:var(--groupText)}.lt-chooser-list{flex:1 1 auto;list-style:none;margin:0;padding:.375rem .5rem;min-height:0;overflow-y:auto}.lt-chooser-row{display:flex;align-items:center;justify-content:space-between;min-height:2.5rem;padding:.5rem;border-radius:var(--lt-radius);background:var(--lt-control-bg)}.lt-chooser-row:hover{background:var(--lt-row-hover)}.lt-chooser-row:hover .lt-chooser-handle{opacity:1}.lt-chooser-toggle{display:flex;align-items:center;gap:.625rem;flex:1 1 auto;min-width:0;padding:0;background:transparent;border:none;font:inherit;font-size:.875rem;color:var(--lt-cell-text);text-align:left;cursor:pointer}.lt-chooser-toggle:disabled{cursor:default;opacity:.5}.lt-chooser-box{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:1rem;height:1rem;border:.09375rem solid var(--lt-control-border, var(--lt-border));border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg);transition:background .12s,border-color .12s}.lt-chooser-toggle.checked .lt-chooser-box{background:var(--lt-link);border-color:var(--lt-link)}.lt-chooser-toggle:not(:disabled):hover .lt-chooser-box{border-color:var(--lt-link)}.lt-chooser-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lt-chooser-default-tag{flex:0 0 auto;margin-left:.375rem;font-size:.6875rem;font-weight:400;font-style:italic;color:var(--groupText);white-space:nowrap}.lt-chooser-handle{flex:0 0 auto;display:inline-flex;align-items:center;color:var(--lt-icon);cursor:grab;padding-left:.5rem;-webkit-user-select:none;user-select:none}.lt-chooser-handle:active{cursor:grabbing}.lt-chooser-footer{flex:0 0 auto;padding:.5rem .75rem;border-top:1px solid var(--lt-border)}.lt-chooser-reset{width:100%;height:1.875rem;font-size:.75rem;font-weight:600;color:var(--lt-link);background:transparent;border:1px solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer}.lt-chooser-reset:disabled{color:var(--groupText);cursor:default;opacity:.6}.lt-chooser-reset:not(:disabled):hover{border-color:var(--lt-link)}.lt-footer{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;padding:.625rem .125rem 0;font-family:var(--lt-font);font-size:.8125rem;color:var(--groupText)}.lt-footer-count{white-space:nowrap}.lt-footer-count span{color:var(--lt-cell-text);font-weight:600}.lt-footer-controls{display:flex;align-items:center;gap:1.25rem;flex-wrap:nowrap}.lt-rpp{display:inline-flex;align-items:center;gap:.5rem;white-space:nowrap}.lt-rpp-label{text-transform:none;font-weight:400;color:var(--groupText)}.lt-rpp-select{width:auto;min-width:4rem;height:var(--lt-toolbar-height);padding:0 1.75rem 0 .625rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background-color:var(--lt-control-bg);background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right .5rem center;border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);appearance:none;-webkit-appearance:none;cursor:pointer}.lt-rpp-select:focus{outline:none;border-color:var(--lt-link)}.lt-page-num{display:inline-flex;align-items:center;justify-content:center;min-width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);padding:0 .5rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;transition:border-color .15s,color .15s,background .15s}.lt-page-num:hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-num.is-current{color:#fff;background:var(--lt-link);border-color:var(--lt-link);cursor:default}.lt-page-gap{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;color:var(--groupText)}.lt-tooltip{position:fixed;z-index:1100;pointer-events:none;animation:lt-tooltip-in .12s ease-out}@keyframes lt-tooltip-in{0%{opacity:0;transform:translateY(-.1875rem)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i6$1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i6$1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i6$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }, { kind: "component", type: ListMenuComponent, selector: "lib-list-menu", inputs: ["open", "items", "left", "top", "menuId"], outputs: ["itemClick", "closed"] }, { kind: "component", type: SelectCellComponent, selector: "lib-select-cell", inputs: ["row", "toggleItem"] }, { kind: "component", type: ListHeaderCellComponent, selector: "lib-list-header-cell", inputs: ["column", "sortDir"], outputs: ["sortChanged", "hideColumn"] }, { kind: "component", type: ListFilterCellComponent, selector: "lib-list-filter-cell", inputs: ["column"] }, { kind: "component", type: NoItemsComponent, selector: "lib-no-items", inputs: ["image", "typeName", "isEmpty", "hasAdd", "hiddenResults", "isLoading", "readonly", "accessDenied"], outputs: ["clickEmit", "refresh"] }] }); }
12961
+ }
12962
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ListTableComponent, decorators: [{
12963
+ type: Component,
12964
+ args: [{ selector: 'lib-list-table', standalone: false, providers: [ListTableService], template: "@if (showTable && !accessDenied) {\n <div class=\"lt-container\">\n <div class=\"lt-toolbar\">\n <div class=\"lt-toolbar-left\">\n <div class=\"lt-search\">\n <svg class=\"lt-search-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <line x1=\"10.4\" y1=\"10.4\" x2=\"14.6\" y2=\"14.6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <input\n class=\"lt-search-input\"\n id=\"SearchFilter\"\n type=\"text\"\n [placeholder]=\"'Search ' + (entityTypeLabel || 'items')\"\n [(ngModel)]=\"searchString\"\n (keyup)=\"onSearchInput()\"\n />\n </div>\n @if (filterableColumns.length) {\n <div class=\"lt-filter-zone\" #filterZone>\n <div class=\"lt-filters\" #filtersRow>\n @for (col of visibleFilterColumns; track col.key) {\n <span class=\"lt-filter-item\" [attr.data-filter-key]=\"col.key\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </span>\n }\n </div>\n @if (overflowFilterColumns.length) {\n <div class=\"lt-filter-more\">\n <button\n type=\"button\"\n class=\"lt-filter-more-btn\"\n [class.is-on]=\"hiddenActiveCount > 0\"\n [class.is-open]=\"showFilterOverflow\"\n (click)=\"toggleFilterOverflow($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showFilterOverflow\"\n [attr.aria-label]=\"overflowFilterColumns.length + ' more filters'\"\n id=\"FilterOverflowButton\"\n >\n <span class=\"lt-filter-more-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"><path fill=\"currentColor\" d=\"M2 3h12l-4.6 5.6v4l-2.8 1.4v-5.4z\"/></svg>\n </span>\n <span class=\"lt-filter-more-label\">More</span>\n <span class=\"lt-filter-more-count\">{{ overflowFilterColumns.length }}</span>\n </button>\n @if (showFilterOverflow) {\n <div class=\"lt-filter-overflow\" (clickOutside)=\"closeFilterOverflow($event)\">\n <div class=\"lt-filter-overflow-title\">Filters</div>\n @for (col of overflowFilterColumns; track col.key) {\n <div class=\"lt-filter-overflow-row\" [class.is-active]=\"isFilterActive(col)\">\n <lib-list-filter-cell [column]=\"col\"></lib-list-filter-cell>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <div class=\"lt-toolbar-right\">\n <ng-template #refreshIcon>\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" d=\"M13.4 8a5.4 5.4 0 1 1-1.5-3.8\"/>\n <path fill=\"currentColor\" d=\"M13.9 2.1l.5 3.2-3.2-.5z\"/>\n </svg>\n </ng-template>\n <button\n type=\"button\"\n class=\"lt-refresh-btn\"\n (click)=\"refresh()\"\n [disabled]=\"filtering\"\n aria-label=\"Refresh\"\n title=\"Refresh\"\n id=\"RefreshTableButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n </button>\n <button\n type=\"button\"\n class=\"lt-autorefresh-btn\"\n [class.is-on]=\"autoRefresh\"\n (click)=\"toggleAutoRefresh()\"\n [attr.aria-pressed]=\"autoRefresh\"\n id=\"AutoRefreshButton\"\n >\n <ng-container [ngTemplateOutlet]=\"refreshIcon\"></ng-container>\n <span>Auto-refresh: {{ autoRefresh ? 'On' : 'Off' }}</span>\n </button>\n <div class=\"lt-columns\">\n <button\n type=\"button\"\n class=\"lt-columns-btn\"\n [class.is-on]=\"hiddenColumnCount > 0\"\n [class.is-open]=\"showColumnChooser\"\n (click)=\"toggleColumnChooser($event)\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"showColumnChooser\"\n id=\"ColumnChooserButton\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M2 3.5h9v1.4H2zM2 7.3h6v1.4H2zM2 11.1h11v1.4H2z\"/>\n <circle cx=\"13\" cy=\"4.2\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"10\" cy=\"8\" r=\"1.7\" fill=\"currentColor\"/>\n <circle cx=\"14.4\" cy=\"11.8\" r=\"1.7\" fill=\"currentColor\"/>\n </svg>\n <span>Columns</span>\n @if (hiddenColumnCount > 0) {\n <span class=\"lt-columns-count\">{{ hiddenColumnCount }}</span>\n }\n </button>\n @if (showColumnChooser) {\n <div class=\"lt-column-chooser\" (clickOutside)=\"closeColumnChooser()\">\n <div class=\"lt-chooser-header\">\n <span class=\"lt-chooser-title\">Columns</span>\n <span class=\"lt-chooser-subtitle\">Customize order and visibility</span>\n </div>\n <ul class=\"lt-chooser-list\" cdkDropList cdkDropListLockAxis=\"y\" (cdkDropListDropped)=\"chooserReorder($event)\">\n @for (col of allColumns; track col.key) {\n <li class=\"lt-chooser-row\" cdkDrag cdkDragLockAxis=\"y\" [cdkDragDisabled]=\"col.reorderable === false\">\n <button\n type=\"button\"\n class=\"lt-chooser-toggle\"\n [class.checked]=\"!col.hidden\"\n [disabled]=\"col.hideable === false\"\n (click)=\"toggleColumnVisible(col)\"\n [attr.aria-pressed]=\"!col.hidden\"\n >\n <span class=\"lt-chooser-box\" aria-hidden=\"true\">\n @if (!col.hidden) {\n <svg viewBox=\"0 0 12 12\" width=\"10\" height=\"10\">\n <path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 6.3 4.6 9 10 3\"/>\n </svg>\n }\n </span>\n <span class=\"lt-chooser-text\">{{ col.label }}</span>\n @if (isDefaultHidden(col)) {\n <span class=\"lt-chooser-default-tag\">hidden by default</span>\n }\n </button>\n @if (col.reorderable !== false) {\n <span class=\"lt-chooser-handle\" cdkDragHandle aria-label=\"Drag to reorder\" title=\"Drag to reorder\">\n <svg viewBox=\"0 0 10 16\" width=\"10\" height=\"15\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"3\" cy=\"3\" r=\"1.25\"/><circle cx=\"7\" cy=\"3\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"8\" r=\"1.25\"/><circle cx=\"7\" cy=\"8\" r=\"1.25\"/>\n <circle cx=\"3\" cy=\"13\" r=\"1.25\"/><circle cx=\"7\" cy=\"13\" r=\"1.25\"/>\n </svg>\n </span>\n }\n </li>\n }\n </ul>\n <div class=\"lt-chooser-footer\">\n <button\n type=\"button\"\n class=\"lt-chooser-reset\"\n [disabled]=\"columnsAtDefault\"\n (click)=\"resetTableColumns()\"\n id=\"ResetColumnsButton\"\n >\n Reset to default\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"lt-scroll\" id=\"ItemTable\">\n <table class=\"lt-table\">\n <colgroup>\n @if (!options.noSelect) { <col class=\"lt-col-select\" /> }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <col [style.width]=\"colWidth(col)\" />\n }\n @if (!options.noMenu) { <col class=\"lt-col-menu\" /> }\n </colgroup>\n <thead class=\"lt-head\">\n <tr cdkDropList cdkDropListOrientation=\"horizontal\" (cdkDropListDropped)=\"onReorder($event)\">\n @if (!options.noSelect) {\n <th class=\"lt-select-cell\">\n <div\n class=\"lt-select-all\"\n [class.checked]=\"allToggled\"\n [class.indeterminate]=\"someSelected\"\n (click)=\"toggleAll()\"\n id=\"SelectAllRows\"\n ></div>\n </th>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <th\n cdkDrag\n [cdkDragDisabled]=\"col.reorderable === false\"\n class=\"lt-th\"\n [ngClass]=\"col.cellClass\"\n id=\"Header_{{ col.key }}\"\n >\n <lib-list-header-cell\n [column]=\"col\"\n [sortDir]=\"col.sortDir\"\n (sortChanged)=\"onHeaderSort($event)\"\n (hideColumn)=\"hideColumn($event)\"\n ></lib-list-header-cell>\n @if (col.resizable !== false) {\n <span class=\"lt-resize\" (pointerdown)=\"startResize(col, $event)\"></span>\n }\n </th>\n }\n @if (!options.noMenu) {\n <th class=\"lt-menu-cell\">\n <div class=\"lt-header-menu-trigger\" (click)=\"openHeaderActionMenu($event)\" id=\"ColumnMenuHeader\">\n &#8942;\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rowData; track trackRow($index, row); let i = $index) {\n <tr\n class=\"lt-row\"\n [class.lt-row-odd]=\"i % 2 === 1\"\n [class.lt-row-selected]=\"row.selected\"\n [class.row-invalid]=\"row.invalid\"\n [class.row-copied]=\"row.copied\"\n >\n @if (!options.noSelect) {\n <td class=\"lt-select-cell\">\n <lib-select-cell [row]=\"row\" [toggleItem]=\"toggleItem\"></lib-select-cell>\n </td>\n }\n @for (col of visibleColumns; track trackCol($index, col)) {\n <td\n class=\"lt-cell\"\n [ngClass]=\"cellClass(col)\"\n (click)=\"onCellClick(col, row, i)\"\n (mouseenter)=\"onCellHover(col, row, i, $event)\"\n (mouseleave)=\"onCellLeave()\"\n >\n @switch (renderMode(col)) {\n @case ('component') {\n <ng-container *ngComponentOutlet=\"col.cell; inputs: cellInputs(col, row, i)\"></ng-container>\n }\n @case ('html') {\n <span class=\"lt-cell-html\" [innerHTML]=\"cellHtml(col, row, i)\"></span>\n }\n @default {\n <span class=\"lt-cell-text\">{{ cellText(col, row, i) }}</span>\n }\n }\n </td>\n }\n @if (!options.noMenu) {\n <td class=\"lt-menu-cell\">\n @if (row.actionList?.length) {\n <button\n type=\"button\"\n class=\"lt-row-menu-trigger\"\n (click)=\"$event.stopPropagation(); openActionMenu($event, row)\"\n attr.id=\"RowMenu_{{ row.id }}\"\n aria-label=\"Row actions\"\n >&#8942;</button>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n @if (!isLoading && filterApplied && (!rowData || rowData.length === 0)) {\n <div class=\"no-rows-message\">No Rows To Show</div>\n }\n </div>\n\n <div class=\"lt-footer\">\n <div class=\"lt-footer-count\">\n Showing <span id=\"Start\">{{ startCount }}</span>&#8211;<span id=\"End\">{{ endCount }}</span>\n of <span id=\"Total\">{{ totalCount }}</span>\n </div>\n <div class=\"lt-footer-controls\">\n <div class=\"lt-rpp\">\n <span class=\"lt-rpp-label\">Rows per page</span>\n <select\n class=\"lt-rpp-select\"\n [disabled]=\"filtering\"\n [value]=\"pageSize\"\n (change)=\"onPageSizeChange(+$any($event.target).value)\"\n aria-label=\"Rows per page\"\n >\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n <div class=\"lt-pager\">\n <button type=\"button\" class=\"lt-page-btn icon-prev\" [disabled]=\"prevDisabled\" (click)=\"prevPage()\" aria-label=\"Previous page\"></button>\n @for (p of pageWindow; track $index) {\n @if (p === 0) {\n <span class=\"lt-page-gap\">&#8230;</span>\n } @else {\n <button\n type=\"button\"\n class=\"lt-page-num\"\n [class.is-current]=\"p === currentPageNum\"\n (click)=\"goToPage(p)\"\n >{{ p }}</button>\n }\n }\n <button type=\"button\" class=\"lt-page-btn icon-next\" [disabled]=\"nextDisabled\" (click)=\"nextPage()\" aria-label=\"Next page\"></button>\n </div>\n </div>\n </div>\n\n <!-- header action menu -->\n <lib-list-menu\n menuId=\"HeaderActionMenu\"\n [open]=\"openHeaderMenu\"\n [items]=\"headerMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onHeaderMenuClick($event)\"\n (closed)=\"closeHeaderActionMenu()\"\n ></lib-list-menu>\n\n <!-- row action menu -->\n <lib-list-menu\n menuId=\"RowActionMenu\"\n [open]=\"openMenu\"\n [items]=\"rowMenuItems\"\n [left]=\"menuLeft\"\n [top]=\"menuTop\"\n (itemClick)=\"onRowMenuClick($event)\"\n (closed)=\"closeActionMenu()\"\n ></lib-list-menu>\n </div>\n} @else {\n <lib-no-items\n (clickEmit)=\"openCreate()\"\n [image]=\"noItemsImage\"\n [isEmpty]=\"!rowData || rowData.length === 0\"\n [isLoading]=\"isLoading\"\n [typeName]=\"entityTypeLabel\"\n [hasAdd]=\"showNoItemsAdd\"\n [readonly]=\"noItemsReadonly\"\n [accessDenied]=\"accessDenied\"\n ></lib-no-items>\n}\n\n@if (hoverTooltip) {\n <div class=\"lt-tooltip\" [ngStyle]=\"{ left: hoverTooltip.left + 'px', top: hoverTooltip.top + 'px' }\">\n <ng-container *ngComponentOutlet=\"hoverTooltip.component; inputs: hoverTooltip.inputs\"></ng-container>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}.lt-columns-btn,.lt-autorefresh-btn,.lt-refresh-btn,.lt-filter-more-btn{display:inline-flex;align-items:center;gap:.4375rem;height:var(--lt-toolbar-height);padding:0 .75rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;white-space:nowrap;transition:border-color .15s,background .15s,color .15s}.lt-columns-btn svg,.lt-autorefresh-btn svg,.lt-refresh-btn svg,.lt-filter-more-btn svg{display:block}.lt-columns-btn:hover,.lt-autorefresh-btn:hover,.lt-refresh-btn:hover,.lt-filter-more-btn:hover,.is-open.lt-columns-btn,.is-open.lt-autorefresh-btn,.is-open.lt-refresh-btn,.is-open.lt-filter-more-btn,.is-on.lt-columns-btn,.is-on.lt-autorefresh-btn,.is-on.lt-refresh-btn,.is-on.lt-filter-more-btn{border-color:var(--lt-link);color:var(--lt-link)}.lt-columns-count,.lt-filter-more-count{display:inline-flex;align-items:center;justify-content:center;min-width:1.125rem;height:1.125rem;padding:0 .3125rem;font-size:.6875rem;font-weight:700;color:var(--lt-control-bg);background:var(--lt-link);border-radius:.5625rem}.lt-container{display:flex;position:relative;height:calc(100% - var(--lt-container-reserve, 5.625rem));width:100%;flex-direction:column}.lt-scroll{position:relative;z-index:1;width:100%;flex:0 1 auto;min-height:0;overflow:auto;background-color:var(--lt-bg)}.lt-scroll .no-rows-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--lt-cell-text);font-size:.875rem;pointer-events:none}.lt-table{width:100%;border-collapse:collapse;table-layout:fixed;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text)}.lt-head{position:sticky;top:0;z-index:10}.lt-head th{position:relative;box-sizing:border-box;height:2.75rem;padding:0 .3125rem 0 .625rem;text-align:left;vertical-align:middle;background-color:var(--lt-header-bg);border-bottom:1px solid var(--lt-header-underline);white-space:nowrap;-webkit-user-select:none;user-select:none}.lt-head th.lt-th{border-right:1px solid var(--lt-border)}.lt-head .lt-select-cell,.lt-head .lt-menu-cell{width:3.75rem;padding:0}.lt-resize{position:absolute;right:0;top:50%;transform:translateY(-50%);width:.625rem;height:.875rem;cursor:col-resize}.lt-resize:before,.lt-resize:after{content:\"\";position:absolute;top:0;height:100%;width:1px;background:transparent;transition:background .15s}.lt-resize:before{right:.25rem}.lt-resize:after{right:.125rem}.lt-resize:hover:before,.lt-resize:hover:after{background:var(--lt-icon)}.lt-head th:hover .lt-resize:before,.lt-head th:hover .lt-resize:after{background:var(--lt-border)}.lt-select-all{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-sizing:border-box;width:1rem;height:1rem;border:1px solid var(--lt-control-border, var(--lt-border));border-radius:.25rem;background:var(--lt-control-bg);background-repeat:no-repeat;background-position:center center;cursor:pointer;transition:border-color .12s,background-color .12s}.lt-select-all:hover{border-color:var(--lt-link)}.lt-select-all.checked{background-color:var(--lt-link);border-color:var(--lt-link);background-size:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 7.6 5.7 10.8 11.5 3.6'/%3E%3C/svg%3E\")}.lt-select-all.indeterminate{background-color:var(--lt-link);border-color:var(--lt-link);background-image:linear-gradient(#fff,#fff);background-size:.5rem .125rem}.lt-header-menu-trigger{display:flex;align-items:center;justify-content:center;height:100%;font-size:1.25rem;color:var(--lt-icon);cursor:pointer}.lt-header-menu-trigger:hover{color:var(--lt-icon-active)}.lt-row-menu-trigger{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;font-size:1.25rem;line-height:1;color:var(--lt-icon);background:transparent;border:none;border-radius:var(--lt-radius);cursor:pointer;transition:color .15s,background .15s}.lt-row-menu-trigger:hover{color:var(--lt-icon-active);background:var(--lt-row-hover)}.cdk-drag-preview{box-sizing:border-box;background-color:var(--lt-header-bg);color:var(--lt-header-text);box-shadow:var(--shadowLarge);padding:0 .625rem;display:flex;align-items:center;border-radius:var(--lt-radius)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s cubic-bezier(0,0,.2,1)}.lt-row{height:var(--lt-row-height);background-color:var(--lt-row-bg);color:var(--lt-cell-text)}.lt-row td{border-bottom:1px solid var(--lt-border)}.lt-row.lt-row-odd{background-color:var(--lt-row-alt-bg)}.lt-row:hover{background-color:var(--lt-row-hover)}.lt-row.lt-row-selected{background-color:var(--lt-row-selected)}.lt-row.lt-row-selected:hover{background-color:var(--lt-row-hover)}.lt-row.row-invalid{background-color:var(--invalid)!important}.lt-row.row-copied{background-color:var(--copiedRow)!important}.lt-cell{height:var(--lt-row-height);padding:0 .625rem 0 1.25rem;text-align:left;vertical-align:middle;border:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-user-select:text;user-select:text}.lt-cell-text{display:inline-block;overflow:hidden;text-overflow:ellipsis;max-width:100%;vertical-align:middle}.lt-cell.lt-cell-center{text-align:center}.lt-badge-cell .lt-cell-text{padding:.1875rem .5rem;font-size:.75rem;font-weight:600;line-height:1.2;color:var(--lt-cell-text);background:color-mix(in srgb,var(--lt-border) 45%,transparent);border:1px solid var(--lt-border);border-radius:999px}.lt-badge-cell .lt-cell-text:empty{display:none}.lt-select-cell,.lt-menu-cell{width:3.75rem;padding:0;text-align:center;vertical-align:middle;overflow:visible;position:relative}.lt-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin:var(--lt-toolbar-margin-y, 0) 0;padding:0 0 var(--lt-toolbar-pad-bottom, .625rem);min-height:var(--lt-toolbar-height);position:relative;z-index:30}.lt-toolbar-left{display:flex;align-items:center;gap:.5rem;flex:1 1 auto;min-width:0;flex-wrap:nowrap}.lt-filter-zone{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;flex:0 1 auto;min-width:0}.lt-filters{display:flex;align-items:center;flex-wrap:nowrap;gap:.5rem;min-width:0;flex:0 1 auto;overflow:hidden}.lt-filter-item{display:inline-flex;flex:0 0 auto}.lt-filter-more{position:relative;flex:0 0 auto}.lt-filter-more-btn{gap:.375rem;padding:0 .625rem}.lt-filter-more-btn .lt-filter-more-icon{display:inline-flex}.lt-filter-overflow{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;min-width:15rem;max-width:min(22rem,90vw);display:flex;flex-direction:column;gap:.5rem;padding:.625rem;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);animation:lt-chooser-in .12s ease-out}.lt-filter-overflow-title{font-family:var(--lt-font);font-size:.6875rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--groupText)}.lt-filter-overflow-row{display:flex}.lt-filter-overflow-row lib-list-filter-cell,.lt-filter-overflow-row .lt-filter-item{flex:1 1 auto;min-width:0}.lt-toolbar-right{display:flex;align-items:center;gap:.75rem;flex:0 0 auto}.lt-divider{flex:0 0 auto;width:1px;height:1.5rem;background:var(--lt-border)}.lt-search{position:relative;display:flex;align-items:center;width:18rem;max-width:40vw;flex:0 1 18rem}.lt-search-icon{position:absolute;left:.625rem;display:block;color:var(--lt-icon);pointer-events:none}.lt-search-input{width:100%;height:var(--lt-toolbar-height);padding:0 .75rem 0 1.75rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);outline:none;transition:border-color .15s,box-shadow .15s}.lt-search-input::placeholder{color:var(--placeholder)}.lt-search-input:focus{border-color:var(--lt-link);box-shadow:0 0 0 .1875rem color-mix(in srgb,var(--lt-link) 18%,transparent)}.lt-count{font-size:.8125rem;color:var(--groupText);white-space:nowrap}.lt-count .lt-count-of{color:var(--groupText)}.lt-pager{display:flex;align-items:center;gap:.25rem}.lt-page-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);color:var(--lt-icon);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;font-size:.9375rem;transition:border-color .15s,color .15s}.lt-page-btn:not(:disabled):hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-btn:disabled{opacity:.4;cursor:default}.lt-refresh-btn{padding:0;width:var(--lt-toolbar-height);justify-content:center}.lt-refresh-btn:disabled{opacity:.5;cursor:default;border-color:var(--lt-border);color:var(--lt-cell-text)}.lt-columns{position:relative;flex:0 0 auto}.lt-column-chooser{position:absolute;top:calc(100% + .25rem);right:0;z-index:1000;width:15rem;max-height:calc(100vh - 9rem);display:flex;flex-direction:column;background:var(--lt-control-bg);border:1px solid var(--lt-border);border-radius:var(--lt-radius);box-shadow:var(--shadowLarge);overflow:hidden;animation:lt-chooser-in .12s ease-out}@keyframes lt-chooser-in{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}.lt-chooser-header{flex:0 0 auto;display:flex;flex-direction:column;padding:.625rem .75rem .5rem;border-bottom:1px solid var(--lt-border)}.lt-chooser-title{font-size:.8125rem;font-weight:700;color:var(--lt-cell-text)}.lt-chooser-subtitle{font-size:.6875rem;color:var(--groupText)}.lt-chooser-list{flex:1 1 auto;list-style:none;margin:0;padding:.375rem .5rem;min-height:0;overflow-y:auto}.lt-chooser-row{display:flex;align-items:center;justify-content:space-between;min-height:2.5rem;padding:.5rem;border-radius:var(--lt-radius);background:var(--lt-control-bg)}.lt-chooser-row:hover{background:var(--lt-row-hover)}.lt-chooser-row:hover .lt-chooser-handle{opacity:1}.lt-chooser-toggle{display:flex;align-items:center;gap:.625rem;flex:1 1 auto;min-width:0;padding:0;background:transparent;border:none;font:inherit;font-size:.875rem;color:var(--lt-cell-text);text-align:left;cursor:pointer}.lt-chooser-toggle:disabled{cursor:default;opacity:.5}.lt-chooser-box{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:1rem;height:1rem;border:.09375rem solid var(--lt-control-border, var(--lt-border));border-radius:.25rem;color:var(--lt-control-bg);background:var(--lt-control-bg);transition:background .12s,border-color .12s}.lt-chooser-toggle.checked .lt-chooser-box{background:var(--lt-link);border-color:var(--lt-link)}.lt-chooser-toggle:not(:disabled):hover .lt-chooser-box{border-color:var(--lt-link)}.lt-chooser-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lt-chooser-default-tag{flex:0 0 auto;margin-left:.375rem;font-size:.6875rem;font-weight:400;font-style:italic;color:var(--groupText);white-space:nowrap}.lt-chooser-handle{flex:0 0 auto;display:inline-flex;align-items:center;color:var(--lt-icon);cursor:grab;padding-left:.5rem;-webkit-user-select:none;user-select:none}.lt-chooser-handle:active{cursor:grabbing}.lt-chooser-footer{flex:0 0 auto;padding:.5rem .75rem;border-top:1px solid var(--lt-border)}.lt-chooser-reset{width:100%;height:1.875rem;font-size:.75rem;font-weight:600;color:var(--lt-link);background:transparent;border:1px solid var(--lt-border);border-radius:var(--lt-radius);cursor:pointer}.lt-chooser-reset:disabled{color:var(--groupText);cursor:default;opacity:.6}.lt-chooser-reset:not(:disabled):hover{border-color:var(--lt-link)}.lt-footer{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;padding:.625rem .125rem 0;font-family:var(--lt-font);font-size:.8125rem;color:var(--groupText)}.lt-footer-count{white-space:nowrap}.lt-footer-count span{color:var(--lt-cell-text);font-weight:600}.lt-footer-controls{display:flex;align-items:center;gap:1.25rem;flex-wrap:nowrap}.lt-rpp{display:inline-flex;align-items:center;gap:.5rem;white-space:nowrap}.lt-rpp-label{text-transform:none;font-weight:400;color:var(--groupText)}.lt-rpp-select{width:auto;min-width:4rem;height:var(--lt-toolbar-height);padding:0 1.75rem 0 .625rem;font-family:var(--lt-font);font-size:.8125rem;color:var(--lt-cell-text);background-color:var(--lt-control-bg);background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right .5rem center;border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);appearance:none;-webkit-appearance:none;cursor:pointer}.lt-rpp-select:focus{outline:none;border-color:var(--lt-link)}.lt-page-num{display:inline-flex;align-items:center;justify-content:center;min-width:var(--lt-toolbar-height);height:var(--lt-toolbar-height);padding:0 .5rem;font-family:var(--lt-font);font-size:.8125rem;font-weight:600;color:var(--lt-cell-text);background:var(--lt-control-bg);border:var(--inputBorderWidth) solid var(--lt-control-border, var(--lt-border));border-radius:var(--lt-radius);cursor:pointer;transition:border-color .15s,color .15s,background .15s}.lt-page-num:hover{border-color:var(--lt-link);color:var(--lt-link)}.lt-page-num.is-current{color:#fff;background:var(--lt-link);border-color:var(--lt-link);cursor:default}.lt-page-gap{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;color:var(--groupText)}.lt-tooltip{position:fixed;z-index:1100;pointer-events:none;animation:lt-tooltip-in .12s ease-out}@keyframes lt-tooltip-in{0%{opacity:0;transform:translateY(-.1875rem)}to{opacity:1;transform:translateY(0)}}\n"] }]
12965
+ }], ctorParameters: () => [{ type: ListTableService }, { type: DataTableFilterService }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i1.DomSanitizer }], propDecorators: { tableId: [{
12966
+ type: Input
12967
+ }], columns: [{
12968
+ type: Input
12969
+ }], rowData: [{
12970
+ type: Input
12971
+ }], isLoading: [{
12972
+ type: Input
12973
+ }], options: [{
12974
+ type: Input
12975
+ }], startCount: [{
12976
+ type: Input
12977
+ }], endCount: [{
12978
+ type: Input
12979
+ }], totalCount: [{
12980
+ type: Input
12981
+ }], filterApplied: [{
12982
+ type: Input
12983
+ }], menuItems: [{
12984
+ type: Input
12985
+ }], headerActions: [{
12986
+ type: Input
12987
+ }], noItemsImage: [{
12988
+ type: Input
12989
+ }], showNoItemsAdd: [{
12990
+ type: Input
12991
+ }], noItemsReadonly: [{
12992
+ type: Input
12993
+ }], accessDenied: [{
12994
+ type: Input
12995
+ }], entityTypeName: [{
12996
+ type: Input
12997
+ }], filterName: [{
12998
+ type: Input
12999
+ }], filterColumn: [{
13000
+ type: Input
13001
+ }], actionRequested: [{
13002
+ type: Output
13003
+ }], tableReady: [{
13004
+ type: Output
13005
+ }], contextMenu: [{
13006
+ type: ViewChild,
13007
+ args: ['contextMenu']
13008
+ }], filterZone: [{
13009
+ type: ViewChild,
13010
+ args: ['filterZone']
13011
+ }], filtersRow: [{
13012
+ type: ViewChild,
13013
+ args: ['filtersRow']
13014
+ }], initialPageSize: [{
13015
+ type: Input
13016
+ }] } });
13017
+
10772
13018
  class Link {
10773
13019
  constructor() {
10774
13020
  this.statusReason = null;
@@ -12828,11 +15074,11 @@ class SideModalComponent {
12828
15074
  this.openChange.emit(this.open);
12829
15075
  }
12830
15076
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SideModalComponent, deps: [{ token: ConsoleEventsService }], target: i0.ɵɵFactoryTarget.Component }); }
12831
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SideModalComponent, isStandalone: false, selector: "lib-side-modal", inputs: { showClose: "showClose", open: "open" }, outputs: { openChange: "openChange" }, ngImport: i0, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-30%;top:0;padding:1.875rem;height:100%;width:30%;min-width:28.125rem;max-width:37.5rem;overflow-y:auto;transition:right .3s ease-in-out;z-index:999999;background-color:var(--formBackground);box-shadow:-.25rem 0 1.25rem #0003;border-left:.0625rem solid var(--tableBorder)}.side-modal-container.side-modal-open{right:0}.side-modal-container .close{position:absolute;top:1rem;right:1rem;font-size:1.5rem;cursor:pointer;color:var(--tableText);transition:all .2s ease;z-index:10}.side-modal-container .close:hover{color:var(--primary);transform:scale(1.1)}::ng-deep .sideNavClosed .side-modal-container.side-modal-open{right:0}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
15077
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SideModalComponent, isStandalone: false, selector: "lib-side-modal", inputs: { showClose: "showClose", open: "open" }, outputs: { openChange: "openChange" }, ngImport: i0, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-42rem;top:0;padding:1.875rem;height:100%;width:30%;min-width:28.125rem;max-width:37.5rem;overflow-y:auto;transition:right .3s ease-in-out;z-index:999999;background-color:var(--formBackground);box-shadow:-.25rem 0 1.25rem #0003;border-left:.0625rem solid var(--tableBorder)}.side-modal-container.side-modal-open{right:0}.side-modal-container .close{position:absolute;top:1rem;right:1rem;font-size:1.5rem;cursor:pointer;color:var(--tableText);transition:all .2s ease;z-index:10}.side-modal-container .close:hover{color:var(--primary);transform:scale(1.1)}::ng-deep .sideNavClosed .side-modal-container.side-modal-open{right:0}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
12832
15078
  }
12833
15079
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SideModalComponent, decorators: [{
12834
15080
  type: Component,
12835
- args: [{ selector: 'lib-side-modal', standalone: false, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-30%;top:0;padding:1.875rem;height:100%;width:30%;min-width:28.125rem;max-width:37.5rem;overflow-y:auto;transition:right .3s ease-in-out;z-index:999999;background-color:var(--formBackground);box-shadow:-.25rem 0 1.25rem #0003;border-left:.0625rem solid var(--tableBorder)}.side-modal-container.side-modal-open{right:0}.side-modal-container .close{position:absolute;top:1rem;right:1rem;font-size:1.5rem;cursor:pointer;color:var(--tableText);transition:all .2s ease;z-index:10}.side-modal-container .close:hover{color:var(--primary);transform:scale(1.1)}::ng-deep .sideNavClosed .side-modal-container.side-modal-open{right:0}\n"] }]
15081
+ args: [{ selector: 'lib-side-modal', standalone: false, template: "<div class=\"side-modal-container\" [ngClass]=\"{'side-modal-open': open}\">\n @if (showClose) {\n <div class=\"close icon-close\" (click)=\"close()\"></div>\n }\n <ng-content></ng-content>\n</div>\n\n", styles: [".side-modal-container{position:fixed;right:-42rem;top:0;padding:1.875rem;height:100%;width:30%;min-width:28.125rem;max-width:37.5rem;overflow-y:auto;transition:right .3s ease-in-out;z-index:999999;background-color:var(--formBackground);box-shadow:-.25rem 0 1.25rem #0003;border-left:.0625rem solid var(--tableBorder)}.side-modal-container.side-modal-open{right:0}.side-modal-container .close{position:absolute;top:1rem;right:1rem;font-size:1.5rem;cursor:pointer;color:var(--tableText);transition:all .2s ease;z-index:10}.side-modal-container .close:hover{color:var(--primary);transform:scale(1.1)}::ng-deep .sideNavClosed .side-modal-container.side-modal-open{right:0}\n"] }]
12836
15082
  }], ctorParameters: () => [{ type: ConsoleEventsService }], propDecorators: { showClose: [{
12837
15083
  type: Input
12838
15084
  }], open: [{
@@ -13317,6 +15563,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
13317
15563
  See the License for the specific language governing permissions and
13318
15564
  limitations under the License.
13319
15565
  */
15566
+ /**
15567
+ * Legacy Identities page (ag-grid `lib-data-table`). Selected by the route when the
15568
+ * "Beta Features" flag is off. Slated for removal once the new list-table page (see
15569
+ * {@link IdentitiesListPageComponent}) graduates — at which point this file and its
15570
+ * template are deleted outright.
15571
+ */
13320
15572
  class IdentitiesPageComponent extends ListPageComponent {
13321
15573
  constructor(svc, filterService, dialogForm, tabNames, router, consoleEvents, extService) {
13322
15574
  super(filterService, svc, consoleEvents, dialogForm, extService);
@@ -13513,6 +15765,231 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
13513
15765
  args: [IDENTITY_EXTENSION_SERVICE]
13514
15766
  }] }] });
13515
15767
 
15768
+ /*
15769
+ Copyright NetFoundry Inc.
15770
+
15771
+ Licensed under the Apache License, Version 2.0 (the "License");
15772
+ you may not use this file except in compliance with the License.
15773
+ You may obtain a copy of the License at
15774
+
15775
+ https://www.apache.org/licenses/LICENSE-2.0
15776
+
15777
+ Unless required by applicable law or agreed to in writing, software
15778
+ distributed under the License is distributed on an "AS IS" BASIS,
15779
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15780
+ See the License for the specific language governing permissions and
15781
+ limitations under the License.
15782
+ */
15783
+ /**
15784
+ * New Identities page, rendered on the shared `lib-list-table`. Extends the common
15785
+ * {@link ListPageComponent} (which future list-table pages extend the same way).
15786
+ * Selected by the route when the "Beta Features" flag is on; the legacy ag-grid page
15787
+ * ({@link IdentitiesPageComponent}) handles the flag-off case. The two pages carry
15788
+ * their own copies of the identity actions during the migration so the legacy one can
15789
+ * be deleted outright once this graduates.
15790
+ */
15791
+ class IdentitiesListPageComponent extends ListPageComponent {
15792
+ constructor(svc, filterService, dialogForm, tabNames, router, consoleEvents, extService) {
15793
+ super(filterService, svc, consoleEvents, dialogForm, extService);
15794
+ this.svc = svc;
15795
+ this.tabNames = tabNames;
15796
+ this.router = router;
15797
+ this.extService = extService;
15798
+ this.title = 'Identities';
15799
+ this.isLoading = false;
15800
+ this.formDataChanged = false;
15801
+ this.tableId = 'identities';
15802
+ this.initialPageSize = 50;
15803
+ }
15804
+ /** The new table consumes the declarative ListColumn set. */
15805
+ loadTableColumns() {
15806
+ return this.svc.initListTableColumns();
15807
+ }
15808
+ ngOnInit() {
15809
+ this.tabs = this.tabNames.getTabs('identities');
15810
+ this.svc.refreshData = this.refreshData;
15811
+ this.subscription.add(this.router.events.subscribe((event) => {
15812
+ if (event instanceof NavigationEnd) {
15813
+ this.refreshData();
15814
+ }
15815
+ }));
15816
+ this.svc.getIdentityRoleAttributes();
15817
+ super.ngOnInit();
15818
+ }
15819
+ headerActionClicked(action) {
15820
+ switch (action) {
15821
+ case 'add':
15822
+ this.svc.openEditForm();
15823
+ break;
15824
+ case 'edit':
15825
+ this.svc.openUpdate();
15826
+ break;
15827
+ case 'delete':
15828
+ const selectedItems = this.rowData.filter((row) => {
15829
+ return row.selected;
15830
+ });
15831
+ const label = selectedItems.length > 1 ? 'identities' : 'identity';
15832
+ this.openBulkDelete(selectedItems, label);
15833
+ break;
15834
+ default:
15835
+ }
15836
+ }
15837
+ closeModal(event) {
15838
+ this.svc.sideModalOpen = false;
15839
+ if (event?.refresh) {
15840
+ this.refreshData();
15841
+ this.svc.getIdentityRoleAttributes();
15842
+ }
15843
+ }
15844
+ dataChanged(event) {
15845
+ this.formDataChanged = event;
15846
+ }
15847
+ tableAction(event) {
15848
+ const extensionActionFound = this.handleExtensionActions(event);
15849
+ this.trackMenuAction(event?.action, event?.item);
15850
+ if (extensionActionFound) {
15851
+ return;
15852
+ }
15853
+ switch (event?.action) {
15854
+ case 'toggleAll':
15855
+ this.itemToggled(event.item);
15856
+ this.rowData.forEach((item) => {
15857
+ if (item.typeId === 'Router') {
15858
+ item.selected = false;
15859
+ }
15860
+ });
15861
+ break;
15862
+ case 'toggleItem':
15863
+ this.itemToggled(event.item);
15864
+ break;
15865
+ case 'update':
15866
+ this.svc.openEditForm(event?.item?.id);
15867
+ break;
15868
+ case 'create':
15869
+ this.svc.openEditForm();
15870
+ break;
15871
+ case 'override':
15872
+ this.svc.openOverridesModal(event.item);
15873
+ break;
15874
+ case 'delete':
15875
+ this.deleteItem(event.item);
15876
+ break;
15877
+ case 'download-enrollment':
15878
+ this.downloadJWT(event.item);
15879
+ break;
15880
+ case 'reset-enrollment':
15881
+ this.resetEnrollment(event.item);
15882
+ break;
15883
+ case 'reissue-enrollment':
15884
+ this.reissueEnrollment(event.item);
15885
+ break;
15886
+ case "identity-service-path":
15887
+ this.openIdentityVisualizer(event.item);
15888
+ break;
15889
+ case "qr-code":
15890
+ this.showQRCode(event.item);
15891
+ break;
15892
+ case 'download-all':
15893
+ this.downloadAllItems();
15894
+ break;
15895
+ case 'download-selected':
15896
+ this.svc.downloadItems(this.selectedItems);
15897
+ break;
15898
+ case 'reset-mfa':
15899
+ this.isLoading = true;
15900
+ this.svc.resetMFA(event.item).finally(() => {
15901
+ this.isLoading = false;
15902
+ this.refreshData();
15903
+ });
15904
+ break;
15905
+ default:
15906
+ break;
15907
+ }
15908
+ }
15909
+ downloadJWT(item) {
15910
+ const jwt = this.svc.getJWT(item);
15911
+ this.svc.downloadJWT(jwt, item.name);
15912
+ }
15913
+ showQRCode(item) {
15914
+ const data = {
15915
+ jwt: this.svc.getJWT(item),
15916
+ expiration: this.svc.getEnrollmentExpiration(item),
15917
+ qrCodeSize: 500,
15918
+ identity: item,
15919
+ qrOnly: true,
15920
+ };
15921
+ this.dialogRef = this.dialogForm.open(QrCodeComponent, {
15922
+ data: data,
15923
+ autoFocus: false,
15924
+ });
15925
+ }
15926
+ trackMenuAction(action, item) {
15927
+ this.extService?.consoleActionTriggered?.emit({
15928
+ action,
15929
+ page: 'identity',
15930
+ item,
15931
+ type: 'menu-action'
15932
+ });
15933
+ }
15934
+ deleteItem(item) {
15935
+ this.openBulkDelete([item], 'identity');
15936
+ }
15937
+ openIdentityVisualizer(identity) {
15938
+ this.dialogRef = this.dialogForm.open(IdentityServicePathComponent, {
15939
+ data: {
15940
+ identity: identity,
15941
+ },
15942
+ autoFocus: false,
15943
+ });
15944
+ }
15945
+ resetEnrollment(identity) {
15946
+ this.dialogRef = this.dialogForm.open(ResetEnrollmentComponent, {
15947
+ data: {
15948
+ identity: identity,
15949
+ type: 'reset',
15950
+ },
15951
+ autoFocus: false,
15952
+ });
15953
+ this.dialogRef.afterClosed().subscribe((result) => {
15954
+ if (result) {
15955
+ this.refreshData(undefined, true);
15956
+ }
15957
+ });
15958
+ }
15959
+ reissueEnrollment(identity) {
15960
+ this.dialogRef = this.dialogForm.open(ResetEnrollmentComponent, {
15961
+ data: {
15962
+ identity: identity,
15963
+ type: 'reissue',
15964
+ },
15965
+ autoFocus: false,
15966
+ });
15967
+ this.dialogRef.afterClosed().subscribe((result) => {
15968
+ if (result) {
15969
+ this.refreshData(undefined, true);
15970
+ }
15971
+ });
15972
+ }
15973
+ ngOnDestroy() {
15974
+ super.ngOnDestroy();
15975
+ }
15976
+ canDeactivate() {
15977
+ if (this.formDataChanged && this.svc.sideModalOpen) {
15978
+ return confirm('You have unsaved changes. Do you want to leave this page and discard your changes or stay on this page?');
15979
+ }
15980
+ return true;
15981
+ }
15982
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: IdentitiesListPageComponent, deps: [{ token: IdentitiesPageService }, { token: DataTableFilterService }, { token: i5.MatDialog }, { token: TabNameService }, { token: i4.Router }, { token: ConsoleEventsService }, { token: IDENTITY_EXTENSION_SERVICE }], target: i0.ɵɵFactoryTarget.Component }); }
15983
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: IdentitiesListPageComponent, isStandalone: false, selector: "lib-identities-list", usesInheritance: true, ngImport: i0, template: "<div class=\"ziti-page-container ziti-identities-container lt-skin-page\">\n <lib-list-table-header\n [title]=\"title\"\n subtitle=\"Manage identities across the network\"\n createLabel=\"Create Identity\"\n infoText=\"Identities are the clients, services, and routers enrolled in this network.\"\n [tabs]=\"tabs\"\n [showAdd]=\"!itemsSelected\"\n [blockCreate]=\"headerBlockCreate\"\n [blockDelete]=\"headerBlockDelete\"\n (actionClicked)=\"headerActionClicked($event)\"\n >\n <span header-icon class=\"icon-identity\" aria-hidden=\"true\"></span>\n </lib-list-table-header>\n <ng-content select=\"[slot=list-page-subheader]\"></ng-content>\n <lib-list-table class=\"lt-skin\" [tableId]=\"tableId\" [initialPageSize]=\"initialPageSize\"\n [noItemsReadonly]=\"headerBlockCreate\"\n [accessDenied]=\"headerBlockRead\"\n [rowData]=\"rowData\"\n [isLoading]=\"isLoading\"\n [columns]=\"columnDefs\"\n (actionRequested)=\"tableAction($event)\"\n [startCount]=\"startCount\"\n [endCount]=\"endCount\"\n [totalCount]=\"totalCount\"\n [filterApplied]=\"filterApplied\"\n [menuItems]=\"svc.menuItems\"\n [headerActions]=\"svc.tableHeaderActions\"\n [noItemsImage]=\"'No_Clients'\"\n (tableReady)=\"tableReady($event)\"\n >\n </lib-list-table>\n</div>\n<lib-side-modal [(open)]=\"svc.sideModalOpen\" [showClose]=\"false\">\n @if (svc.modalType === 'overrides' && svc.sideModalOpen) {\n <lib-overrides\n [identity]=\"svc.selectedIdentity\"\n (close)=\"closeModal($event)\"\n ></lib-overrides>\n }\n</lib-side-modal>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n", styles: [".ziti-page-container .action{line-height:3.75rem;top:1.5625rem;right:1.5625rem;z-index:999}.ziti-identities-container ::ng-deep .zac-wrapper-container{height:0rem;left:-62499.9375rem;position:fixed}.ziti-identities-container ::ng-deep .zac-wrapper-container .action{display:none}.ziti-ag-grid{height:100%;position:relative;width:100%}.ziti-ag-grid .ziti-ag-grid-table{height:100%;width:100%}.ziti-ag-grid .ziti-ag-grid-table .ag-root-wrapper{border-bottom-style:none}.ziti-ag-grid .ziti-ag-grid-table .ag-cell{align-items:center;display:flex}.system-policies-toggle,.hidden-results-container{position:relative;right:0;display:flex;flex-direction:row;justify-content:center;align-items:flex-start;margin-top:.625rem;padding-left:1.25rem;border-radius:var(--inputBorderRadius);padding-bottom:0;padding-right:0;padding-top:0;flex-wrap:nowrap;flex:1 0 auto}.system-policies-toggle .toggle-text,.hidden-results-container .toggle-text{font-weight:600;font-size:.75rem}.system-policies-toggle .toggle-text a,.hidden-results-container .toggle-text a{cursor:pointer}.system-policies-toggle .toggle,.hidden-results-container .toggle{width:4.6875rem;margin-left:.625rem}.system-policies-toggle .toggle.on .switch,.hidden-results-container .toggle.on .switch{left:3.25rem}.system-policies-toggle .spinner,.hidden-results-container .spinner{display:inline-block;width:.625rem;height:.625rem;border-radius:50%;background:transparent;border-top:.125rem solid var(--tableText);border-right:.125rem solid var(--tableText);border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;animation:loading .5s infinite linear;margin-left:.3125rem}.hidden-results-container.loading-results{opacity:.7}.nf-cell-vert-align,.ag-row .ag-cell{align-items:center;display:flex}.hidden-columns-list{align-items:center;display:flex;flex-direction:row;margin-left:.4375rem;min-height:1.875rem;flex-wrap:wrap;flex:0 1 auto}.hidden-columns-list.hidden-results{flex-wrap:nowrap;overflow:auto}.hidden-columns-list.hidden-results .hidden-column-item{flex:1 0 auto}.hidden-columns-list::-webkit-scrollbar{height:.875rem}.hidden-columns-list .hidden-column-item{align-items:center;background-color:var(--background);border:.125rem solid var(--background);border-radius:.75rem;cursor:pointer;color:var(----stroke);font-weight:600;font-family:Open Sans,sans-serif;display:flex;flex:0 1 auto;height:1.5625rem!important;margin-left:.625rem;margin-top:.3125rem;padding:.125rem .625rem .125rem .3125rem;position:relative;text-align:center;box-shadow:unset;transform:translateY(0)}.hidden-columns-list .ag-dnd-ghost:hover{border:.125rem solid var(--primaryColor)!important}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-label,.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash{color:var(--ag-alpine-active-color, var(--primaryColor))}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash:before{content:\"\\f113\"}.hidden-columns-list .ag-dnd-ghost-label{line-height:initial}::ng-deep .darkmode .ag-dnd-ghost-icon .ag-icon-eye-slash{color:#fff!important}.tMenu{transition-property:opacity;z-index:99999999999;height:fit-content}.tMenu.action-menu{position:fixed;width:10.9375rem}.tMenu .tActionRow{transition-property:opacity}.tMenu .tActionRow.has-icon{display:flex;flex-direction:row;align-items:center}.tMenu .tActionRow .v7Offline{background-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline{width:.875rem;height:.875rem;border-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline:hover{border-color:var(--placeholder)}.hidden-items-container{display:flex;flex-direction:row;width:100%;flex-wrap:nowrap;justify-content:space-between}.os-icon{width:1.4375rem;height:1.4375rem;background-size:1.4375rem 1.4375rem;display:inline-flex;margin-right:.5rem;margin-top:.3125rem;margin-bottom:.3125rem}::ng-deep .date-time-container{position:fixed}::ng-deep .date-time-container .p-calendar .p-inputtext{height:.0625rem;width:.0625rem;padding:0}::ng-deep .date-time-container .filter-button-container{height:3.125rem;display:flex;flex-direction:row;justify-content:center;align-items:center}::ng-deep .date-time-container .filter-button-container .toggleButton{padding-left:.3125rem;padding-right:.3125rem;width:unset}::ng-deep .date-time-container .p-calendar{height:1.875rem}::ng-deep .date-time-container .p-calendar .p-inputtext{height:1.875rem;border-top-left-radius:1.25rem;border-bottom-left-radius:1.25rem;text-indent:.3125rem}::ng-deep .date-time-container .p-calendar .p-button-icon-only{border-top-right-radius:1.25rem;border-bottom-right-radius:1.25rem}#TimeSearch{position:relative;float:left;display:inline-block;margin-left:.625rem;width:3.125rem;height:2.5rem;background-color:var(--primary)}#TimeSearch #TimeSearchList{width:2.75rem;height:0;position:absolute;top:0;left:-.125rem;overflow:hidden;transition:var(--transition);background-color:var(--menu);border-radius:.5rem;z-index:30}#TimeSearch #TimeSearchList .time{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;font-size:.75rem;font-weight:700;text-align:center;margin-top:.125rem;margin-left:.125rem;left:-.125rem;top:-3.1875rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSearchList.open{top:-3.1875rem;width:2.75rem;height:14.125rem}#TimeSearch #TimeSelectButton{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;text-align:center;font-size:.75rem;font-weight:700;margin-top:.4375rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSelectButton:hover,#TimeSearch #TimeSearchList .time:hover{background-color:var(--primaryColor);color:var(--white)}::ng-deep p-togglebutton .p-togglebutton.p-button{border-radius:var(--inputBorderRadius)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;margin-top:-.125rem!important}::ng-deep p-togglebutton .p-togglebutton.p-button:hover{filter:brightness(99%)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;transition:all .3s ease}::ng-deep p-togglebutton .p-togglebutton.p-button:active{filter:brightness(95%)!important;box-shadow:none!important;transform:translateY(.0625rem)!important;transition:all .3s ease}#TimeSearch #TimeSelectButton{margin-top:.25rem!important}::ng-deep .p-calendar .p-inputtext{height:2.3125rem}::ng-deep .p-inputtext{border-radius:var(--inputBorderRadius)}.calendar{display:inline-block;padding-left:.3125rem;float:left;padding-right:.625rem;margin-top:-.125rem}::ng-deep .p-datepicker:not(.p-datepicker-inline){background:var(--table);border:0 none;box-shadow:#091e4226 0 1.25rem 2rem -.5rem;border-radius:.75rem}::ng-deep .p-button.p-button-text{background-color:transparent;color:var(--primaryColor);border-color:transparent}::ng-deep .p-button.p-button-text:enabled:hover{background:var(--primaryColorShadowLight);color:var(--primaryColor);border-color:transparent}::ng-deep .p-datepicker table td>span.p-highlight{color:var(--tableText);background:var(--primaryColorShadowLight)}::ng-deep .p-inputtext:enabled:hover{border-color:var(--primaryColor)}::ng-deep .p-datepicker{padding:.5rem;padding-bottom:0;background:var(--table);color:var(--tableText);border:.0625rem solid var(--inputBorder);border-radius:.1875rem}::ng-deep .p-inputtext:enabled:focus{outline:0 none;outline-offset:0;box-shadow:0 0 0 1 var(--primaryColorShadowLight);border-color:var(--primaryColor)}::ng-deep .p-inputtext{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol;font-size:1rem;color:var(--tableText);background:var(--table);padding:.5rem;border:.0625rem solid var(--inputBorder);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--inputBorderRadius)}::ng-deep .p-datepicker:not(.p-datepicker-inline) .p-datepicker-header{background:var(--table)}::ng-deep .p-datepicker .p-datepicker-header{padding:.5rem;color:var(--tableText);background:var(--table);font-weight:600;margin:0;border-bottom:.0625rem solid var(--inputBorder);border-top-right-radius:.75rem;border-top-left-radius:.75rem}::ng-deep .p-datepicker:not(.p-disabled) table td span:not(.p-highlight):not(.p-disabled):hover{background:#e9ecef;color:var(--table)}::ng-deep .p-datepicker .p-timepicker{border-top:.0625rem solid var(--background);padding:.5rem}::ng-deep .p-togglebutton.p-button{background:var(--background);border:.0625rem solid var(--inputBorder);color:var(--tableText);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s}::ng-deep .p-datepicker .p-datepicker-buttonbar{padding:1rem 0;border-top:.0625rem solid var(--inputBorder)}::ng-deep .p-button{color:#fff;background:var(--primaryColor);border:.0625rem solid var(--primaryColor);padding:.495rem 1rem;font-size:1rem;transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;border-radius:var(--inputBorderRadius)}::ng-deep .p-togglebutton.p-button.p-highlight{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}::ng-deep .p-togglebutton.p-button.p-highlight:hover{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}app-zac-wrapper{z-index:999999}\n"], dependencies: [{ kind: "component", type: ListTableHeaderComponent, selector: "lib-list-table-header", inputs: ["title", "subtitle", "createLabel", "infoText", "tabs", "showAdd", "blockCreate", "blockDelete", "tabsUseRouter", "activeTab"], outputs: ["actionClicked", "tabClicked"] }, { kind: "component", type: ListTableComponent, selector: "lib-list-table", inputs: ["tableId", "columns", "rowData", "isLoading", "options", "startCount", "endCount", "totalCount", "filterApplied", "menuItems", "headerActions", "noItemsImage", "showNoItemsAdd", "noItemsReadonly", "accessDenied", "entityTypeName", "filterName", "filterColumn", "initialPageSize"], outputs: ["actionRequested", "tableReady"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: OverridesComponent, selector: "lib-overrides", inputs: ["identity"], outputs: ["close"] }] }); }
15984
+ }
15985
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: IdentitiesListPageComponent, decorators: [{
15986
+ type: Component,
15987
+ args: [{ selector: 'lib-identities-list', standalone: false, template: "<div class=\"ziti-page-container ziti-identities-container lt-skin-page\">\n <lib-list-table-header\n [title]=\"title\"\n subtitle=\"Manage identities across the network\"\n createLabel=\"Create Identity\"\n infoText=\"Identities are the clients, services, and routers enrolled in this network.\"\n [tabs]=\"tabs\"\n [showAdd]=\"!itemsSelected\"\n [blockCreate]=\"headerBlockCreate\"\n [blockDelete]=\"headerBlockDelete\"\n (actionClicked)=\"headerActionClicked($event)\"\n >\n <span header-icon class=\"icon-identity\" aria-hidden=\"true\"></span>\n </lib-list-table-header>\n <ng-content select=\"[slot=list-page-subheader]\"></ng-content>\n <lib-list-table class=\"lt-skin\" [tableId]=\"tableId\" [initialPageSize]=\"initialPageSize\"\n [noItemsReadonly]=\"headerBlockCreate\"\n [accessDenied]=\"headerBlockRead\"\n [rowData]=\"rowData\"\n [isLoading]=\"isLoading\"\n [columns]=\"columnDefs\"\n (actionRequested)=\"tableAction($event)\"\n [startCount]=\"startCount\"\n [endCount]=\"endCount\"\n [totalCount]=\"totalCount\"\n [filterApplied]=\"filterApplied\"\n [menuItems]=\"svc.menuItems\"\n [headerActions]=\"svc.tableHeaderActions\"\n [noItemsImage]=\"'No_Clients'\"\n (tableReady)=\"tableReady($event)\"\n >\n </lib-list-table>\n</div>\n<lib-side-modal [(open)]=\"svc.sideModalOpen\" [showClose]=\"false\">\n @if (svc.modalType === 'overrides' && svc.sideModalOpen) {\n <lib-overrides\n [identity]=\"svc.selectedIdentity\"\n (close)=\"closeModal($event)\"\n ></lib-overrides>\n }\n</lib-side-modal>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n", styles: [".ziti-page-container .action{line-height:3.75rem;top:1.5625rem;right:1.5625rem;z-index:999}.ziti-identities-container ::ng-deep .zac-wrapper-container{height:0rem;left:-62499.9375rem;position:fixed}.ziti-identities-container ::ng-deep .zac-wrapper-container .action{display:none}.ziti-ag-grid{height:100%;position:relative;width:100%}.ziti-ag-grid .ziti-ag-grid-table{height:100%;width:100%}.ziti-ag-grid .ziti-ag-grid-table .ag-root-wrapper{border-bottom-style:none}.ziti-ag-grid .ziti-ag-grid-table .ag-cell{align-items:center;display:flex}.system-policies-toggle,.hidden-results-container{position:relative;right:0;display:flex;flex-direction:row;justify-content:center;align-items:flex-start;margin-top:.625rem;padding-left:1.25rem;border-radius:var(--inputBorderRadius);padding-bottom:0;padding-right:0;padding-top:0;flex-wrap:nowrap;flex:1 0 auto}.system-policies-toggle .toggle-text,.hidden-results-container .toggle-text{font-weight:600;font-size:.75rem}.system-policies-toggle .toggle-text a,.hidden-results-container .toggle-text a{cursor:pointer}.system-policies-toggle .toggle,.hidden-results-container .toggle{width:4.6875rem;margin-left:.625rem}.system-policies-toggle .toggle.on .switch,.hidden-results-container .toggle.on .switch{left:3.25rem}.system-policies-toggle .spinner,.hidden-results-container .spinner{display:inline-block;width:.625rem;height:.625rem;border-radius:50%;background:transparent;border-top:.125rem solid var(--tableText);border-right:.125rem solid var(--tableText);border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;animation:loading .5s infinite linear;margin-left:.3125rem}.hidden-results-container.loading-results{opacity:.7}.nf-cell-vert-align,.ag-row .ag-cell{align-items:center;display:flex}.hidden-columns-list{align-items:center;display:flex;flex-direction:row;margin-left:.4375rem;min-height:1.875rem;flex-wrap:wrap;flex:0 1 auto}.hidden-columns-list.hidden-results{flex-wrap:nowrap;overflow:auto}.hidden-columns-list.hidden-results .hidden-column-item{flex:1 0 auto}.hidden-columns-list::-webkit-scrollbar{height:.875rem}.hidden-columns-list .hidden-column-item{align-items:center;background-color:var(--background);border:.125rem solid var(--background);border-radius:.75rem;cursor:pointer;color:var(----stroke);font-weight:600;font-family:Open Sans,sans-serif;display:flex;flex:0 1 auto;height:1.5625rem!important;margin-left:.625rem;margin-top:.3125rem;padding:.125rem .625rem .125rem .3125rem;position:relative;text-align:center;box-shadow:unset;transform:translateY(0)}.hidden-columns-list .ag-dnd-ghost:hover{border:.125rem solid var(--primaryColor)!important}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-label,.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash{color:var(--ag-alpine-active-color, var(--primaryColor))}.hidden-columns-list .ag-dnd-ghost:hover .ag-dnd-ghost-icon .ag-icon-eye-slash:before{content:\"\\f113\"}.hidden-columns-list .ag-dnd-ghost-label{line-height:initial}::ng-deep .darkmode .ag-dnd-ghost-icon .ag-icon-eye-slash{color:#fff!important}.tMenu{transition-property:opacity;z-index:99999999999;height:fit-content}.tMenu.action-menu{position:fixed;width:10.9375rem}.tMenu .tActionRow{transition-property:opacity}.tMenu .tActionRow.has-icon{display:flex;flex-direction:row;align-items:center}.tMenu .tActionRow .v7Offline{background-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline{width:.875rem;height:.875rem;border-color:var(--placeholder)}.tMenu .tActionRow .v7OnlineOffline:hover{border-color:var(--placeholder)}.hidden-items-container{display:flex;flex-direction:row;width:100%;flex-wrap:nowrap;justify-content:space-between}.os-icon{width:1.4375rem;height:1.4375rem;background-size:1.4375rem 1.4375rem;display:inline-flex;margin-right:.5rem;margin-top:.3125rem;margin-bottom:.3125rem}::ng-deep .date-time-container{position:fixed}::ng-deep .date-time-container .p-calendar .p-inputtext{height:.0625rem;width:.0625rem;padding:0}::ng-deep .date-time-container .filter-button-container{height:3.125rem;display:flex;flex-direction:row;justify-content:center;align-items:center}::ng-deep .date-time-container .filter-button-container .toggleButton{padding-left:.3125rem;padding-right:.3125rem;width:unset}::ng-deep .date-time-container .p-calendar{height:1.875rem}::ng-deep .date-time-container .p-calendar .p-inputtext{height:1.875rem;border-top-left-radius:1.25rem;border-bottom-left-radius:1.25rem;text-indent:.3125rem}::ng-deep .date-time-container .p-calendar .p-button-icon-only{border-top-right-radius:1.25rem;border-bottom-right-radius:1.25rem}#TimeSearch{position:relative;float:left;display:inline-block;margin-left:.625rem;width:3.125rem;height:2.5rem;background-color:var(--primary)}#TimeSearch #TimeSearchList{width:2.75rem;height:0;position:absolute;top:0;left:-.125rem;overflow:hidden;transition:var(--transition);background-color:var(--menu);border-radius:.5rem;z-index:30}#TimeSearch #TimeSearchList .time{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;font-size:.75rem;font-weight:700;text-align:center;margin-top:.125rem;margin-left:.125rem;left:-.125rem;top:-3.1875rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSearchList.open{top:-3.1875rem;width:2.75rem;height:14.125rem}#TimeSearch #TimeSelectButton{width:2.5rem;background-color:var(--background);padding-left:.5rem;padding-right:.5rem;border-radius:.5rem;line-height:1.625rem;text-align:center;font-size:.75rem;font-weight:700;margin-top:.4375rem;cursor:pointer;transition:var(--transition)}#TimeSearch #TimeSelectButton:hover,#TimeSearch #TimeSearchList .time:hover{background-color:var(--primaryColor);color:var(--white)}::ng-deep p-togglebutton .p-togglebutton.p-button{border-radius:var(--inputBorderRadius)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;margin-top:-.125rem!important}::ng-deep p-togglebutton .p-togglebutton.p-button:hover{filter:brightness(99%)!important;box-shadow:0 .3125rem 1.25rem #0000000d!important;transition:all .3s ease}::ng-deep p-togglebutton .p-togglebutton.p-button:active{filter:brightness(95%)!important;box-shadow:none!important;transform:translateY(.0625rem)!important;transition:all .3s ease}#TimeSearch #TimeSelectButton{margin-top:.25rem!important}::ng-deep .p-calendar .p-inputtext{height:2.3125rem}::ng-deep .p-inputtext{border-radius:var(--inputBorderRadius)}.calendar{display:inline-block;padding-left:.3125rem;float:left;padding-right:.625rem;margin-top:-.125rem}::ng-deep .p-datepicker:not(.p-datepicker-inline){background:var(--table);border:0 none;box-shadow:#091e4226 0 1.25rem 2rem -.5rem;border-radius:.75rem}::ng-deep .p-button.p-button-text{background-color:transparent;color:var(--primaryColor);border-color:transparent}::ng-deep .p-button.p-button-text:enabled:hover{background:var(--primaryColorShadowLight);color:var(--primaryColor);border-color:transparent}::ng-deep .p-datepicker table td>span.p-highlight{color:var(--tableText);background:var(--primaryColorShadowLight)}::ng-deep .p-inputtext:enabled:hover{border-color:var(--primaryColor)}::ng-deep .p-datepicker{padding:.5rem;padding-bottom:0;background:var(--table);color:var(--tableText);border:.0625rem solid var(--inputBorder);border-radius:.1875rem}::ng-deep .p-inputtext:enabled:focus{outline:0 none;outline-offset:0;box-shadow:0 0 0 1 var(--primaryColorShadowLight);border-color:var(--primaryColor)}::ng-deep .p-inputtext{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol;font-size:1rem;color:var(--tableText);background:var(--table);padding:.5rem;border:.0625rem solid var(--inputBorder);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--inputBorderRadius)}::ng-deep .p-datepicker:not(.p-datepicker-inline) .p-datepicker-header{background:var(--table)}::ng-deep .p-datepicker .p-datepicker-header{padding:.5rem;color:var(--tableText);background:var(--table);font-weight:600;margin:0;border-bottom:.0625rem solid var(--inputBorder);border-top-right-radius:.75rem;border-top-left-radius:.75rem}::ng-deep .p-datepicker:not(.p-disabled) table td span:not(.p-highlight):not(.p-disabled):hover{background:#e9ecef;color:var(--table)}::ng-deep .p-datepicker .p-timepicker{border-top:.0625rem solid var(--background);padding:.5rem}::ng-deep .p-togglebutton.p-button{background:var(--background);border:.0625rem solid var(--inputBorder);color:var(--tableText);transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s}::ng-deep .p-datepicker .p-datepicker-buttonbar{padding:1rem 0;border-top:.0625rem solid var(--inputBorder)}::ng-deep .p-button{color:#fff;background:var(--primaryColor);border:.0625rem solid var(--primaryColor);padding:.495rem 1rem;font-size:1rem;transition:background-color .2s,color .2s,border-color .2s,box-shadow .2s;border-radius:var(--inputBorderRadius)}::ng-deep .p-togglebutton.p-button.p-highlight{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}::ng-deep .p-togglebutton.p-button.p-highlight:hover{background:var(--primaryColor);border:.0625rem solid var(--primaryColor);color:var(--background)}app-zac-wrapper{z-index:999999}\n"] }]
15988
+ }], ctorParameters: () => [{ type: IdentitiesPageService }, { type: DataTableFilterService }, { type: i5.MatDialog }, { type: TabNameService }, { type: i4.Router }, { type: ConsoleEventsService }, { type: undefined, decorators: [{
15989
+ type: Inject,
15990
+ args: [IDENTITY_EXTENSION_SERVICE]
15991
+ }] }] });
15992
+
13516
15993
  class EdgeRoutersPageComponent extends ListPageComponent {
13517
15994
  constructor(svc, filterService, dialogForm, tabNames, consoleEvents, extService) {
13518
15995
  super(filterService, svc, consoleEvents, dialogForm);
@@ -19416,8 +21893,8 @@ class NetworkVisualizerComponent extends VisualizerServiceClass {
19416
21893
  })
19417
21894
  .append('xhtml:body')
19418
21895
  .style('background', 'transparent')
19419
- .html(function (d) {
19420
- return `<div class="node-text-container"><span>${d.data.name}</span></div>`;
21896
+ .html((d) => {
21897
+ return `<div class="node-text-container"><span>${this.escapeHtml(d.data.name)}</span></div>`;
19421
21898
  });
19422
21899
  this.nodeUpdate = this.nodeEnter.merge(this.node);
19423
21900
  this.nodeUpdate
@@ -19636,7 +22113,11 @@ class NetworkVisualizerComponent extends VisualizerServiceClass {
19636
22113
  this.treetooltip.style('top', y);
19637
22114
  document.getElementById('tooltip').innerHTML = this.readTreeKeyValues(event, s);
19638
22115
  }
22116
+ escapeHtml(value) {
22117
+ return String(value == null ? '' : value).replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
22118
+ }
19639
22119
  readTreeKeyValues(event, d) {
22120
+ const self = this;
19640
22121
  const startIndex = d.data.name.indexOf('(');
19641
22122
  const finIndex = d.data.name.indexOf(')');
19642
22123
  const countStr = startIndex > 0 ? d.data.name.substring(startIndex + 1, finIndex) : 0;
@@ -19665,14 +22146,14 @@ class NetworkVisualizerComponent extends VisualizerServiceClass {
19665
22146
  info =
19666
22147
  info +
19667
22148
  '<div class="prop-row"><div class="prop-name">' +
19668
- k +
22149
+ self.escapeHtml(k) +
19669
22150
  ':</div><div class="prop-val">' +
19670
- d[k].join('<br> &nbsp;&nbsp; &nbsp;&nbsp;') +
22151
+ d[k].map((v) => self.escapeHtml(v)).join('<br> &nbsp;&nbsp; &nbsp;&nbsp;') +
19671
22152
  '</div></div>';
19672
22153
  }
19673
22154
  else {
19674
22155
  const propVal = d.data[k] || '';
19675
- info = info + '<div class="prop-row"><div class="prop-name">' + k + ':</div><div class="prop-val">' + propVal + '</div></div>';
22156
+ info = info + '<div class="prop-row"><div class="prop-name">' + self.escapeHtml(k) + ':</div><div class="prop-val">' + self.escapeHtml(propVal) + '</div></div>';
19676
22157
  }
19677
22158
  });
19678
22159
  if (d.data.firstChild && d.data.firstChild === 'Yes' && countStr > this.maxObjectsPerNode - 1) {
@@ -19888,13 +22369,16 @@ class EdgeRouterFormComponent extends ProjectableForm {
19888
22369
  get tunnelerEnabledReadOnly() {
19889
22370
  return this.config.isOpenZiti ? true : !this.isTunnelerDisabled();
19890
22371
  }
22372
+ get isCustomerHosted() {
22373
+ return this.extService.disabledComponents.some(item => item.key === 'customer-hosted');
22374
+ }
19891
22375
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EdgeRouterFormComponent, deps: [{ token: SETTINGS_SERVICE }, { token: EdgeRouterFormService }, { token: ZITI_DATA_SERVICE }, { token: GrowlerService }, { token: EDGE_ROUTER_EXTENSION_SERVICE }, { token: DEFAULT_APP_CONFIG }, { token: i4.Router }, { token: i4.ActivatedRoute }, { token: i1$1.Location }, { token: i5.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
19892
22376
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: EdgeRouterFormComponent, isStandalone: false, selector: "lib-edge-router-form", inputs: { edgeRouterRoleAttributes: "edgeRouterRoleAttributes" }, outputs: { close: "close" }, providers: [
19893
22377
  {
19894
22378
  provide: MatDialogRef,
19895
22379
  useValue: {}
19896
22380
  }
19897
- ], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: PreviewListComponent, selector: "lib-preview-list", inputs: ["label", "clickable", "isLoading", "allNames", "items", "allowRemove", "tooltip", "showfilter", "localFilter"], outputs: ["itemSelected", "itemRemoved", "filterChanged"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: TagSelectorComponent, selector: "lib-tag-selector", inputs: ["placeholder", "disableField", "disableCreate", "hideOption", "isLoading", "errorMessage", "focusOnInit", "namedAttributesServerFiltered", "selectedRoleAttributes", "selectedNamedAttributes", "hasError", "availableRoleAttributes", "availableNamedAttributes"], outputs: ["addRoleAttribute", "addNamedAttribute", "roleAttributeRemoved", "namedAttributeRemoved", "selectedRoleAttributesChange", "selectedNamedAttributesChange", "filterChange"] }, { kind: "component", type: QrCodeComponent, selector: "lib-qr-code", inputs: ["jwt", "token", "expiration", "identity", "type", "authenticators", "qrOnly", "canExpand"], outputs: ["doRefresh"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
22381
+ ], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (isCustomerHosted) {\n <lib-form-field-container\n [title]=\"'LINK LISTENER'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item disabled\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLE LINK LISTENER</div>\n <div\n [ngClass]=\"{ on: formData.linkListener, disabled: true }\"\n class=\"toggle\"\n role=\"switch\"\n aria-disabled=\"true\"\n [attr.aria-checked]=\"!!formData.linkListener\"\n matTooltip=\"This value is reported by the router and can't be changed here.\"\n matTooltipPosition=\"above\"\n >\n <span [hidden]=\"!formData.linkListener\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.linkListener\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container-label.advertise-address-label{height:auto;padding:.125rem 0}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}.config-container.advertise-address-container{height:auto;min-height:var(--defaultInputHeight);padding:0rem 1.25rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: PreviewListComponent, selector: "lib-preview-list", inputs: ["label", "clickable", "isLoading", "allNames", "items", "allowRemove", "tooltip", "showfilter", "localFilter"], outputs: ["itemSelected", "itemRemoved", "filterChanged"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: TagSelectorComponent, selector: "lib-tag-selector", inputs: ["placeholder", "disableField", "disableCreate", "hideOption", "isLoading", "errorMessage", "focusOnInit", "namedAttributesServerFiltered", "selectedRoleAttributes", "selectedNamedAttributes", "hasError", "availableRoleAttributes", "availableNamedAttributes"], outputs: ["addRoleAttribute", "addNamedAttribute", "roleAttributeRemoved", "namedAttributeRemoved", "selectedRoleAttributesChange", "selectedNamedAttributesChange", "filterChange"] }, { kind: "component", type: QrCodeComponent, selector: "lib-qr-code", inputs: ["jwt", "token", "expiration", "identity", "type", "authenticators", "qrOnly", "canExpand"], outputs: ["doRefresh"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
19898
22382
  }
19899
22383
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EdgeRouterFormComponent, decorators: [{
19900
22384
  type: Component,
@@ -19903,7 +22387,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
19903
22387
  provide: MatDialogRef,
19904
22388
  useValue: {}
19905
22389
  }
19906
- ], standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}\n"] }]
22390
+ ], standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n [class.projectable-form-hidden]=\"!dataInit\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"formData.id ? 'Edit Edge Router: ' : 'Create New Edge Router'\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [class.is-scrolled]=\"isScrolled\"\n [saveDisabled]=\"formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <ng-content select=\"[slot=tabs-slot]\"></ng-content>\n <div class=\"edge-router-form-container projectable-form-container\" #formContainer>\n @if (formView === 'simple') {\n <div class=\"projectable-form-main-column form-group-row\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container [title]=\"'Edge Router Name'\" [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\">\n <input\n class=\"form-field-input\"\n placeholder=\"Name this edge router\"\n [ngClass]=\"{error: errors['name']}\"\n [(ngModel)]=\"formData.name\"\n autofocus\n #nameFieldInput\n [disabled]=\"useStrictReadonlyForm\"\n />\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Select or create Edge Router attributes'\"\n [label]=\"'OPTIONAL'\"\n [contentStyle]=\"'z-index: 99999999'\"\n [helpText]=\"'Attributes are tags applied to an resource. Apply the same tag to other Edge Routers to form a group of Edge Routers.'\"\n >\n <lib-tag-selector\n [disableField]=\"useStrictReadonlyForm\"\n [disableCreate]=\"useStrictReadonlyForm\"\n [(selectedRoleAttributes)]=\"formData.roleAttributes\"\n [availableRoleAttributes]=\"edgeRouterRoleAttributes\"\n [placeholder]=\"'Add attributes to group Edge Routers'\"\n ></lib-tag-selector>\n </lib-form-field-container>\n <ng-content select=\"[slot=column-1-slot-1]\"></ng-content>\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore) {\n <div class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'TUNNELER'\"\n [title2]=\"'TRAVERSAL'\"\n [layout]=\"'row'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item\" [class.disabled]=\"!tunnelerEnabledReadOnly\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">TUNNELER ENABLED</div>\n <div (click)=\"tunnelerEnabledReadOnly && !useStrictReadonlyForm && toggleTunnelerEnabled()\"\n [ngClass]=\"{ on: formData.isTunnelerEnabled, disabled: !tunnelerEnabledReadOnly, 'toggle-readonly': useStrictReadonlyForm }\" class=\"toggle\"\n [matTooltip]=\"useStrictReadonlyForm ? '' : (!tunnelerEnabledReadOnly ? 'Disabled because already hosted by netfoundry ' : '')\"\n matTooltipPosition=\"above\" style=\"pointer-events: auto;\">\n <span [hidden]=\"!formData.isTunnelerEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.isTunnelerEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ALLOW TRAVERSAL</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleNoTraversal()\"\n [ngClass]=\"{ on: !formData.noTraversal, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"formData.noTraversal\" class=\"on-label\">YES</span>\n <span [hidden]=\"!formData.noTraversal\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (isCustomerHosted) {\n <lib-form-field-container\n [title]=\"'LINK LISTENER'\"\n class=\"form-field-advanced\"\n >\n <div class=\"config-item disabled\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLE LINK LISTENER</div>\n <div\n [ngClass]=\"{ on: formData.linkListener, disabled: true }\"\n class=\"toggle\"\n role=\"switch\"\n aria-disabled=\"true\"\n [attr.aria-checked]=\"!!formData.linkListener\"\n matTooltip=\"This value is reported by the router and can't be changed here.\"\n matTooltipPosition=\"above\"\n >\n <span [hidden]=\"!formData.linkListener\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.linkListener\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n @if (formData?.hostname || formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <lib-form-field-container [showHeader]=\"false\">\n @if (formData?.hostname) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">Domain Name</span>\n </div>\n <div class=\"config-container domain-container\">\n <div class=\"config-container-label domain-label\">\n {{ formData?.hostname }}\n </div>\n </div>\n </div>\n }\n @if (formData?.supportedProtocols?.tls || formData?.supportedProtocols?.wss) {\n <div class=\"config-item\">\n <div class=\"config-item-label-container\">\n <span class=\"config-title\">ADVERTISE ADDRESS</span>\n </div>\n <div class=\"config-container advertise-address-container\">\n <div class=\"config-container-label advertise-address-label\">\n {{ formData?.supportedProtocols?.tls }}\n <br />\n {{ formData?.supportedProtocols?.wss }}\n </div>\n </div>\n </div>\n }\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'App Data'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-json-view [(data)]=\"formData.appData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </lib-form-field-container>\n </div>\n }\n </div>\n <div class=\"form-group-column two-fifths\">\n <ng-content select=\"[slot=column-2-slot-1]\"></ng-content>\n @if (hasEnrolmentToken && !extService['hideZitiRegistration']) {\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <lib-qr-code\n [identity]=\"formData\"\n [jwt]=\"formData.enrollmentJwt\"\n [token]=\"formData.enrollmentToken\"\n [expiration]=\"formData.enrollmentExpiresAt\"\n (doRefresh)=\"refreshEdgeRouter()\"\n [type]=\"'router'\"\n ></lib-qr-code>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Services'\"\n [count]=\"svc.associatedServices.length\"\n [class]=\"'hide-overflow'\"\n [helpText]=\"'Preview list of all services that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"servicesLoading\"\n [allNames]=\"svc.associatedServiceNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (formData.id) {\n <lib-form-field-container\n [title]=\"'Associated Identities'\"\n [count]=\"svc.associatedIdentities.length\"\n [helpText]=\"'Preview list of all Identities that this Edge Router is associated with.'\"\n >\n <lib-preview-list\n [clickable]=\"false\"\n [isLoading]=\"identitiesLoading\"\n [allNames]=\"svc.associatedIdentityNames\"\n ></lib-preview-list>\n </lib-form-field-container>\n }\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [label]=\"'OPTIONAL'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n }\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading || !dataInit) {\n <lib-loading-indicator [isLoading]=\"isLoading || !dataInit\"></lib-loading-indicator>\n}\n", styles: [".edge-router-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:.9375rem}.form-field-dropdown{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .625rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:1.75rem}.disabled{opacity:.5;pointer-events:none}.toggle.disabled{cursor:not-allowed}::ng-deep .admin-toggle{width:100%;width:35%;display:flex}::ng-deep .admin-toggle .form-field-line{display:none}::ng-deep .admin-toggle .toggle{width:100%}::ng-deep .is-admin-container .form-field-title-container.title-container-2{width:35%!important}.svc-test-result{position:absolute;transition:var(--transition);height:0;width:100%;background-color:var(--background);bottom:0;font-weight:600;padding-left:.625rem;padding-right:.625rem;border-color:var(--inputBorder);border-style:solid;border-width:var(--inputBorderWidth);border-bottom-right-radius:var(--inputBorderRadius);border-bottom-left-radius:var(--inputBorderRadius);border-top-style:none}.svc-test-result.test-result-open{height:fit-content;box-shadow:0 -.125rem .6875rem 0 var(--boxShadow);padding-bottom:.9375rem;padding-top:.9375rem}.svc-test-result .close.icon-close{top:.625rem;right:.625rem;font-size:1.125rem;height:1.125rem;width:1.125rem;line-height:1.125rem}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-label{color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .form-field-line{border-color:var(--primaryColor)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle{border-color:var(--primaryColor);border-style:solid;border-width:var(--inputBorderWidth)}::ng-deep .admin-toggle .form-field-toggle-container:focus .toggle .switch{top:.0625rem}.config-container-label.domain-label,.config-container-label.advertise-address-label{text-transform:lowercase}.config-container-label.advertise-address-label{height:auto;padding:.125rem 0}.config-container.domain-container,.config-container.advertise-address-container{background-color:var(--stroke)}.config-container.advertise-address-container{height:auto;min-height:var(--defaultInputHeight);padding:0rem 1.25rem}\n"] }]
19907
22391
  }], ctorParameters: () => [{ type: SettingsService, decorators: [{
19908
22392
  type: Inject,
19909
22393
  args: [SETTINGS_SERVICE]
@@ -26403,6 +28887,9 @@ class JwtSigner {
26403
28887
  this.targetToken = 'ACCESS';
26404
28888
  this.enrollToCertEnabled = false;
26405
28889
  this.enrollToTokenEnabled = false;
28890
+ this.enrollAuthPolicyId = '';
28891
+ this.enrollNameClaimsSelector = '';
28892
+ this.enrollAttributeClaimsSelector = '';
26406
28893
  }
26407
28894
  }
26408
28895
  ;
@@ -26616,6 +29103,7 @@ class JwtSignerFormComponent extends ProjectableForm {
26616
29103
  this.configKey = 'oauth_test_callback_config';
26617
29104
  this.tokenTypeKey = 'oauth_test_callback_token_type';
26618
29105
  this.oidcVerified = false;
29106
+ this.authPolicies = [];
26619
29107
  this.usePreviousLocation = false;
26620
29108
  this.entityType = 'external-jwt-signers';
26621
29109
  this.entityClass = JwtSigner;
@@ -26627,10 +29115,25 @@ class JwtSignerFormComponent extends ProjectableForm {
26627
29115
  this.settingsService.settingsChange.subscribe((results) => {
26628
29116
  this.settings = results;
26629
29117
  });
29118
+ this.getAuthPolicies();
26630
29119
  if (window.location.href.indexOf('test-auth') > 0) {
26631
29120
  this.handleOAuthCallback();
26632
29121
  }
26633
29122
  }
29123
+ getAuthPolicies() {
29124
+ const paging = {
29125
+ filter: "",
29126
+ noSearch: true,
29127
+ order: "asc",
29128
+ page: 1,
29129
+ searchOn: "name",
29130
+ sort: "name",
29131
+ total: 100
29132
+ };
29133
+ this.zitiService.get('auth-policies', paging, []).then((result) => {
29134
+ this.authPolicies = [...result.data];
29135
+ });
29136
+ }
26634
29137
  ngAfterViewInit() {
26635
29138
  super.ngAfterViewInit(true);
26636
29139
  }
@@ -26796,6 +29299,18 @@ class JwtSignerFormComponent extends ProjectableForm {
26796
29299
  }
26797
29300
  this.formData.targetToken = val;
26798
29301
  }
29302
+ get enrollAuthPolicyId() {
29303
+ if (this.formData && isNil(this.formData.enrollAuthPolicyId)) {
29304
+ this.formData.enrollAuthPolicyId = '';
29305
+ }
29306
+ return this.formData?.enrollAuthPolicyId;
29307
+ }
29308
+ set enrollAuthPolicyId(val) {
29309
+ if (!this.formData) {
29310
+ return;
29311
+ }
29312
+ this.formData.enrollAuthPolicyId = val;
29313
+ }
26799
29314
  get apiCallURL() {
26800
29315
  return this.settings.selectedEdgeController + '/edge/management/v1/external-jwt-signers' + (this.formData.id ? `/${this.formData.id}` : '');
26801
29316
  }
@@ -26822,6 +29337,11 @@ class JwtSignerFormComponent extends ProjectableForm {
26822
29337
  if (!isEmpty(this.formData.targetToken) && this.showTokenType) {
26823
29338
  data.targetToken = this.formData.targetToken;
26824
29339
  }
29340
+ if (this.showAutoEnrollment) {
29341
+ data.enrollAuthPolicyId = this.formData.enrollAuthPolicyId || '';
29342
+ data.enrollNameClaimsSelector = this.formData.enrollNameClaimsSelector || '';
29343
+ data.enrollAttributeClaimsSelector = this.formData.enrollAttributeClaimsSelector || '';
29344
+ }
26825
29345
  if (!isEmpty(this.formData.id)) {
26826
29346
  data.id = this.formData.id;
26827
29347
  }
@@ -27091,11 +29611,11 @@ class JwtSignerFormComponent extends ProjectableForm {
27091
29611
  return !this.errors.jwksEndpoint;
27092
29612
  }
27093
29613
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JwtSignerFormComponent, deps: [{ token: JwtSignerFormService }, { token: SchemaService }, { token: GrowlerService }, { token: SHAREDZ_EXTENSION }, { token: SETTINGS_SERVICE }, { token: ZITI_DATA_SERVICE }, { token: i4.Router }, { token: i4.ActivatedRoute }, { token: i1$1.Location }, { token: ValidationService }, { token: AuthService }, { token: i1$6.OAuthService }, { token: i1$3.HttpClient }, { token: ZAC_LOGIN_SERVICE }, { token: i5.MatDialog }], target: i0.ɵɵFactoryTarget.Component }); }
27094
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JwtSignerFormComponent, isStandalone: false, selector: "lib-configuration", inputs: { errors: "errors" }, outputs: { close: "close" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "oidcVerification", first: true, predicate: ["oidcVerification"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChipsInputComponent, selector: "lib-chips-input", inputs: ["placeholder", "inputId", "addOnBlur", "allowDuplicate", "separator", "invalid"], outputs: ["onAdd", "onRemove", "onModelChange", "onBlur", "keyup"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
29614
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JwtSignerFormComponent, isStandalone: false, selector: "lib-configuration", inputs: { errors: "errors" }, outputs: { close: "close" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["fileSelect"], descendants: true }, { propertyName: "oidcVerification", first: true, predicate: ["oidcVerification"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment Options'\"\n class=\"form-field-advanced\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Enrollment Auth Policy</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The authentication policy assigned to identities created through auto-enrollment. Defaults to the &quot;default&quot; policy.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <select\n [(ngModel)]=\"enrollAuthPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"\">Select an Authentication Policy</option>\n @for (policy of authPolicies; track policy.id) {\n <option [value]=\"policy.id\">{{ policy.name }}</option>\n }\n </select>\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Name Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the name of enrolling identities. Defaults to /sub\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter name claims selector\" [(ngModel)]=\"formData.enrollNameClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Attribute Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the attributes of enrolling identities.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter attribute claims selector\" [(ngModel)]=\"formData.enrollAttributeClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-lock\"></span>\n Enrollment Auth Policy\n </h3>\n <p class=\"help-panel-text\">\n The authentication policy assigned to identities that are created through auto-enrollment. When left unset, the controller's \"default\" authentication policy is used.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Name Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the name for enrolling identities. Defaults to <code>/sub</code> when unset.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Attribute Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the role attributes for enrolling identities.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChipsInputComponent, selector: "lib-chips-input", inputs: ["placeholder", "inputId", "addOnBlur", "allowDuplicate", "separator", "invalid"], outputs: ["onAdd", "onRemove", "onModelChange", "onBlur", "keyup"] }, { kind: "component", type: SideModalComponent, selector: "lib-side-modal", inputs: ["showClose", "open"], outputs: ["openChange"] }, { kind: "component", type: FormHeaderComponent, selector: "lib-form-header", inputs: ["data", "badges", "title", "moreActions", "formView", "saveDisabled", "saveTooltip", "actionButtonText", "moreActionsText", "showHeaderToggle", "showHeaderButton", "strictReadonlyUi", "multiActions"], outputs: ["formViewChange", "actionRequested", "multiActionRequested"] }, { kind: "component", type: FormFieldContainerComponent, selector: "lib-form-field-container", inputs: ["title", "title2", "titleIcon", "layout", "helpText", "helpText2", "label", "count", "action", "actionLabel", "class", "contentStyle", "contentClass", "showHeader", "showToggle", "headerActions", "headerToggle"], outputs: ["actionRequested", "headerToggleChange", "helpClicked"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }, { kind: "component", type: FormFieldToggleComponent, selector: "lib-form-field-toggle", inputs: ["label", "orientation", "labelOn", "labelOff", "toggleOn"], outputs: ["toggleOnChange"] }, { kind: "component", type: JsonViewComponent, selector: "lib-json-view", inputs: ["data", "schema", "readOnly", "jsonInvalid", "showCopy", "showLineNumbers"], outputs: ["dataChange", "jsonChange", "jsonInvalidChange"] }, { kind: "component", type: CustomTagsComponent, selector: "lib-custom-tags", inputs: ["tags", "readOnly"], outputs: ["tagsChange"] }] }); }
27095
29615
  }
27096
29616
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JwtSignerFormComponent, decorators: [{
27097
29617
  type: Component,
27098
- args: [{ selector: 'lib-configuration', standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"] }]
29618
+ args: [{ selector: 'lib-configuration', standalone: false, template: "<div class=\"projectable-form-wrapper\"\n (keyup.enter)=\"save($event)\"\n (keyup.escape)=\"closeModal(false)\"\n tabindex=\"0\"\n >\n <lib-form-header\n [data]=\"formData\"\n [title]=\"(formData.id ? 'JWT Signer Details' : 'Create JWT Signer')\"\n [moreActions]=\"moreActions\"\n [multiActions]=\"multiActions\"\n (actionRequested)=\"headerActionRequested($event)\"\n (multiActionRequested)=\"multiActionRequested($event)\"\n [(formView)]=\"formView\"\n [saveDisabled]=\"svc.saveDisabled || formDataInvalid || formPermissionSaveDisabled\"\n [saveTooltip]=\"formPermissionSaveTooltip\"\n [strictReadonlyUi]=\"useStrictReadonlyForm\"\n ></lib-form-header>\n <div class=\"jwt-signer-form-container projectable-form-container\" #scrollContainer>\n <div class=\"projectable-form-main-column form-group-row\" [hidden]=\"formView !== 'simple'\">\n <div class=\"form-group-column three-fifths\">\n <lib-form-field-container\n [title]=\"'Name'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n @if (!useStrictReadonlyForm) {\n <input class=\"form-field-input\" [(ngModel)]=\"formData.name\" [ngClass]=\"{error: errors['name']}\" placeholder=\"Name this JWT Signer\" #nameFieldInput/>\n } @else {\n <div class=\"form-field-readonly-value\">{{ formData.name }}</div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [layout]=\"'column'\"\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Issuer</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The &quot;issuer&quot; field on the JWT Signer must match the &quot;iss&quot; field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <span class=\"form-field-label\" >{{ useStrictReadonlyForm ? '' : 'REQUIRED' }}</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter issuer\" [(ngModel)]=\"formData.issuer\" [ngClass]=\"{error: errors['issuer']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Audience</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The expected &quot;aud&quot; (audience) field on the JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter audience\" [(ngModel)]=\"formData.audience\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Claims Property</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The field on the JWT for mapping to an identity. Matches the identity's id or externalId if &quot;Use External Id&quot; is set to true. Defaults to &quot;sub&quot;\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter claims property\" [(ngModel)]=\"formData.claimsProperty\" (keyup)=\"checkExternalIdToggle()\" [ngClass]=\"{error: errors['claimsProperty']}\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\" style=\"font-weight: 600; font-size: 14px;\">External ID</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"If enabled, will match the &quot;claimsProperty&quot; to an identity's &quot;externalId&quot; field. If false, it will match to the identity's id field.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"form-field-title-container\">\n <div class=\"config-container-label\">Use External ID</div>\n </div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleUseExternalId()\"\n [ngClass]=\"{ on: formData.useExternalId, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.useExternalId\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.useExternalId\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Client ID</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The client id to use when authenticating to the IDP\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter client ID\" [(ngModel)]=\"formData.clientId\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">External Auth Url</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The URL that users are directed to, to obtain a JWT\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter external auth URL\" [(ngModel)]=\"formData.externalAuthUrl\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Scopes</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"Enter as a comma or space seperated list of claims/permissions\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <lib-chips-input\n (keyup)=\"scopesOnKeyup($event)\"\n [(ngModel)]=\"formData.scopes\"\n [allowDuplicate]=\"false\"\n placeholder=\"Enter scopes\"\n [addOnBlur]=\"true\"\n separator=\",\"\n [disabled]=\"useStrictReadonlyForm\"\n ></lib-chips-input>\n </div>\n </lib-form-field-container>\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment'\"\n [helpText]=\"'Click for more information'\"\n (helpClicked)=\"showAutoEnrollHelp()\"\n >\n <div class=\"form-field-row\">\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Cert</div>\n <div\n (click)=\"toggleEnrollToCertEnabled()\"\n [ngClass]=\"{ on: formData.enrollToCertEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToCertEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToCertEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">Auto-Enroll to Token</div>\n <div\n (click)=\"toggleEnrollToTokenEnabled()\"\n [ngClass]=\"{ on: formData.enrollToTokenEnabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enrollToTokenEnabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enrollToTokenEnabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-toggle [(toggleOn)]=\"showMore\" (toggleOnChange)=\"showMoreChanged($event)\" style=\"margin: 0px 10px\"></lib-form-field-toggle>\n @if (showMore && showTokenType) {\n <lib-form-field-container\n [title]=\"'Target Token Type'\"\n [label]=\"'Required'\"\n [helpText]=\"'The target token type. Can be either Access or ID'\"\n class=\"form-field-advanced\"\n >\n <select\n [(ngModel)]=\"targetToken\"\n id=\"authPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"ACCESS\">Access</option>\n <option value=\"ID\">ID</option>\n </select>\n </lib-form-field-container>\n }\n <div [hidden]=\"!showMore\" class=\"form-group-column\">\n @if (showAutoEnrollment) {\n <lib-form-field-container\n [title]=\"'Auto Enrollment Options'\"\n class=\"form-field-advanced\"\n >\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Enrollment Auth Policy</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The authentication policy assigned to identities created through auto-enrollment. Defaults to the &quot;default&quot; policy.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <select\n [(ngModel)]=\"enrollAuthPolicyId\"\n class=\"form-field-dropdown\"\n [disabled]=\"useStrictReadonlyForm\"\n >\n <option value=\"\">Select an Authentication Policy</option>\n @for (policy of authPolicies; track policy.id) {\n <option [value]=\"policy.id\">{{ policy.name }}</option>\n }\n </select>\n </div>\n <div class=\"form-field-row\">\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Name Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the name of enrolling identities. Defaults to /sub\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter name claims selector\" [(ngModel)]=\"formData.enrollNameClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">Attribute Claims Selector</span>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"The claims JSON pointer selector or top level property used for the attributes of enrolling identities.\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter attribute claims selector\" [(ngModel)]=\"formData.enrollAttributeClaimsSelector\" [disabled]=\"useStrictReadonlyForm\" />\n </div>\n </div>\n </lib-form-field-container>\n }\n <lib-form-field-container\n [title]=\"'Verify OIDC Authentication'\"\n class=\"form-field-advanced\"\n [ngClass]=\"{'formFieldError': !oidcVerified && oidcErrorMessageDetail, 'formFieldSuccess': oidcVerified}\"\n >\n <div class=\"form-field-row\" #oidcVerification>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-title-container\">\n <div class=\"form-field-title\">Callback URL</div>\n <div\n class=\"form-field-info infoicon\"\n matTooltip=\"This URL must be added to the list of allowed callback URLs in your OAuth provider configuration\"\n matTooltipPosition=\"below\"\n ></div>\n </div>\n <div class=\"copy-link-container\">\n <pre class=\"read-only-2\" (click)=\"copyCallbackURL()\">{{ callbackURL }}</pre>\n <div class=\"copy-button\" (click)=\"copyCallbackURL()\"></div>\n </div>\n </div>\n @if (!useStrictReadonlyForm) {\n <div class=\"test-oauth-button\"\n [ngClass]=\"{'oidc-verified': oidcVerified}\"\n (click)=\"testOIDCAuthentication()\"\n >\n {{oidcVerified ? 'Verified' : 'Authenticate' }}\n @if (oauthLoading) {\n <div class=\"spinner\"></div>\n }\n @if (oidcVerified) {\n <input type=\"checkbox\" [disabled]=\"true\" [checked]=\"true\" class=\"success-check\">\n }\n </div>\n }\n </div>\n @if (oidcErrorMessageDetail) {\n <div class=\"oidc-error-message-container\">\n <code class=\"oidc-error-message-source\">{{oidcErrorMessageSource}}: </code>\n <code class=\"oidc-error-message-detail\">{{oidcErrorMessageDetail}}</code>\n @if (oidcErrorMessageDetail2) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail2}}</code>\n }\n @if (oidcErrorMessageDetail3) {\n <code class=\"oidc-error-message-detail2\">{{oidcErrorMessageDetail3}}</code>\n }\n </div>\n }\n @if (oidcAuthTokenClaims) {\n <div class=\"oidc-token-claims-container\">\n <code class=\"oidc-token-claims-title\">OAuth Token Claims: </code>\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"oidcAuthTokenClaims\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [title]=\"'Custom Tags'\"\n [label]=\"'OPTIONAL'\"\n class=\"form-field-advanced\"\n >\n <lib-custom-tags [(tags)]=\"formData.tags\" [readOnly]=\"useStrictReadonlyForm\"></lib-custom-tags>\n </lib-form-field-container>\n </div>\n </div>\n <div class=\"form-group-column two-fifths\">\n <lib-form-field-container\n [title]=\"'Verification'\"\n [label]=\"useStrictReadonlyForm ? '' : 'REQUIRED'\"\n >\n <div\n class=\"radio-group-container\"\n [class.radio-group-readonly]=\"useStrictReadonlyForm\"\n (keydown)=\"radioKeyDownHandler($event)\"\n [attr.tabindex]=\"useStrictReadonlyForm ? -1 : 0\"\n >\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'JWKS_ENDPOINT', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('JWKS_ENDPOINT')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">JWKS Endpoint</span>\n </div>\n <div\n class=\"radio-button-container\"\n [ngClass]=\"{'selected': signatureMethod === 'CERT_PEM', 'toggle-readonly': useStrictReadonlyForm}\"\n (click)=\"!useStrictReadonlyForm && selectSignatureMethod('CERT_PEM')\"\n >\n <div class=\"radio-button-circle\">\n <div class=\"radio-button-inner-circle\"></div>\n </div>\n <span class=\"radio-button-label\">Cert PEM</span>\n </div>\n </div>\n @if (signatureMethod === 'JWKS_ENDPOINT') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">JWKS Endpoint</span>\n </div>\n </div>\n <input\n class=\"form-field-input\"\n placeholder=\"Enter endpoint URL\"\n [(ngModel)]=\"formData.jwksEndpoint\"\n [ngClass]=\"{error: errors['jwksEndpoint']}\"\n [disabled]=\"useStrictReadonlyForm\"\n />\n </div>\n </div>\n }\n @if (signatureMethod === 'CERT_PEM') {\n <div class=\"form-field-input-group\">\n <div class=\"form-field-label-container select-file-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">PEM</span>\n </div>\n <span\n class=\"select-file-button\"\n [class.toggle-readonly]=\"useStrictReadonlyForm\"\n (click)=\"!useStrictReadonlyForm && openFileSelect($event)\"\n >\n Select File\n @if (fileSelectOpening) {\n <div class=\"spinner\"></div>\n }\n </span>\n <input\n #fileSelect\n type=\"file\"\n style=\"display:none\"\n [disabled]=\"useStrictReadonlyForm\"\n (change)=\"selectPemFile($event)\"\n >\n </div>\n <textarea\n placeholder=\"Paste pem contents or select file\"\n [(ngModel)]=\"formData.certPem\"\n [ngClass]=\"{error: errors['certPem']}\"\n [disabled]=\"useStrictReadonlyForm\"\n ></textarea>\n </div>\n <div class=\"form-field-label-container\">\n <div class=\"form-field-header\">\n <div class=\"form-field-title-container\">\n <span class=\"form-field-title\">KID</span>\n </div>\n <span class=\"form-field-label\" >Optional</span>\n </div>\n <input class=\"form-field-input\" placeholder=\"Enter KID\" [(ngModel)]=\"formData.kid\" [disabled]=\"useStrictReadonlyForm\"/>\n </div>\n </div>\n }\n </lib-form-field-container>\n <lib-form-field-container\n [showHeader]=\"false\"\n >\n <div class=\"config-item\">\n <div class=\"config-container toggle-container\">\n <div class=\"config-container-label\">ENABLED</div>\n <div\n (click)=\"!useStrictReadonlyForm && toggleEnabled()\"\n [ngClass]=\"{ on: formData.enabled, 'toggle-readonly': useStrictReadonlyForm }\"\n class=\"toggle\"\n >\n <span [hidden]=\"!formData.enabled\" class=\"on-label\">YES</span>\n <span [hidden]=\"formData.enabled\" class=\"off-label\">NO</span>\n <div class=\"switch\"></div>\n </div>\n </div>\n </div>\n </lib-form-field-container>\n @if (showApiCalls) {\n <lib-form-field-container\n [title]=\"'API Calls'\"\n [headerActions]=\"apiOptions\"\n (actionRequested)=\"apiActionRequested($event)\"\n [class]=\"'api-data-no-wrap'\"\n >\n <div class=\"form-row\">\n <input class=\"form-field-input\" [value]=\"apiCallURL\"/>\n <div class=\"icon-copy copy-text\" (click)=\"copyToClipboard(apiCallURL)\"></div>\n </div>\n @if (formData) {\n <lib-json-view [showLineNumbers]=\"false\" [(data)]=\"apiData\" [readOnly]=\"true\" [showCopy]=\"true\"></lib-json-view>\n }\n </lib-form-field-container>\n }\n </div>\n </div>\n @if (formView === 'raw') {\n <div class=\"form-group-column\">\n @if (formData) {\n <lib-json-view [(data)]=\"formData\" [readOnly]=\"useStrictReadonlyForm\"></lib-json-view>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n}\n<lib-side-modal [(open)]=\"showAutoEnrollHelpModal\" [showClose]=\"true\">\n <div class=\"help-panel-content\">\n <h2 class=\"help-panel-title\">What is Auto Enrollment?</h2>\n <p class=\"help-panel-intro\">\n Auto Enrollment enables automatic identity creation when users authenticate via JWT tokens.\n </p>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-certificate\"></span>\n Auto-Enroll to Cert\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with a certificate when a user authenticates using a JWT token. This is useful for certificate-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-key\"></span>\n Auto-Enroll to Token\n </h3>\n <p class=\"help-panel-text\">\n When enabled, the controller will automatically create an identity and enroll it with an authentication token when a user authenticates using a JWT token. This is useful for token-based authentication workflows.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-lock\"></span>\n Enrollment Auth Policy\n </h3>\n <p class=\"help-panel-text\">\n The authentication policy assigned to identities that are created through auto-enrollment. When left unset, the controller's \"default\" authentication policy is used.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Name Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the name for enrolling identities. Defaults to <code>/sub</code> when unset.\n </p>\n </div>\n\n <div class=\"help-panel-section\">\n <h3 class=\"help-panel-subtitle\">\n <span class=\"icon-identity\"></span>\n Attribute Claims Selector\n </h3>\n <p class=\"help-panel-text\">\n A JSON pointer selector (or top-level claim property) identifying which JWT claim supplies the role attributes for enrolling identities.\n </p>\n </div>\n\n <div class=\"help-panel-note\">\n <strong>Note:</strong> These options are only available in Ziti 2.0 and later versions.\n </div>\n </div>\n</lib-side-modal>\n", styles: ["@charset \"UTF-8\";.jwt-signer-form-container{display:flex;flex-direction:column;height:100%;width:100%;align-items:center;gap:var(--gapXL)}.jwt-signer-form-container .select-file-button{padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);height:1.5625rem;width:fit-content;white-space:nowrap;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:.875rem;background:var(--primary);border-style:solid;border-width:.0625rem;border-color:var(--stroke);color:var(--white);cursor:pointer;border-radius:var(--inputBorderRadius);gap:var(--marginMedium);margin-top:-.3125rem}.jwt-signer-form-container .select-file-button:hover{filter:brightness(.8)}.jwt-signer-form-container .select-file-button:active{filter:grayscale(1);transform:translateY(.0625rem)}.jwt-signer-form-container .select-file-button .spinner{display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:transparent;border-top:.125rem solid white;border-right:.125rem solid white;border-bottom:.125rem solid transparent;border-left:.125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear}.jwt-signer-form-container .select-file-label-container{gap:var(--marginMedium)}::ng-deep .jwt-signer-form-container select:disabled,::ng-deep .jwt-signer-form-container input:disabled{opacity:.75!important;cursor:text;background-color:var(--stroke)!important}::ng-deep .api-data-no-wrap .jse-text-mode{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller{width:100%}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content{width:100%;overflow:auto}::ng-deep .api-data-no-wrap .jse-text-mode .cm-editor .cm-scroller .cm-content .\\37cr{white-space:nowrap}.radio-group-container{display:flex;gap:var(--marginLarge)}.radio-group-container:focus .radio-button-container .radio-button-circle{border-color:var(--primaryColor)}.radio-group-container .radio-button-container{display:flex;flex-direction:row;align-items:center;background-color:var(--formGroup);border-radius:var(--inputBorderRadius);width:100%;padding-left:var(--paddingMedium);padding-right:var(--paddingMedium);padding-top:var(--paddingLarge);padding-bottom:var(--paddingLarge);cursor:pointer;gap:var(--marginMedium)}.radio-group-container .radio-button-container .radio-button-circle{display:flex;width:1.3125rem;height:1.3125rem;border-radius:1.3125rem;border-width:.125rem;border-style:solid;border-color:var(--offWhite_ALWAYS);align-items:center;justify-content:center;background-color:var(--offWhite)}.radio-group-container .radio-button-container .radio-button-circle .radio-button-inner-circle{display:none;width:.9375rem;height:.9375rem;border-radius:.9375rem;align-items:center;justify-content:center;background-color:var(--primaryColor)}.radio-group-container .radio-button-container.selected .radio-button-circle .radio-button-inner-circle{display:flex}.radio-group-container .radio-button-container:active .radio-button-circle .radio-button-inner-circle{display:flex;background-color:var(--menu)}.radio-group-container .radio-button-container .radio-button-label{color:var(--offWhite_ALWAYS);font-size:.875rem;font-weight:600}.radio-button-container.toggle-readonly{cursor:default;pointer-events:none;opacity:.92}.select-file-button.toggle-readonly{cursor:default;pointer-events:none;opacity:.85}.test-oauth-button{color:var(--offWhite_ALWAYS);height:var(--defaultInputHeight);border-style:solid;border-color:var(--inputBorder);border-radius:var(--inputBorderRadius);vertical-align:middle;text-align:center;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:color .2s;font-size:.9375rem;font-weight:600;font-family:Open Sans,sans-serif;text-transform:uppercase;cursor:pointer;width:100%;background-color:var(--primaryColor);margin-top:1.5rem;max-width:15rem}.test-oauth-button.oidc-verified{filter:brightness(.95);color:var(--offWhite_ALWAYS);background-color:var(--green)}.test-oauth-button .spinner{display:inline-block;width:var(--spinnerSizeMedium);height:var(--spinnerSizeMedium);border-radius:50%;background:transparent;border-top:var(--spinnerBorderTopMedium);border-right:var(--spinnerBorderRightMedium);border-bottom:var(--spinnerBorderBottomMedium);border-left:var(--spinnerBorderLeftMedium);animation:loading .5s infinite linear;margin-left:var(--marginSmall)}.test-oauth-button:hover{filter:brightness(.95)}.test-oauth-button:active{transform:translateY(1px)}.success-check{width:1.0625rem;height:1.0625rem;cursor:pointer;background-color:var(--navigation);margin:0;margin-top:.25rem;border-radius:2rem;margin-left:var(--marginSmall)}.success-check:checked{background-color:var(--green)!important;background-image:url(/assets/images/check.png);background-size:.675rem;background-position:center center;background-repeat:no-repeat;border:none}.success-check:checked:disabled{opacity:1!important}.oidc-token-claims-container{background-color:var(--backgroundMuted);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--tableText);font-weight:600;overflow:auto}.oidc-token-claims-container .oidc-token-claims-title{font-weight:600;font-size:1rem;white-space:nowrap;margin-bottom:var(--marginMedium)}.oidc-error-message-container{background-color:var(--lightRed);height:fit-content;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding:var(--paddingMedium);border-radius:var(--inputBorderRadius);color:var(--red);font-weight:600;overflow:auto}.oidc-error-message-container .oidc-error-message-source{font-weight:600;font-size:1rem;white-space:nowrap}.oidc-error-message-container .oidc-error-message-detail2,.oidc-error-message-container .oidc-error-message-detail{margin-top:var(--marginSmall);font-weight:400;white-space:nowrap}.oidc-error-message-container::-webkit-scrollbar{height:6px;width:6px}.oidc-error-message-container::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.oidc-error-message-container:hover::-webkit-scrollbar-thumb{background-clip:unset!important}.read-only-2{display:flex;overflow:auto;white-space:nowrap;opacity:1!important;cursor:pointer;padding:var(--paddingMedium);padding-bottom:var(--paddingSmall)}.read-only-2::-webkit-scrollbar{height:6px;width:6px}.read-only-2::-webkit-scrollbar-thumb{background-color:var(--menu);border-radius:10px}.read-only-2:hover{text-decoration:underline}.read-only-2:hover::-webkit-scrollbar-thumb{background-clip:unset}.copy-link-container{display:flex;flex-direction:row;justify-content:space-between;align-items:center;opacity:1!important;cursor:text;border-radius:var(--inputBorderRadius);height:var(--defaultInputHeight);background-color:var(--backgroundMuted)!important}.copy-button{display:flex;align-items:center;justify-content:center;height:100%;width:2.575rem;margin-left:.125rem;background:url(/assets/svgs/copy.svg) no-repeat;background-size:1.125rem 1.125rem;background-position:center;cursor:pointer;border-top-right-radius:var(--inputBorderRadius);border-bottom-right-radius:var(--inputBorderRadius);background-color:#0000000f}.copy-button:hover{filter:brightness(.95);background-color:#0000001f}.help-panel-content{display:flex;flex-direction:column;padding-top:3rem}.help-panel-content .help-panel-title{margin:0 0 .25rem;color:var(--primary);font-size:1.5rem;font-weight:600;line-height:1.3;padding-right:2rem}.help-panel-content .help-panel-intro{line-height:1.6;margin-bottom:1.5rem;color:var(--tableText);font-size:.9375rem}.help-panel-content .help-panel-section{margin-bottom:1.5rem;padding:1rem;background-color:var(--navigation);border-radius:var(--inputBorderRadius);border-left:.25rem solid var(--primary)}.help-panel-content .help-panel-section .help-panel-subtitle{margin:0 0 .75rem;color:var(--primary);font-size:1.125rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.help-panel-content .help-panel-section .help-panel-subtitle span[class^=icon-]{font-size:1rem;opacity:.8}.help-panel-content .help-panel-section .help-panel-text{margin:0;line-height:1.6;color:var(--tableText);font-size:.875rem}.help-panel-content .help-panel-note{padding:1rem;background-color:#ffc1071a;border-radius:var(--inputBorderRadius);border-left:.25rem solid #ffc107;color:var(--tableText);font-size:.875rem;line-height:1.6}.help-panel-content .help-panel-note strong{color:#ffc107}\n"] }]
27099
29619
  }], ctorParameters: () => [{ type: JwtSignerFormService }, { type: SchemaService }, { type: GrowlerService }, { type: undefined, decorators: [{
27100
29620
  type: Inject,
27101
29621
  args: [SHAREDZ_EXTENSION]
@@ -31923,11 +34443,13 @@ class SettingsComponent {
31923
34443
  this.hideTags = false;
31924
34444
  this.transitions = true;
31925
34445
  this.isFilterAfterSave = false;
34446
+ this.betaFeatures = false;
31926
34447
  }
31927
34448
  ngOnInit() {
31928
34449
  this.isDark = localStorage.getItem('mode') === 'dark';
31929
34450
  this.hideTags = localStorage.getItem('hideTags') === 'yes';
31930
34451
  this.isFilterAfterSave = localStorage.getItem('filterAfterSave') === 'on';
34452
+ this.betaFeatures = localStorage.getItem('betaFeatures') === 'on';
31931
34453
  const transitions = localStorage.getItem('Transitions');
31932
34454
  this.transitions = transitions !== 'off';
31933
34455
  this.updateTransitions();
@@ -31945,6 +34467,10 @@ class SettingsComponent {
31945
34467
  const mode = this.isFilterAfterSave ? 'on' : 'off';
31946
34468
  localStorage.setItem('filterAfterSave', mode);
31947
34469
  }
34470
+ betaFeaturesChanged() {
34471
+ const mode = this.betaFeatures ? 'on' : 'off';
34472
+ localStorage.setItem('betaFeatures', mode);
34473
+ }
31948
34474
  transitionsChange() {
31949
34475
  const mode = this.transitions ? 'on' : 'off';
31950
34476
  localStorage.setItem('Transitions', mode);
@@ -31967,11 +34493,11 @@ class SettingsComponent {
31967
34493
  document.getElementsByTagName('html')[0].style.setProperty('--secondary', color);
31968
34494
  }
31969
34495
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31970
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsComponent, isStandalone: false, selector: "lib-settings", ngImport: i0, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"], dependencies: [{ kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
34496
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsComponent, isStandalone: false, selector: "lib-settings", ngImport: i0, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Beta Features</label>\n <div class=\"toggle\" [class.on]=\"betaFeatures\" (click)=\"betaFeatures = !betaFeatures; betaFeaturesChanged()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"], dependencies: [{ kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
31971
34497
  }
31972
34498
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsComponent, decorators: [{
31973
34499
  type: Component,
31974
- args: [{ selector: 'lib-settings', standalone: false, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"] }]
34500
+ args: [{ selector: 'lib-settings', standalone: false, template: "<div class=\"projectable-form-wrapper\">\n <div class=\"header\">\n <div class=\"title\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"line\"></div>\n <div class=\"tabs\">\n <div class=\"tab selected\">Manage <span>{{pageTitle}}</span></div>\n <div class=\"tab\" routerLink=\"/profile\">Manage Profile</div>\n </div>\n <div class=\"constrained\">\n <div class=\"grid split\">\n <div>\n <label>Filter after Save</label>\n <div class=\"toggle\" [class.on]=\"isFilterAfterSave\" (click)=\"isFilterAfterSave = !isFilterAfterSave; filterAfter()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Dark mode</label>\n <div class=\"toggle\" [class.on]=\"isDark\" (click)=\"isDark = !isDark; swap()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Hide Tags</label>\n <div class=\"toggle\" [class.on]=\"hideTags\" (click)=\"hideTags = !hideTags; hide()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Transitions</label>\n <div class=\"toggle\" [class.on]=\"transitions\" (click)=\"transitions = !transitions; transitionsChange()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n <div>\n <label>Beta Features</label>\n <div class=\"toggle\" [class.on]=\"betaFeatures\" (click)=\"betaFeatures = !betaFeatures; betaFeaturesChanged()\">\n <div class=\"switch\"></div>\n <div class=\"label\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".header .constrained .grid.split{grid-template-columns:1fr 1fr}label{color:var(--dark);text-transform:uppercase;margin-top:var(--marginMedium);position:relative;display:inline-block;width:100%;margin-bottom:.1875rem;font-size:.875rem;white-space:nowrap;font-weight:600;font-family:Open Sans;overflow:hidden;text-overflow:ellipsis;line-height:var(--defaultLineHeight)}.toggle{width:3.75rem;height:1.875rem;background-color:var(--black);color:var(--white);cursor:pointer;border-radius:6.25rem;position:relative;display:inline-block;margin-top:var(--marginMedium);transition:var(--transition);float:left;margin-right:var(--marginXL)}.toggle.on{background-color:var(--green)}.toggle.on .switch{left:2.1875rem}.toggle.on .label{left:.625rem;content:\"YES\"}.toggle.on .label:before{content:\"YES\"}.toggle .switch{width:1.25rem;height:1.25rem;border-radius:.625rem;background-color:var(--white);transition:var(--transition);position:absolute;left:.3125rem;top:.3125rem;z-index:10}.toggle .label{right:.625rem;line-height:1.875rem;font-size:.75rem;color:var(--white);position:absolute;z-index:8;transition:var(--transition);text-transform:uppercase}.toggleLabel{line-height:3.125rem}.toggle .label:before{content:\"NO\"}.constrained{width:37.5rem;position:relative;margin-left:auto;margin-right:auto}.split{display:grid;grid-gap:.625rem;grid-template-columns:auto 50%}.header{position:relative;display:inline-block;width:100%}.line{position:absolute;width:100%;top:2.5rem;height:.25rem;background-color:var(--stroke)}.tabs{position:relative;display:inline-block;width:100%;font-size:.875rem;min-height:2.5rem;padding-top:.9375rem}.tabs .tab{position:relative;float:left;margin-right:1.25rem;padding-bottom:.3125rem;margin-bottom:.625rem;font-size:.875rem;font-weight:600;cursor:pointer;transition:var(--transition)}.tabs .tab.selected{border-bottom-width:.25rem;border-bottom-color:var(--primary);border-bottom-style:solid}\n"] }]
31975
34501
  }], ctorParameters: () => [] });
31976
34502
 
31977
34503
  class ProfileComponent {
@@ -39492,11 +42018,11 @@ class HeaderBarComponent {
39492
42018
  this.router.navigate(['/dashboard']);
39493
42019
  }
39494
42020
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: HeaderBarComponent, deps: [{ token: i4.Router }, { token: i5.MatDialog }, { token: ZAC_LOGIN_SERVICE }, { token: i4.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); }
39495
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: HeaderBarComponent, isStandalone: false, selector: "lib-header-bar", inputs: { headerTitle: "headerTitle" }, ngImport: i0, template: "<header>\n <div class=\"overlay\"></div>\n <div class=\"left-side-buttons\">\n <div class=\"apps-button clickable\" mattooltip=\"Select Application\" mattooltipposition=\"below\" mattooltipshowdelay=\"500\" (click)=\"goToDashboard()\">\n <div class=\"icon\"></div>\n </div>\n <div class=\"divider-line\"></div>\n <div class=\"header-title-wrapper\">\n <div class=\"header-title-container\" (click)=\"goToDashboard()\">\n <div class=\"header-title-label\">{{headerTitle}}</div>\n <div class=\"title-text-row title-bottom-text\">By <a class=\"nf-name-text\" href=\"https://netfoundry.io\" target=\"_blank\">NetFoundry</a></div>\n </div>\n </div>\n </div>\n <div class=\"right-side-buttons\">\n <div\n (click)=\"openQuickAdd()\"\n [ngClass]=\"{ open: supportOpen }\"\n class=\"clickable\"\n id=\"QuickAddButton\"\n matTooltip=\"Support\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"clicked\"></div>\n <div class=\"circle\"></div>\n <div class=\"icon icon-Icon_Add\"></div>\n </div>\n <div\n class=\"clickable\"\n id=\"AttributesButton\"\n matTooltip=\"Attributes Explorer\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n routerLink=\"/attributes/\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-hash\"></div>\n </div>\n <div\n (click)=\"openAlerts()\"\n class=\"clickable\"\n id=\"RecentAlertsButton\"\n matTooltip=\"Recent Alerts\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-Bell\"></div>\n </div>\n <div\n class=\"clickable main\"\n id=\"UserButton\"\n (clickOutside)=\"closeProfileMenu()\"\n (click)=\"openProfileMenu()\"\n >\n <div class=\"main clickable\"></div>\n <div class=\"circle\"></div>\n <div\n [ngStyle]=\"{ 'background-image': 'url(' + profilePhoto + ')' }\"\n class=\"icon icon-profile\"\n ></div>\n <div [ngClass]=\"{ open: profileOpen }\" class=\"header-menu\" id=\"UserMenu\">\n @if (profileOpen) {\n <div class=\"menutitle\">{{ username }}</div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"EditProfile\"\n routerLink=\"/profile/\"\n >\n Edit Profile\n </div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"PersonalSettings\"\n routerLink=\"/settings/\"\n >\n Personal Settings\n </div>\n }\n @if (profileOpen) {\n <div (click)=\"confirmLogout()\" class=\"link\" id=\"LogoutButton\">Logout</div>\n }\n </div>\n </div>\n </div>\n</header>\n", styles: ["header{position:relative;top:0rem;left:0;bottom:unset;width:100%;height:var(--headerHeight);z-index:99999999;background:var(--formBackground);border-bottom-style:solid;border-bottom-color:var(--inputBorder);border-bottom-width:1px}header .overlay{width:100%;height:100%;position:absolute;z-index:1;opacity:1!important}header .left-side-buttons{position:absolute;top:0rem;left:0rem;right:0rem;width:50%;z-index:5;height:100%;padding-left:1.25rem;display:flex;align-items:center}header .left-side-buttons .apps-button .icon{width:2.25rem;height:2.25rem;position:absolute;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s;top:unset;left:unset;background-image:url(/assets/svgs/ziti-logo.svg)}header .left-side-buttons #NavigationButton{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton .status{height:1.375rem;width:1.5rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton .circle{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton.header-nav-icon{width:2.8125rem;height:2.75rem;cursor:pointer}header .left-side-buttons #NavigationButton.header-nav-icon .status{height:2.125rem;width:2.125rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton.header-nav-icon .circle{height:2.625rem;width:2.625rem;top:unset;left:unset}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .status{display:none}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .icon{background-image:url(/assets/svgs/icon.svg);display:flex;top:unset;left:unset;height:2.125rem;width:2.125rem}header .right-side-buttons{position:absolute;right:0rem;bottom:unset;left:unset;z-index:5;width:50%;height:100%;display:flex;flex-direction:row;justify-content:flex-end;align-items:center;padding-right:1.25rem}header .clickable{position:relative;clear:both;float:left;width:2.8125rem;height:3.125rem;display:flex;align-items:center;justify-content:center;margin:0;cursor:pointer;transition:var(--transition)}header .clickable .icon{width:1.6875rem;height:1.6875rem;position:absolute;left:.625rem;top:.75rem;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s}header .clickable .icon:active{transform:scale(.95);transition:.3s}header .clickable .icon.icon-Bell{font-weight:600}header .clickable .icon:before{color:var(--headerButtonColor)!important}header .clickable .icon-profile{font-size:1.7rem;font-weight:600;margin-top:.075rem}header .clickable .icon-Bell{top:.65rem;left:.575rem}header .clickable .main{opacity:1;width:2.3125rem;height:2.3125rem;left:.5625rem;top:.5625rem;border-radius:2.3125rem;position:absolute;transition:var(--transition)}header .clickable .circle{opacity:0;z-index:-2;background-color:var(--white);width:2.8125rem;height:2.8125rem;left:1px;top:.1875rem;border-radius:2.0625rem;position:absolute;transition:var(--transition)}header .clickable.open .circle{opacity:.2}header .clickable:hover .circle{opacity:.2;background-color:var(--menu)}header .clickable:active .circle{opacity:.1}header .divider-line{height:2.8125rem;width:0;margin-right:.625rem;margin-left:.625rem;border-style:solid;border-width:1px;border-color:var(--inputBorder)}header .header-title-wrapper{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;height:100%;padding-top:.875rem;padding-bottom:.75rem}header .header-title-wrapper .header-title-container{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:0;height:100%;cursor:pointer}header .header-title-wrapper .header-title-container .header-title-label{font-family:Russo One;text-align:center;line-height:1.5rem;color:var(--primaryColor);filter:brightness(var(--brightnessDark));cursor:pointer;font-size:1.75rem;font-variant:small-caps;font-size-adjust:.46;word-spacing:-4px}header .header-breadcrumb{display:flex;align-items:center;justify-content:flex-start;height:100%}header .header-breadcrumb .breadcrumb-path-label{font-size:.875rem;font-weight:900;filter:brightness(var(--brightnessDark));color:var(--primary)}header .header-breadcrumb .breadcrumb-path-label.non-click{text-transform:capitalize}header .header-breadcrumb .breadcrumb-path-label:hover{text-decoration:underline;cursor:pointer}header .header-breadcrumb .breadcrumb-path-label:hover.non-click{text-decoration:none;cursor:default}header .header-breadcrumb .breadcrumb-path-divider{font-size:.875rem;font-weight:600;filter:brightness(var(--brightnessDark));text-transform:uppercase;color:var(--primary);margin-left:.3125rem;margin-right:.3125rem}header .icon{font-size:1.8125rem;text-align:center;color:#fff}header #ApplicationButton{width:3.125rem}header #ApplicationButton .icon{font-size:1.5625rem;height:2.3125rem;width:2.3125rem;display:flex;justify-content:center;align-items:center;border-radius:1.875rem;font-family:icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--primaryColor);color:var(--mainBackground);left:.4375rem;top:.4375rem}header #ApplicationButton .circle{left:.1875rem}header #ApplicationButton.nav-button-organization .icon:before{content:\"\\e948\"}header #ApplicationButton.nav-button-browzer .icon:before{content:\"\\e96a\"}header #ApplicationButton.nav-button-billing .icon:before{content:\"\\e932\"}#AlertButton .count{width:.8125rem;height:.8125rem;border-radius:50%;font-weight:400;font-family:Open Sans,Arial,sans-serif;background-color:var(--secondaryColor);z-index:20;color:var(--white);font-size:.5rem;line-height:.75rem;font-weight:700;position:absolute;right:.8125rem;top:.9375rem;text-align:center;box-shadow:var(--shadows)}#UserButton #UserMenu{position:absolute;top:2.5rem;right:.3125rem;bottom:unset;left:unset}#UserButton #UserMenu .menutitle{color:var(--lightText)}#UserButton #UserMenu .link{color:#fff}#UserButton #UserMenu.header-menu{background-color:var(--menu);border-radius:.4375rem;box-shadow:0 0 0 1px var(--shadow),0 .5rem 1rem var(--shadow);font-size:.875rem;font-weight:600;text-transform:uppercase;color:var(--white);letter-spacing:1px;height:0rem;opacity:0;transition:.2s;z-index:50;padding-bottom:.625rem;padding-top:.625rem}#UserButton #UserMenu.header-menu .link{cursor:pointer;line-height:2.25rem;white-space:nowrap;padding-left:1.25rem;padding-right:1.25rem;transition:.2s}#UserButton #UserMenu.header-menu .link:hover{background-color:var(--primaryColor)}#UserButton #UserMenu.header-menu.open{height:unset;opacity:1}#UserButton #UserMenu .menutitle{opacity:.8;white-space:nowrap;line-height:2.25rem;padding-left:1.25rem;padding-right:1.25rem}.title-text-row{color:var(--primaryColor)!important;filter:brightness(var(--brightnessDark));opacity:1;font-family:Russo One,Arial,sans-serif;font-size:3rem;font-weight:400!important;margin:0!important;text-align:center}.title-text-row.title-top-text{font-size:4.25rem!important;line-height:3.25rem;font-variant:small-caps;font-size-adjust:.47}.title-text-row.title-bottom-text{display:flex;justify-content:flex-start;align-items:baseline;gap:.175rem;font-size:1rem}.title-text-row.title-bottom-text .nf-name-text{font-variant:small-caps;font-size-adjust:.47;font-size:1.125rem;font-weight:400;color:var(--primaryColor)}.title-text-row.title-bottom-text .nf-name-text:hover{text-decoration:underline!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
42021
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: HeaderBarComponent, isStandalone: false, selector: "lib-header-bar", inputs: { headerTitle: "headerTitle" }, ngImport: i0, template: "<header>\n <div class=\"overlay\"></div>\n <div class=\"left-side-buttons\">\n <div class=\"apps-button clickable\" mattooltip=\"Select Application\" mattooltipposition=\"below\" mattooltipshowdelay=\"500\" (click)=\"goToDashboard()\">\n <div class=\"icon\"></div>\n </div>\n <div class=\"divider-line\"></div>\n <div class=\"header-title-wrapper\">\n <div class=\"header-title-container\" (click)=\"goToDashboard()\">\n <div class=\"header-title-label\">{{headerTitle}}</div>\n <div class=\"title-text-row title-bottom-text\">By <a class=\"nf-name-text\" href=\"https://netfoundry.io\" target=\"_blank\">NetFoundry</a></div>\n </div>\n </div>\n </div>\n <div class=\"right-side-buttons\">\n <div\n (click)=\"openQuickAdd()\"\n class=\"clickable\"\n id=\"QuickAddButton\"\n matTooltip=\"Add Entity\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"clicked\"></div>\n <div class=\"circle\"></div>\n <div class=\"icon icon-Icon_Add\"></div>\n </div>\n <div\n class=\"clickable\"\n id=\"AttributesButton\"\n matTooltip=\"Attributes Explorer\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n routerLink=\"/attributes/\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-hash\"></div>\n </div>\n <div\n (click)=\"openAlerts()\"\n class=\"clickable\"\n id=\"RecentAlertsButton\"\n matTooltip=\"Recent Alerts\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-Bell\"></div>\n </div>\n <div\n class=\"clickable main\"\n id=\"UserButton\"\n (clickOutside)=\"closeProfileMenu()\"\n (click)=\"openProfileMenu()\"\n >\n <div class=\"main clickable\"></div>\n <div class=\"circle\"></div>\n <div\n [ngStyle]=\"{ 'background-image': 'url(' + profilePhoto + ')' }\"\n class=\"icon icon-profile\"\n ></div>\n <div [ngClass]=\"{ open: profileOpen }\" class=\"header-menu\" id=\"UserMenu\">\n @if (profileOpen) {\n <div class=\"menutitle\">{{ username }}</div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"EditProfile\"\n routerLink=\"/profile/\"\n >\n Edit Profile\n </div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"PersonalSettings\"\n routerLink=\"/settings/\"\n >\n Personal Settings\n </div>\n }\n @if (profileOpen) {\n <div (click)=\"confirmLogout()\" class=\"link\" id=\"LogoutButton\">Logout</div>\n }\n </div>\n </div>\n </div>\n</header>\n", styles: ["header{position:relative;top:0rem;left:0;bottom:unset;width:100%;height:var(--headerHeight);z-index:99999999;background:var(--formBackground);border-bottom-style:solid;border-bottom-color:var(--inputBorder);border-bottom-width:1px}header .overlay{width:100%;height:100%;position:absolute;z-index:1;opacity:1!important}header .left-side-buttons{position:absolute;top:0rem;left:0rem;right:0rem;width:50%;z-index:5;height:100%;padding-left:1.25rem;display:flex;align-items:center}header .left-side-buttons .apps-button .icon{width:2.25rem;height:2.25rem;position:absolute;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s;top:unset;left:unset;background-image:url(/assets/svgs/ziti-logo.svg)}header .left-side-buttons #NavigationButton{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton .status{height:1.375rem;width:1.5rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton .circle{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton.header-nav-icon{width:2.8125rem;height:2.75rem;cursor:pointer}header .left-side-buttons #NavigationButton.header-nav-icon .status{height:2.125rem;width:2.125rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton.header-nav-icon .circle{height:2.625rem;width:2.625rem;top:unset;left:unset}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .status{display:none}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .icon{background-image:url(/assets/svgs/icon.svg);display:flex;top:unset;left:unset;height:2.125rem;width:2.125rem}header .right-side-buttons{position:absolute;right:0rem;bottom:unset;left:unset;z-index:5;width:50%;height:100%;display:flex;flex-direction:row;justify-content:flex-end;align-items:center;padding-right:1.25rem}header .clickable{position:relative;clear:both;float:left;width:2.8125rem;height:3.125rem;display:flex;align-items:center;justify-content:center;margin:0;cursor:pointer;transition:var(--transition)}header .clickable .icon{width:1.6875rem;height:1.6875rem;position:absolute;left:.625rem;top:.75rem;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s}header .clickable .icon:active{transform:scale(.95);transition:.3s}header .clickable .icon.icon-Bell{font-weight:600}header .clickable .icon:before{color:var(--headerButtonColor)!important}header .clickable .icon-profile{font-size:1.7rem;font-weight:600;margin-top:.075rem}header .clickable .icon-Bell{top:.65rem;left:.575rem}header .clickable .main{opacity:1;width:2.3125rem;height:2.3125rem;left:.5625rem;top:.5625rem;border-radius:2.3125rem;position:absolute;transition:var(--transition)}header .clickable .circle{opacity:0;z-index:-2;background-color:var(--white);width:2.8125rem;height:2.8125rem;left:1px;top:.1875rem;border-radius:2.0625rem;position:absolute;transition:var(--transition)}header .clickable.open .circle{opacity:.2}header .clickable:hover .circle{opacity:.2;background-color:var(--menu)}header .clickable:active .circle{opacity:.1}header .divider-line{height:2.8125rem;width:0;margin-right:.625rem;margin-left:.625rem;border-style:solid;border-width:1px;border-color:var(--inputBorder)}header .header-title-wrapper{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;height:100%;padding-top:.875rem;padding-bottom:.75rem}header .header-title-wrapper .header-title-container{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:0;height:100%;cursor:pointer}header .header-title-wrapper .header-title-container .header-title-label{font-family:Russo One;text-align:center;line-height:1.5rem;color:var(--primaryColor);filter:brightness(var(--brightnessDark));cursor:pointer;font-size:1.75rem;font-variant:small-caps;font-size-adjust:.46;word-spacing:-4px}header .header-breadcrumb{display:flex;align-items:center;justify-content:flex-start;height:100%}header .header-breadcrumb .breadcrumb-path-label{font-size:.875rem;font-weight:900;filter:brightness(var(--brightnessDark));color:var(--primary)}header .header-breadcrumb .breadcrumb-path-label.non-click{text-transform:capitalize}header .header-breadcrumb .breadcrumb-path-label:hover{text-decoration:underline;cursor:pointer}header .header-breadcrumb .breadcrumb-path-label:hover.non-click{text-decoration:none;cursor:default}header .header-breadcrumb .breadcrumb-path-divider{font-size:.875rem;font-weight:600;filter:brightness(var(--brightnessDark));text-transform:uppercase;color:var(--primary);margin-left:.3125rem;margin-right:.3125rem}header .icon{font-size:1.8125rem;text-align:center;color:#fff}header #ApplicationButton{width:3.125rem}header #ApplicationButton .icon{font-size:1.5625rem;height:2.3125rem;width:2.3125rem;display:flex;justify-content:center;align-items:center;border-radius:1.875rem;font-family:icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--primaryColor);color:var(--mainBackground);left:.4375rem;top:.4375rem}header #ApplicationButton .circle{left:.1875rem}header #ApplicationButton.nav-button-organization .icon:before{content:\"\\e948\"}header #ApplicationButton.nav-button-browzer .icon:before{content:\"\\e96a\"}header #ApplicationButton.nav-button-billing .icon:before{content:\"\\e932\"}#AlertButton .count{width:.8125rem;height:.8125rem;border-radius:50%;font-weight:400;font-family:Open Sans,Arial,sans-serif;background-color:var(--secondaryColor);z-index:20;color:var(--white);font-size:.5rem;line-height:.75rem;font-weight:700;position:absolute;right:.8125rem;top:.9375rem;text-align:center;box-shadow:var(--shadows)}#UserButton #UserMenu{position:absolute;top:2.5rem;right:.3125rem;bottom:unset;left:unset}#UserButton #UserMenu .menutitle{color:var(--lightText)}#UserButton #UserMenu .link{color:#fff}#UserButton #UserMenu.header-menu{background-color:var(--menu);border-radius:.4375rem;box-shadow:0 0 0 1px var(--shadow),0 .5rem 1rem var(--shadow);font-size:.875rem;font-weight:600;text-transform:uppercase;color:var(--white);letter-spacing:1px;height:0rem;opacity:0;transition:.2s;z-index:50;padding-bottom:.625rem;padding-top:.625rem}#UserButton #UserMenu.header-menu .link{cursor:pointer;line-height:2.25rem;white-space:nowrap;padding-left:1.25rem;padding-right:1.25rem;transition:.2s}#UserButton #UserMenu.header-menu .link:hover{background-color:var(--primaryColor)}#UserButton #UserMenu.header-menu.open{height:unset;opacity:1}#UserButton #UserMenu .menutitle{opacity:.8;white-space:nowrap;line-height:2.25rem;padding-left:1.25rem;padding-right:1.25rem}.title-text-row{color:var(--primaryColor)!important;filter:brightness(var(--brightnessDark));opacity:1;font-family:Russo One,Arial,sans-serif;font-size:3rem;font-weight:400!important;margin:0!important;text-align:center}.title-text-row.title-top-text{font-size:4.25rem!important;line-height:3.25rem;font-variant:small-caps;font-size-adjust:.47}.title-text-row.title-bottom-text{display:flex;justify-content:flex-start;align-items:baseline;gap:.175rem;font-size:1rem}.title-text-row.title-bottom-text .nf-name-text{font-variant:small-caps;font-size-adjust:.47;font-size:1.125rem;font-weight:400;color:var(--primaryColor)}.title-text-row.title-bottom-text .nf-name-text:hover{text-decoration:underline!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i4.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i3$1.ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutsideEnabled", "attachOutsideOnClick", "delayClickOutsideInit", "emitOnBlur", "exclude", "excludeBeforeClick", "clickOutsideEvents"], outputs: ["clickOutside"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
39496
42022
  }
39497
42023
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: HeaderBarComponent, decorators: [{
39498
42024
  type: Component,
39499
- args: [{ selector: 'lib-header-bar', standalone: false, template: "<header>\n <div class=\"overlay\"></div>\n <div class=\"left-side-buttons\">\n <div class=\"apps-button clickable\" mattooltip=\"Select Application\" mattooltipposition=\"below\" mattooltipshowdelay=\"500\" (click)=\"goToDashboard()\">\n <div class=\"icon\"></div>\n </div>\n <div class=\"divider-line\"></div>\n <div class=\"header-title-wrapper\">\n <div class=\"header-title-container\" (click)=\"goToDashboard()\">\n <div class=\"header-title-label\">{{headerTitle}}</div>\n <div class=\"title-text-row title-bottom-text\">By <a class=\"nf-name-text\" href=\"https://netfoundry.io\" target=\"_blank\">NetFoundry</a></div>\n </div>\n </div>\n </div>\n <div class=\"right-side-buttons\">\n <div\n (click)=\"openQuickAdd()\"\n [ngClass]=\"{ open: supportOpen }\"\n class=\"clickable\"\n id=\"QuickAddButton\"\n matTooltip=\"Support\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"clicked\"></div>\n <div class=\"circle\"></div>\n <div class=\"icon icon-Icon_Add\"></div>\n </div>\n <div\n class=\"clickable\"\n id=\"AttributesButton\"\n matTooltip=\"Attributes Explorer\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n routerLink=\"/attributes/\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-hash\"></div>\n </div>\n <div\n (click)=\"openAlerts()\"\n class=\"clickable\"\n id=\"RecentAlertsButton\"\n matTooltip=\"Recent Alerts\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-Bell\"></div>\n </div>\n <div\n class=\"clickable main\"\n id=\"UserButton\"\n (clickOutside)=\"closeProfileMenu()\"\n (click)=\"openProfileMenu()\"\n >\n <div class=\"main clickable\"></div>\n <div class=\"circle\"></div>\n <div\n [ngStyle]=\"{ 'background-image': 'url(' + profilePhoto + ')' }\"\n class=\"icon icon-profile\"\n ></div>\n <div [ngClass]=\"{ open: profileOpen }\" class=\"header-menu\" id=\"UserMenu\">\n @if (profileOpen) {\n <div class=\"menutitle\">{{ username }}</div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"EditProfile\"\n routerLink=\"/profile/\"\n >\n Edit Profile\n </div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"PersonalSettings\"\n routerLink=\"/settings/\"\n >\n Personal Settings\n </div>\n }\n @if (profileOpen) {\n <div (click)=\"confirmLogout()\" class=\"link\" id=\"LogoutButton\">Logout</div>\n }\n </div>\n </div>\n </div>\n</header>\n", styles: ["header{position:relative;top:0rem;left:0;bottom:unset;width:100%;height:var(--headerHeight);z-index:99999999;background:var(--formBackground);border-bottom-style:solid;border-bottom-color:var(--inputBorder);border-bottom-width:1px}header .overlay{width:100%;height:100%;position:absolute;z-index:1;opacity:1!important}header .left-side-buttons{position:absolute;top:0rem;left:0rem;right:0rem;width:50%;z-index:5;height:100%;padding-left:1.25rem;display:flex;align-items:center}header .left-side-buttons .apps-button .icon{width:2.25rem;height:2.25rem;position:absolute;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s;top:unset;left:unset;background-image:url(/assets/svgs/ziti-logo.svg)}header .left-side-buttons #NavigationButton{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton .status{height:1.375rem;width:1.5rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton .circle{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton.header-nav-icon{width:2.8125rem;height:2.75rem;cursor:pointer}header .left-side-buttons #NavigationButton.header-nav-icon .status{height:2.125rem;width:2.125rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton.header-nav-icon .circle{height:2.625rem;width:2.625rem;top:unset;left:unset}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .status{display:none}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .icon{background-image:url(/assets/svgs/icon.svg);display:flex;top:unset;left:unset;height:2.125rem;width:2.125rem}header .right-side-buttons{position:absolute;right:0rem;bottom:unset;left:unset;z-index:5;width:50%;height:100%;display:flex;flex-direction:row;justify-content:flex-end;align-items:center;padding-right:1.25rem}header .clickable{position:relative;clear:both;float:left;width:2.8125rem;height:3.125rem;display:flex;align-items:center;justify-content:center;margin:0;cursor:pointer;transition:var(--transition)}header .clickable .icon{width:1.6875rem;height:1.6875rem;position:absolute;left:.625rem;top:.75rem;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s}header .clickable .icon:active{transform:scale(.95);transition:.3s}header .clickable .icon.icon-Bell{font-weight:600}header .clickable .icon:before{color:var(--headerButtonColor)!important}header .clickable .icon-profile{font-size:1.7rem;font-weight:600;margin-top:.075rem}header .clickable .icon-Bell{top:.65rem;left:.575rem}header .clickable .main{opacity:1;width:2.3125rem;height:2.3125rem;left:.5625rem;top:.5625rem;border-radius:2.3125rem;position:absolute;transition:var(--transition)}header .clickable .circle{opacity:0;z-index:-2;background-color:var(--white);width:2.8125rem;height:2.8125rem;left:1px;top:.1875rem;border-radius:2.0625rem;position:absolute;transition:var(--transition)}header .clickable.open .circle{opacity:.2}header .clickable:hover .circle{opacity:.2;background-color:var(--menu)}header .clickable:active .circle{opacity:.1}header .divider-line{height:2.8125rem;width:0;margin-right:.625rem;margin-left:.625rem;border-style:solid;border-width:1px;border-color:var(--inputBorder)}header .header-title-wrapper{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;height:100%;padding-top:.875rem;padding-bottom:.75rem}header .header-title-wrapper .header-title-container{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:0;height:100%;cursor:pointer}header .header-title-wrapper .header-title-container .header-title-label{font-family:Russo One;text-align:center;line-height:1.5rem;color:var(--primaryColor);filter:brightness(var(--brightnessDark));cursor:pointer;font-size:1.75rem;font-variant:small-caps;font-size-adjust:.46;word-spacing:-4px}header .header-breadcrumb{display:flex;align-items:center;justify-content:flex-start;height:100%}header .header-breadcrumb .breadcrumb-path-label{font-size:.875rem;font-weight:900;filter:brightness(var(--brightnessDark));color:var(--primary)}header .header-breadcrumb .breadcrumb-path-label.non-click{text-transform:capitalize}header .header-breadcrumb .breadcrumb-path-label:hover{text-decoration:underline;cursor:pointer}header .header-breadcrumb .breadcrumb-path-label:hover.non-click{text-decoration:none;cursor:default}header .header-breadcrumb .breadcrumb-path-divider{font-size:.875rem;font-weight:600;filter:brightness(var(--brightnessDark));text-transform:uppercase;color:var(--primary);margin-left:.3125rem;margin-right:.3125rem}header .icon{font-size:1.8125rem;text-align:center;color:#fff}header #ApplicationButton{width:3.125rem}header #ApplicationButton .icon{font-size:1.5625rem;height:2.3125rem;width:2.3125rem;display:flex;justify-content:center;align-items:center;border-radius:1.875rem;font-family:icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--primaryColor);color:var(--mainBackground);left:.4375rem;top:.4375rem}header #ApplicationButton .circle{left:.1875rem}header #ApplicationButton.nav-button-organization .icon:before{content:\"\\e948\"}header #ApplicationButton.nav-button-browzer .icon:before{content:\"\\e96a\"}header #ApplicationButton.nav-button-billing .icon:before{content:\"\\e932\"}#AlertButton .count{width:.8125rem;height:.8125rem;border-radius:50%;font-weight:400;font-family:Open Sans,Arial,sans-serif;background-color:var(--secondaryColor);z-index:20;color:var(--white);font-size:.5rem;line-height:.75rem;font-weight:700;position:absolute;right:.8125rem;top:.9375rem;text-align:center;box-shadow:var(--shadows)}#UserButton #UserMenu{position:absolute;top:2.5rem;right:.3125rem;bottom:unset;left:unset}#UserButton #UserMenu .menutitle{color:var(--lightText)}#UserButton #UserMenu .link{color:#fff}#UserButton #UserMenu.header-menu{background-color:var(--menu);border-radius:.4375rem;box-shadow:0 0 0 1px var(--shadow),0 .5rem 1rem var(--shadow);font-size:.875rem;font-weight:600;text-transform:uppercase;color:var(--white);letter-spacing:1px;height:0rem;opacity:0;transition:.2s;z-index:50;padding-bottom:.625rem;padding-top:.625rem}#UserButton #UserMenu.header-menu .link{cursor:pointer;line-height:2.25rem;white-space:nowrap;padding-left:1.25rem;padding-right:1.25rem;transition:.2s}#UserButton #UserMenu.header-menu .link:hover{background-color:var(--primaryColor)}#UserButton #UserMenu.header-menu.open{height:unset;opacity:1}#UserButton #UserMenu .menutitle{opacity:.8;white-space:nowrap;line-height:2.25rem;padding-left:1.25rem;padding-right:1.25rem}.title-text-row{color:var(--primaryColor)!important;filter:brightness(var(--brightnessDark));opacity:1;font-family:Russo One,Arial,sans-serif;font-size:3rem;font-weight:400!important;margin:0!important;text-align:center}.title-text-row.title-top-text{font-size:4.25rem!important;line-height:3.25rem;font-variant:small-caps;font-size-adjust:.47}.title-text-row.title-bottom-text{display:flex;justify-content:flex-start;align-items:baseline;gap:.175rem;font-size:1rem}.title-text-row.title-bottom-text .nf-name-text{font-variant:small-caps;font-size-adjust:.47;font-size:1.125rem;font-weight:400;color:var(--primaryColor)}.title-text-row.title-bottom-text .nf-name-text:hover{text-decoration:underline!important}\n"] }]
42025
+ args: [{ selector: 'lib-header-bar', standalone: false, template: "<header>\n <div class=\"overlay\"></div>\n <div class=\"left-side-buttons\">\n <div class=\"apps-button clickable\" mattooltip=\"Select Application\" mattooltipposition=\"below\" mattooltipshowdelay=\"500\" (click)=\"goToDashboard()\">\n <div class=\"icon\"></div>\n </div>\n <div class=\"divider-line\"></div>\n <div class=\"header-title-wrapper\">\n <div class=\"header-title-container\" (click)=\"goToDashboard()\">\n <div class=\"header-title-label\">{{headerTitle}}</div>\n <div class=\"title-text-row title-bottom-text\">By <a class=\"nf-name-text\" href=\"https://netfoundry.io\" target=\"_blank\">NetFoundry</a></div>\n </div>\n </div>\n </div>\n <div class=\"right-side-buttons\">\n <div\n (click)=\"openQuickAdd()\"\n class=\"clickable\"\n id=\"QuickAddButton\"\n matTooltip=\"Add Entity\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"clicked\"></div>\n <div class=\"circle\"></div>\n <div class=\"icon icon-Icon_Add\"></div>\n </div>\n <div\n class=\"clickable\"\n id=\"AttributesButton\"\n matTooltip=\"Attributes Explorer\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n routerLink=\"/attributes/\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-hash\"></div>\n </div>\n <div\n (click)=\"openAlerts()\"\n class=\"clickable\"\n id=\"RecentAlertsButton\"\n matTooltip=\"Recent Alerts\"\n matTooltipPosition=\"below\"\n matTooltipShowDelay=\"500\"\n >\n <div class=\"circle\"></div>\n <div class=\"icon icon-Bell\"></div>\n </div>\n <div\n class=\"clickable main\"\n id=\"UserButton\"\n (clickOutside)=\"closeProfileMenu()\"\n (click)=\"openProfileMenu()\"\n >\n <div class=\"main clickable\"></div>\n <div class=\"circle\"></div>\n <div\n [ngStyle]=\"{ 'background-image': 'url(' + profilePhoto + ')' }\"\n class=\"icon icon-profile\"\n ></div>\n <div [ngClass]=\"{ open: profileOpen }\" class=\"header-menu\" id=\"UserMenu\">\n @if (profileOpen) {\n <div class=\"menutitle\">{{ username }}</div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"EditProfile\"\n routerLink=\"/profile/\"\n >\n Edit Profile\n </div>\n }\n @if (profileOpen) {\n <div\n class=\"link\"\n id=\"PersonalSettings\"\n routerLink=\"/settings/\"\n >\n Personal Settings\n </div>\n }\n @if (profileOpen) {\n <div (click)=\"confirmLogout()\" class=\"link\" id=\"LogoutButton\">Logout</div>\n }\n </div>\n </div>\n </div>\n</header>\n", styles: ["header{position:relative;top:0rem;left:0;bottom:unset;width:100%;height:var(--headerHeight);z-index:99999999;background:var(--formBackground);border-bottom-style:solid;border-bottom-color:var(--inputBorder);border-bottom-width:1px}header .overlay{width:100%;height:100%;position:absolute;z-index:1;opacity:1!important}header .left-side-buttons{position:absolute;top:0rem;left:0rem;right:0rem;width:50%;z-index:5;height:100%;padding-left:1.25rem;display:flex;align-items:center}header .left-side-buttons .apps-button .icon{width:2.25rem;height:2.25rem;position:absolute;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s;top:unset;left:unset;background-image:url(/assets/svgs/ziti-logo.svg)}header .left-side-buttons #NavigationButton{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton .status{height:1.375rem;width:1.5rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton .circle{height:1.5rem;width:1.625rem}header .left-side-buttons #NavigationButton.header-nav-icon{width:2.8125rem;height:2.75rem;cursor:pointer}header .left-side-buttons #NavigationButton.header-nav-icon .status{height:2.125rem;width:2.125rem;top:unset;left:unset;animation:none}header .left-side-buttons #NavigationButton.header-nav-icon .circle{height:2.625rem;width:2.625rem;top:unset;left:unset}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .status{display:none}header .left-side-buttons #NavigationButton.header-nav-icon.nav-logo .icon{background-image:url(/assets/svgs/icon.svg);display:flex;top:unset;left:unset;height:2.125rem;width:2.125rem}header .right-side-buttons{position:absolute;right:0rem;bottom:unset;left:unset;z-index:5;width:50%;height:100%;display:flex;flex-direction:row;justify-content:flex-end;align-items:center;padding-right:1.25rem}header .clickable{position:relative;clear:both;float:left;width:2.8125rem;height:3.125rem;display:flex;align-items:center;justify-content:center;margin:0;cursor:pointer;transition:var(--transition)}header .clickable .icon{width:1.6875rem;height:1.6875rem;position:absolute;left:.625rem;top:.75rem;background-size:contain;background-repeat:no-repeat;background-position:center center;transition:.3s}header .clickable .icon:active{transform:scale(.95);transition:.3s}header .clickable .icon.icon-Bell{font-weight:600}header .clickable .icon:before{color:var(--headerButtonColor)!important}header .clickable .icon-profile{font-size:1.7rem;font-weight:600;margin-top:.075rem}header .clickable .icon-Bell{top:.65rem;left:.575rem}header .clickable .main{opacity:1;width:2.3125rem;height:2.3125rem;left:.5625rem;top:.5625rem;border-radius:2.3125rem;position:absolute;transition:var(--transition)}header .clickable .circle{opacity:0;z-index:-2;background-color:var(--white);width:2.8125rem;height:2.8125rem;left:1px;top:.1875rem;border-radius:2.0625rem;position:absolute;transition:var(--transition)}header .clickable.open .circle{opacity:.2}header .clickable:hover .circle{opacity:.2;background-color:var(--menu)}header .clickable:active .circle{opacity:.1}header .divider-line{height:2.8125rem;width:0;margin-right:.625rem;margin-left:.625rem;border-style:solid;border-width:1px;border-color:var(--inputBorder)}header .header-title-wrapper{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;height:100%;padding-top:.875rem;padding-bottom:.75rem}header .header-title-wrapper .header-title-container{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:0;height:100%;cursor:pointer}header .header-title-wrapper .header-title-container .header-title-label{font-family:Russo One;text-align:center;line-height:1.5rem;color:var(--primaryColor);filter:brightness(var(--brightnessDark));cursor:pointer;font-size:1.75rem;font-variant:small-caps;font-size-adjust:.46;word-spacing:-4px}header .header-breadcrumb{display:flex;align-items:center;justify-content:flex-start;height:100%}header .header-breadcrumb .breadcrumb-path-label{font-size:.875rem;font-weight:900;filter:brightness(var(--brightnessDark));color:var(--primary)}header .header-breadcrumb .breadcrumb-path-label.non-click{text-transform:capitalize}header .header-breadcrumb .breadcrumb-path-label:hover{text-decoration:underline;cursor:pointer}header .header-breadcrumb .breadcrumb-path-label:hover.non-click{text-decoration:none;cursor:default}header .header-breadcrumb .breadcrumb-path-divider{font-size:.875rem;font-weight:600;filter:brightness(var(--brightnessDark));text-transform:uppercase;color:var(--primary);margin-left:.3125rem;margin-right:.3125rem}header .icon{font-size:1.8125rem;text-align:center;color:#fff}header #ApplicationButton{width:3.125rem}header #ApplicationButton .icon{font-size:1.5625rem;height:2.3125rem;width:2.3125rem;display:flex;justify-content:center;align-items:center;border-radius:1.875rem;font-family:icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--primaryColor);color:var(--mainBackground);left:.4375rem;top:.4375rem}header #ApplicationButton .circle{left:.1875rem}header #ApplicationButton.nav-button-organization .icon:before{content:\"\\e948\"}header #ApplicationButton.nav-button-browzer .icon:before{content:\"\\e96a\"}header #ApplicationButton.nav-button-billing .icon:before{content:\"\\e932\"}#AlertButton .count{width:.8125rem;height:.8125rem;border-radius:50%;font-weight:400;font-family:Open Sans,Arial,sans-serif;background-color:var(--secondaryColor);z-index:20;color:var(--white);font-size:.5rem;line-height:.75rem;font-weight:700;position:absolute;right:.8125rem;top:.9375rem;text-align:center;box-shadow:var(--shadows)}#UserButton #UserMenu{position:absolute;top:2.5rem;right:.3125rem;bottom:unset;left:unset}#UserButton #UserMenu .menutitle{color:var(--lightText)}#UserButton #UserMenu .link{color:#fff}#UserButton #UserMenu.header-menu{background-color:var(--menu);border-radius:.4375rem;box-shadow:0 0 0 1px var(--shadow),0 .5rem 1rem var(--shadow);font-size:.875rem;font-weight:600;text-transform:uppercase;color:var(--white);letter-spacing:1px;height:0rem;opacity:0;transition:.2s;z-index:50;padding-bottom:.625rem;padding-top:.625rem}#UserButton #UserMenu.header-menu .link{cursor:pointer;line-height:2.25rem;white-space:nowrap;padding-left:1.25rem;padding-right:1.25rem;transition:.2s}#UserButton #UserMenu.header-menu .link:hover{background-color:var(--primaryColor)}#UserButton #UserMenu.header-menu.open{height:unset;opacity:1}#UserButton #UserMenu .menutitle{opacity:.8;white-space:nowrap;line-height:2.25rem;padding-left:1.25rem;padding-right:1.25rem}.title-text-row{color:var(--primaryColor)!important;filter:brightness(var(--brightnessDark));opacity:1;font-family:Russo One,Arial,sans-serif;font-size:3rem;font-weight:400!important;margin:0!important;text-align:center}.title-text-row.title-top-text{font-size:4.25rem!important;line-height:3.25rem;font-variant:small-caps;font-size-adjust:.47}.title-text-row.title-bottom-text{display:flex;justify-content:flex-start;align-items:baseline;gap:.175rem;font-size:1rem}.title-text-row.title-bottom-text .nf-name-text{font-variant:small-caps;font-size-adjust:.47;font-size:1.125rem;font-weight:400;color:var(--primaryColor)}.title-text-row.title-bottom-text .nf-name-text:hover{text-decoration:underline!important}\n"] }]
39500
42026
  }], ctorParameters: () => [{ type: i4.Router }, { type: i5.MatDialog }, { type: LoginServiceClass, decorators: [{
39501
42027
  type: Inject,
39502
42028
  args: [ZAC_LOGIN_SERVICE]
@@ -39542,12 +42068,24 @@ class OpenZitiConsoleLibModule {
39542
42068
  ConfigTypesPageComponent,
39543
42069
  ConfigTypeFormComponent,
39544
42070
  ListPageHeaderComponent,
42071
+ ListTableHeaderComponent,
39545
42072
  FilterBarComponent,
39546
42073
  ListPageFormComponent,
39547
42074
  IdentitiesPageComponent,
42075
+ IdentitiesListPageComponent,
39548
42076
  EdgeRoutersPageComponent,
39549
42077
  TransitRoutersPageComponent,
39550
42078
  DataTableComponent,
42079
+ ListTableComponent,
42080
+ ListMenuComponent,
42081
+ NameCellComponent,
42082
+ SelectCellComponent,
42083
+ ListHeaderCellComponent,
42084
+ ListFilterCellComponent,
42085
+ OsInfoTooltipComponent,
42086
+ SdkInfoTooltipComponent,
42087
+ BooleanCellComponent,
42088
+ TokenCellComponent,
39551
42089
  TableCellSelectComponent,
39552
42090
  TableColumnSelectComponent,
39553
42091
  TableCellMenuComponent,
@@ -39650,6 +42188,7 @@ class OpenZitiConsoleLibModule {
39650
42188
  MatButtonModule,
39651
42189
  MatIconModule,
39652
42190
  OverlayModule,
42191
+ DragDropModule,
39653
42192
  AgGridModule,
39654
42193
  QRCodeComponent,
39655
42194
  ClickOutsideModule,
@@ -39678,6 +42217,7 @@ class OpenZitiConsoleLibModule {
39678
42217
  EdgeRouterFormComponent,
39679
42218
  TransitRouterFormComponent,
39680
42219
  IdentitiesPageComponent,
42220
+ IdentitiesListPageComponent,
39681
42221
  EdgeRoutersPageComponent,
39682
42222
  TransitRoutersPageComponent,
39683
42223
  ServicesPageComponent,
@@ -39721,7 +42261,9 @@ class OpenZitiConsoleLibModule {
39721
42261
  SideNavigatorComponent,
39722
42262
  FilterSelectComponent,
39723
42263
  FilterSelectOptionTemplateDirective,
39724
- FilterSelectTriggerTemplateDirective] }); }
42264
+ FilterSelectTriggerTemplateDirective,
42265
+ ListTableComponent,
42266
+ ListTableHeaderComponent] }); }
39725
42267
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: OpenZitiConsoleLibModule, providers: [
39726
42268
  { provide: SHAREDZ_EXTENSION, useClass: ExtensionsNoopService },
39727
42269
  { provide: ZITI_NAVIGATOR, useValue: {} },
@@ -39746,6 +42288,7 @@ class OpenZitiConsoleLibModule {
39746
42288
  MatButtonModule,
39747
42289
  MatIconModule,
39748
42290
  OverlayModule,
42291
+ DragDropModule,
39749
42292
  AgGridModule,
39750
42293
  QRCodeComponent,
39751
42294
  ClickOutsideModule,
@@ -39777,12 +42320,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
39777
42320
  ConfigTypesPageComponent,
39778
42321
  ConfigTypeFormComponent,
39779
42322
  ListPageHeaderComponent,
42323
+ ListTableHeaderComponent,
39780
42324
  FilterBarComponent,
39781
42325
  ListPageFormComponent,
39782
42326
  IdentitiesPageComponent,
42327
+ IdentitiesListPageComponent,
39783
42328
  EdgeRoutersPageComponent,
39784
42329
  TransitRoutersPageComponent,
39785
42330
  DataTableComponent,
42331
+ ListTableComponent,
42332
+ ListMenuComponent,
42333
+ NameCellComponent,
42334
+ SelectCellComponent,
42335
+ ListHeaderCellComponent,
42336
+ ListFilterCellComponent,
42337
+ OsInfoTooltipComponent,
42338
+ SdkInfoTooltipComponent,
42339
+ BooleanCellComponent,
42340
+ TokenCellComponent,
39786
42341
  TableCellSelectComponent,
39787
42342
  TableColumnSelectComponent,
39788
42343
  TableCellMenuComponent,
@@ -39896,6 +42451,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
39896
42451
  EdgeRouterFormComponent,
39897
42452
  TransitRouterFormComponent,
39898
42453
  IdentitiesPageComponent,
42454
+ IdentitiesListPageComponent,
39899
42455
  EdgeRoutersPageComponent,
39900
42456
  TransitRoutersPageComponent,
39901
42457
  ServicesPageComponent,
@@ -39939,7 +42495,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
39939
42495
  SideNavigatorComponent,
39940
42496
  FilterSelectComponent,
39941
42497
  FilterSelectOptionTemplateDirective,
39942
- FilterSelectTriggerTemplateDirective
42498
+ FilterSelectTriggerTemplateDirective,
42499
+ ListTableComponent,
42500
+ ListTableHeaderComponent
39943
42501
  ], imports: [
39944
42502
  CommonModule,
39945
42503
  FormsModule,
@@ -39954,6 +42512,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
39954
42512
  MatButtonModule,
39955
42513
  MatIconModule,
39956
42514
  OverlayModule,
42515
+ DragDropModule,
39957
42516
  AgGridModule,
39958
42517
  QRCodeComponent,
39959
42518
  ClickOutsideModule,
@@ -40360,6 +42919,10 @@ class NodeDataService extends ZitiDataService {
40360
42919
  paging.rawFilter = true;
40361
42920
  filterVal = this.getAttributeFilter(filter.value, filter.columnId);
40362
42921
  break;
42922
+ case 'MULTISELECT':
42923
+ paging.rawFilter = true;
42924
+ filterVal = this.getMultiSelectFilter(filter.value, filter.columnId, filter.semantic);
42925
+ break;
40363
42926
  default:
40364
42927
  filterVal = `${filter.columnId} contains "${filter.value}"`;
40365
42928
  break;
@@ -40801,6 +43364,9 @@ class ZitiControllerDataService extends ZitiDataService {
40801
43364
  case 'ATTRIBUTE':
40802
43365
  filterVal = this.getAttributeFilter(filter.value, filter.columnId, filter.semantic);
40803
43366
  break;
43367
+ case 'MULTISELECT':
43368
+ filterVal = this.getMultiSelectFilter(filter.value, filter.columnId, filter.semantic);
43369
+ break;
40804
43370
  case 'BOOLEAN':
40805
43371
  filterVal = `${filter.columnId}=${filter.value}`;
40806
43372
  break;
@@ -41410,8 +43976,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
41410
43976
  args: [SETTINGS_SERVICE]
41411
43977
  }] }, { type: ZitiOidcService }, { type: GrowlerService }, { type: i0.Injector }] });
41412
43978
 
43979
+ /*
43980
+ Copyright NetFoundry Inc.
43981
+
43982
+ Licensed under the Apache License, Version 2.0 (the "License");
43983
+ you may not use this file except in compliance with the License.
43984
+ You may obtain a copy of the License at
43985
+
43986
+ https://www.apache.org/licenses/LICENSE-2.0
43987
+
43988
+ Unless required by applicable law or agreed to in writing, software
43989
+ distributed under the License is distributed on an "AS IS" BASIS,
43990
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43991
+ See the License for the specific language governing permissions and
43992
+ limitations under the License.
43993
+ */
43994
+
41413
43995
  const ZAC_VERSION = {
41414
- "version": "4.5.2"
43996
+ "version": "4.5.4"
41415
43997
  };
41416
43998
 
41417
43999
  /*
@@ -41422,5 +44004,5 @@ const ZAC_VERSION = {
41422
44004
  * Generated bundle index. Do not edit.
41423
44005
  */
41424
44006
 
41425
- export { APISessionFormComponent, APISessionsPageComponent, API_RESOURCE_TO_ENTITY_PERMISSION, ApiSessionsPageService, AttributesComponent, AuthPoliciesPageComponent, AuthPoliciesPageService, AuthPolicyFormComponent, AuthPolicyFormService, AuthService, BooleanToggleInputComponent, CallbackComponent, CardListComponent, CertificateAuthoritiesPageComponent, CertificateAuthoritiesPageService, CertificateAuthorityFormComponent, CertificateAuthorityFormService, CheckboxListInputComponent, ChipsInputComponent, ConfigEditorComponent, ConfigTypeFormComponent, ConfigTypesPageComponent, ConfigurationFormComponent, ConfigurationService, ConfigurationsPageComponent, CsvDownloadService, DEACTIVATE_GUARD, DEFAULT_APP_CONFIG, DataTableFilterService, DeactivateGuardService, EDGE_ROUTER_EXTENSION_SERVICE, EDGE_ROUTER_POLICY_EXTENSION_SERVICE, EdgeRouter, EdgeRouterFormComponent, EdgeRouterFormService, EdgeRouterPoliciesPageComponent, EdgeRouterPoliciesPageService, EdgeRouterPolicyFormComponent, EdgeRouterPolicyFormService, EdgeRoutersPageComponent, EdgeRoutersPageService, Entity, ExtendableComponent, ExtensionsNoopService, FilterSelectComponent, FilterSelectOptionTemplateDirective, FilterSelectTriggerTemplateDirective, FormFieldContainerComponent, FormFieldToggleComponent, FormHeaderComponent, GeolocateComponent, GrowlerComponent, GrowlerModel, GrowlerModule, GrowlerService, HAControllerService, HaStatusIndicatorComponent, HeaderBarComponent, IDENTITY_EXTENSION_SERVICE, IdentitiesPageComponent, IdentitiesPageService, Identity$1 as Identity, IdentityFormComponent, IdentityFormService, IdentityServicePathComponent, InsecureContextError, JsonViewComponent, JwtSignerFormComponent, JwtSignerFormService, JwtSignersPageComponent, JwtSignersPageService, KEY_CODES, ListPageComponent, ListPageServiceClass, LoadingIndicatorComponent, LoggerService, LoginDialogComponent, LoginServiceClass, ManagementPermissionsService, NetworkVisualizerComponent, NodeDataService, NoopLoginService, NoopTabInterceptorService, NumberInputComponent, OIDC_CLIENT_ID, OIDC_CLIENT_ID_LEGACY, ObjectComponent, OidcEnvironmentalError, OidcSessionUnrecoverableError, OpenZitiConsoleLibModule, PasswordInputComponent, PostureCheckFormComponent, PostureChecksPageComponent, PostureChecksPageService, PreviewListComponent, ProfileComponent, ProgressMeterComponent, ProgressMeterStep, ProgressWizardComponent, ProgressWizardStep, ProjectableForm, ProtocolAddressPortInputComponent, QuickAddComponent, ResetEnrollmentService, SAVE_DENIED_BY_PERMISSION_TOOLTIP, SERVICE_EDGE_ROUTER_POLICY_EXTENSION_SERVICE, SERVICE_EXTENSION_SERVICE, SERVICE_POLICY_EXTENSION_SERVICE, SETTINGS_SERVICE, SHAREDZ_EXTENSION, SelectorInputComponent, ServiceEdgeRouterPoliciesPageComponent, ServiceEdgeRouterPoliciesPageService, ServiceEdgeRouterPolicyFormComponent, ServiceEdgeRouterPolicyFormService, ServiceFormComponent, ServiceFormService, ServicePoliciesPageComponent, ServicePoliciesPageService, ServicePolicyFormComponent, ServicePolicyFormService, ServicesPageComponent, ServicesPageService, Session, SessionFormComponent, SessionRefreshService, SessionsPageComponent, SessionsPageService, SettingsComponent, SettingsService, SettingsServiceClass, SideModalComponent, SideNavbarComponent, SideNavigatorComponent, SimpleServiceComponent, StringInputComponent, TabNameService, TagSelectorComponent, TerminatorFormComponent, TerminatorFormService, TerminatorsPageComponent, TerminatorsPageService, TextListInputComponent, TransitRouterFormComponent, TransitRouterFormService, TransitRoutersPageComponent, TransitRoutersPageService, ValidationService, VerifyCertificateComponent, VisualizerServiceClass, ZAC_LOGIN_SERVICE, ZAC_VERSION, ZITI_DATA_SERVICE, ZITI_DOMAIN_CONTROLLER, ZITI_NAVIGATOR, ZITI_TAB_OVERRIDES, ZITI_URLS, ZacRoutingModule, ZitiControllerDataService, ZitiDataService, ZitiOidcService, decodeJwtPayload, jwtExpMs, jwtIatMs, pkceChallengeS256, playerFactory, randomUrlSafeString };
44007
+ export { APISessionFormComponent, APISessionsPageComponent, API_RESOURCE_TO_ENTITY_PERMISSION, ApiSessionsPageService, AttributesComponent, AuthPoliciesPageComponent, AuthPoliciesPageService, AuthPolicyFormComponent, AuthPolicyFormService, AuthService, BooleanCellComponent, BooleanToggleInputComponent, CallbackComponent, CardListComponent, CertificateAuthoritiesPageComponent, CertificateAuthoritiesPageService, CertificateAuthorityFormComponent, CertificateAuthorityFormService, CheckboxListInputComponent, ChipsInputComponent, ConfigEditorComponent, ConfigTypeFormComponent, ConfigTypesPageComponent, ConfigurationFormComponent, ConfigurationService, ConfigurationsPageComponent, CsvDownloadService, DEACTIVATE_GUARD, DEFAULT_APP_CONFIG, DataTableFilterService, DeactivateGuardService, EDGE_ROUTER_EXTENSION_SERVICE, EDGE_ROUTER_POLICY_EXTENSION_SERVICE, EdgeRouter, EdgeRouterFormComponent, EdgeRouterFormService, EdgeRouterPoliciesPageComponent, EdgeRouterPoliciesPageService, EdgeRouterPolicyFormComponent, EdgeRouterPolicyFormService, EdgeRoutersPageComponent, EdgeRoutersPageService, Entity, ExtendableComponent, ExtensionsNoopService, FilterSelectComponent, FilterSelectOptionTemplateDirective, FilterSelectTriggerTemplateDirective, FormFieldContainerComponent, FormFieldToggleComponent, FormHeaderComponent, GeolocateComponent, GrowlerComponent, GrowlerModel, GrowlerModule, GrowlerService, HAControllerService, HaStatusIndicatorComponent, HeaderBarComponent, IDENTITY_EXTENSION_SERVICE, IdentitiesListPageComponent, IdentitiesPageComponent, IdentitiesPageService, Identity$1 as Identity, IdentityFormComponent, IdentityFormService, IdentityServicePathComponent, InsecureContextError, JsonViewComponent, JwtSignerFormComponent, JwtSignerFormService, JwtSignersPageComponent, JwtSignersPageService, KEY_CODES, ListFilterCellComponent, ListHeaderCellComponent, ListMenuComponent, ListPageComponent, ListPageServiceClass, ListTableComponent, ListTableHeaderComponent, ListTableService, LoadingIndicatorComponent, LoggerService, LoginDialogComponent, LoginServiceClass, ManagementPermissionsService, NameCellComponent, NetworkVisualizerComponent, NodeDataService, NoopLoginService, NoopTabInterceptorService, NumberInputComponent, OIDC_CLIENT_ID, OIDC_CLIENT_ID_LEGACY, ObjectComponent, OidcEnvironmentalError, OidcSessionUnrecoverableError, OpenZitiConsoleLibModule, OsInfoTooltipComponent, PasswordInputComponent, PostureCheckFormComponent, PostureChecksPageComponent, PostureChecksPageService, PreviewListComponent, ProfileComponent, ProgressMeterComponent, ProgressMeterStep, ProgressWizardComponent, ProgressWizardStep, ProjectableForm, ProtocolAddressPortInputComponent, QuickAddComponent, ResetEnrollmentService, SAVE_DENIED_BY_PERMISSION_TOOLTIP, SERVICE_EDGE_ROUTER_POLICY_EXTENSION_SERVICE, SERVICE_EXTENSION_SERVICE, SERVICE_POLICY_EXTENSION_SERVICE, SETTINGS_SERVICE, SHAREDZ_EXTENSION, SdkInfoTooltipComponent, SelectCellComponent, SelectorInputComponent, ServiceEdgeRouterPoliciesPageComponent, ServiceEdgeRouterPoliciesPageService, ServiceEdgeRouterPolicyFormComponent, ServiceEdgeRouterPolicyFormService, ServiceFormComponent, ServiceFormService, ServicePoliciesPageComponent, ServicePoliciesPageService, ServicePolicyFormComponent, ServicePolicyFormService, ServicesPageComponent, ServicesPageService, Session, SessionFormComponent, SessionRefreshService, SessionsPageComponent, SessionsPageService, SettingsComponent, SettingsService, SettingsServiceClass, SideModalComponent, SideNavbarComponent, SideNavigatorComponent, SimpleServiceComponent, StringInputComponent, TabNameService, TagSelectorComponent, TerminatorFormComponent, TerminatorFormService, TerminatorsPageComponent, TerminatorsPageService, TextListInputComponent, TokenCellComponent, TransitRouterFormComponent, TransitRouterFormService, TransitRoutersPageComponent, TransitRoutersPageService, ValidationService, VerifyCertificateComponent, VisualizerServiceClass, ZAC_LOGIN_SERVICE, ZAC_VERSION, ZITI_DATA_SERVICE, ZITI_DOMAIN_CONTROLLER, ZITI_NAVIGATOR, ZITI_TAB_OVERRIDES, ZITI_URLS, ZacRoutingModule, ZitiControllerDataService, ZitiDataService, ZitiOidcService, decodeJwtPayload, jwtExpMs, jwtIatMs, pkceChallengeS256, playerFactory, randomUrlSafeString };
41426
44008
  //# sourceMappingURL=openziti-ziti-console-lib.mjs.map