@noatgnu/cupcake-vanilla 1.1.0 → 1.2.1

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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, signal, computed, EventEmitter, inject, Output, Input, Component, effect } from '@angular/core';
3
- import * as i3 from '@cupcake/core';
4
- import { BaseApiService, WebSocketService, NotificationService as NotificationService$1, LabGroupService, ToastService, ResourceVisibility } from '@cupcake/core';
3
+ import * as i3 from '@noatgnu/cupcake-core';
4
+ import { BaseApiService, WebSocketService, NotificationService as NotificationService$1, LabGroupService, ToastService, ResourceVisibility } from '@noatgnu/cupcake-core';
5
5
  import { switchMap, map, tap, takeUntil, filter as filter$1 } from 'rxjs/operators';
6
6
  import { from, Observable, throwError, NEVER, debounceTime, distinctUntilChanged, filter, switchMap as switchMap$1, map as map$1, catchError, of, tap as tap$1, Subject, BehaviorSubject } from 'rxjs';
7
7
  import jsSHA from 'jssha';
@@ -11,8 +11,6 @@ import * as i1 from '@angular/forms';
11
11
  import { FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
12
12
  import * as i2 from '@ng-bootstrap/ng-bootstrap';
13
13
  import { NgbTypeahead, NgbModule, NgbNavModule, NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
14
- import * as i4 from '@cupcake/vanilla';
15
- import { SdrfSyntaxService as SdrfSyntaxService$1, OntologyType as OntologyType$1, SdrfAgeInput as SdrfAgeInput$1, SdrfModificationInput as SdrfModificationInput$1, SdrfCleavageInput as SdrfCleavageInput$1, SdrfSpikedCompoundInput as SdrfSpikedCompoundInput$1, COLUMN_TYPE_CONFIGS as COLUMN_TYPE_CONFIGS$1, ONTOLOGY_TYPE_CONFIGS as ONTOLOGY_TYPE_CONFIGS$1, OFFICIAL_SDRF_COLUMNS as OFFICIAL_SDRF_COLUMNS$1, ColumnType as ColumnType$1, MetadataValueEditModal as MetadataValueEditModal$1 } from '@cupcake/vanilla';
16
14
  import * as i5$1 from '@angular/cdk/drag-drop';
17
15
  import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
18
16
 
@@ -658,10 +656,10 @@ class MetadataTableService extends BaseApiService {
658
656
  confirmed
659
657
  });
660
658
  }
661
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
662
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableService, providedIn: 'root' });
659
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
660
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableService, providedIn: 'root' });
663
661
  }
664
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableService, decorators: [{
662
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableService, decorators: [{
665
663
  type: Injectable,
666
664
  args: [{
667
665
  providedIn: 'root'
@@ -750,10 +748,10 @@ class MetadataColumnService extends BaseApiService {
750
748
  const httpParams = this.buildHttpParams(params);
751
749
  return this.get(`${this.apiUrl}/metadata-columns/detect_ontology_type/`, { params: httpParams });
752
750
  }
753
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
754
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnService, providedIn: 'root' });
751
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
752
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnService, providedIn: 'root' });
755
753
  }
756
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnService, decorators: [{
754
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnService, decorators: [{
757
755
  type: Injectable,
758
756
  args: [{
759
757
  providedIn: 'root'
@@ -804,10 +802,10 @@ class SamplePoolService extends BaseApiService {
804
802
  getMetadataColumns(id) {
805
803
  return this.get(`${this.apiUrl}/sample-pools/${id}/metadata_columns/`);
806
804
  }
807
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SamplePoolService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
808
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SamplePoolService, providedIn: 'root' });
805
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SamplePoolService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
806
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SamplePoolService, providedIn: 'root' });
809
807
  }
810
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SamplePoolService, decorators: [{
808
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SamplePoolService, decorators: [{
811
809
  type: Injectable,
812
810
  args: [{
813
811
  providedIn: 'root'
@@ -944,10 +942,10 @@ class MetadataTableTemplateService extends BaseApiService {
944
942
  createTableFromSchemas(request) {
945
943
  return this.post(`${this.apiUrl}/metadata-table-templates/create_table_from_schemas/`, request);
946
944
  }
947
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplateService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
948
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplateService, providedIn: 'root' });
945
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplateService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
946
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplateService, providedIn: 'root' });
949
947
  }
950
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplateService, decorators: [{
948
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplateService, decorators: [{
951
949
  type: Injectable,
952
950
  args: [{
953
951
  providedIn: 'root'
@@ -992,10 +990,10 @@ class FavouriteMetadataOptionService extends BaseApiService {
992
990
  deleteFavouriteMetadataOption(id) {
993
991
  return this.delete(`${this.apiUrl}/favourite-options/${id}/`);
994
992
  }
995
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: FavouriteMetadataOptionService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
996
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: FavouriteMetadataOptionService, providedIn: 'root' });
993
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: FavouriteMetadataOptionService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
994
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: FavouriteMetadataOptionService, providedIn: 'root' });
997
995
  }
998
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: FavouriteMetadataOptionService, decorators: [{
996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: FavouriteMetadataOptionService, decorators: [{
999
997
  type: Injectable,
1000
998
  args: [{
1001
999
  providedIn: 'root'
@@ -1082,10 +1080,10 @@ class MetadataColumnTemplateService extends BaseApiService {
1082
1080
  });
1083
1081
  return this.get(`${this.apiUrl}/column-templates/ontology_suggestions/`, { params: httpParams });
1084
1082
  }
1085
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnTemplateService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1086
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnTemplateService, providedIn: 'root' });
1083
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnTemplateService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1084
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnTemplateService, providedIn: 'root' });
1087
1085
  }
1088
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnTemplateService, decorators: [{
1086
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnTemplateService, decorators: [{
1089
1087
  type: Injectable,
1090
1088
  args: [{
1091
1089
  providedIn: 'root'
@@ -1124,10 +1122,10 @@ class MetadataColumnTemplateShareService extends BaseApiService {
1124
1122
  deleteMetadataColumnTemplateShare(id) {
1125
1123
  return this.delete(`${this.apiUrl}/template-shares/${id}/`);
1126
1124
  }
1127
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnTemplateShareService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1128
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnTemplateShareService, providedIn: 'root' });
1125
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnTemplateShareService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1126
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnTemplateShareService, providedIn: 'root' });
1129
1127
  }
1130
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataColumnTemplateShareService, decorators: [{
1128
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataColumnTemplateShareService, decorators: [{
1131
1129
  type: Injectable,
1132
1130
  args: [{
1133
1131
  providedIn: 'root'
@@ -1180,10 +1178,10 @@ class MetadataManagementService extends BaseApiService {
1180
1178
  exportSdrfFile(request) {
1181
1179
  return this.post(`${this.apiUrl}/metadata-management/export_sdrf_file/`, request, { responseType: 'blob' });
1182
1180
  }
1183
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataManagementService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1184
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataManagementService, providedIn: 'root' });
1181
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataManagementService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1182
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataManagementService, providedIn: 'root' });
1185
1183
  }
1186
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataManagementService, decorators: [{
1184
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataManagementService, decorators: [{
1187
1185
  type: Injectable,
1188
1186
  args: [{
1189
1187
  providedIn: 'root'
@@ -1203,10 +1201,10 @@ class Websocket extends WebSocketService {
1203
1201
  getLabGroupUpdates() {
1204
1202
  return this.filterMessages('lab_group.update');
1205
1203
  }
1206
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: Websocket, deps: [{ token: i3.AuthService }], target: i0.ɵɵFactoryTarget.Injectable });
1207
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: Websocket, providedIn: 'root' });
1204
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Websocket, deps: [{ token: i3.AuthService }], target: i0.ɵɵFactoryTarget.Injectable });
1205
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Websocket, providedIn: 'root' });
1208
1206
  }
1209
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: Websocket, decorators: [{
1207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Websocket, decorators: [{
1210
1208
  type: Injectable,
1211
1209
  args: [{
1212
1210
  providedIn: 'root'
@@ -1226,10 +1224,10 @@ class AdminWebSocketService extends WebSocketService {
1226
1224
  getSystemNotifications() {
1227
1225
  return this.filterMessages('system.notification');
1228
1226
  }
1229
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AdminWebSocketService, deps: [{ token: i3.AuthService }], target: i0.ɵɵFactoryTarget.Injectable });
1230
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AdminWebSocketService, providedIn: 'root' });
1227
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AdminWebSocketService, deps: [{ token: i3.AuthService }], target: i0.ɵɵFactoryTarget.Injectable });
1228
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AdminWebSocketService, providedIn: 'root' });
1231
1229
  }
1232
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AdminWebSocketService, decorators: [{
1230
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AdminWebSocketService, decorators: [{
1233
1231
  type: Injectable,
1234
1232
  args: [{
1235
1233
  providedIn: 'root'
@@ -1515,10 +1513,10 @@ class NotificationService extends NotificationService$1 {
1515
1513
  createVanillaNotification(type, title, message, level = 'info', autoClose = true) {
1516
1514
  this.createLocalNotification(type, title, message, level, autoClose);
1517
1515
  }
1518
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NotificationService, deps: [{ token: Websocket }, { token: AdminWebSocketService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Injectable });
1519
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NotificationService, providedIn: 'root' });
1516
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: NotificationService, deps: [{ token: Websocket }, { token: AdminWebSocketService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Injectable });
1517
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: NotificationService, providedIn: 'root' });
1520
1518
  }
1521
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: NotificationService, decorators: [{
1519
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: NotificationService, decorators: [{
1522
1520
  type: Injectable,
1523
1521
  args: [{
1524
1522
  providedIn: 'root'
@@ -1533,10 +1531,10 @@ class OntologySearchService extends BaseApiService {
1533
1531
  const httpParams = this.buildHttpParams(params);
1534
1532
  return this.get(`${this.apiUrl}/ontology-search/suggest/`, { params: httpParams });
1535
1533
  }
1536
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: OntologySearchService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1537
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: OntologySearchService, providedIn: 'root' });
1534
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: OntologySearchService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1535
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: OntologySearchService, providedIn: 'root' });
1538
1536
  }
1539
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: OntologySearchService, decorators: [{
1537
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: OntologySearchService, decorators: [{
1540
1538
  type: Injectable,
1541
1539
  args: [{
1542
1540
  providedIn: 'root'
@@ -1569,10 +1567,10 @@ class SchemaService extends BaseApiService {
1569
1567
  syncBuiltin() {
1570
1568
  return this.post(`${this.apiUrl}/schemas/sync_builtin/`, {});
1571
1569
  }
1572
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SchemaService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1573
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SchemaService, providedIn: 'root' });
1570
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SchemaService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1571
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SchemaService, providedIn: 'root' });
1574
1572
  }
1575
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SchemaService, decorators: [{
1573
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SchemaService, decorators: [{
1576
1574
  type: Injectable,
1577
1575
  args: [{
1578
1576
  providedIn: 'root'
@@ -1663,10 +1661,10 @@ class SDRFDefaultsService extends BaseApiService {
1663
1661
  getQuickValues() {
1664
1662
  return this.get(`${this.apiUrl}/sdrf-defaults/quick_values/`);
1665
1663
  }
1666
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SDRFDefaultsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1667
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SDRFDefaultsService, providedIn: 'root' });
1664
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SDRFDefaultsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1665
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SDRFDefaultsService, providedIn: 'root' });
1668
1666
  }
1669
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SDRFDefaultsService, decorators: [{
1667
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SDRFDefaultsService, decorators: [{
1670
1668
  type: Injectable,
1671
1669
  args: [{
1672
1670
  providedIn: 'root'
@@ -1706,10 +1704,10 @@ class AsyncTaskService extends BaseApiService {
1706
1704
  const httpParams = this.buildHttpParams({ token });
1707
1705
  return this.get(`${this.apiUrl}/async-tasks/${id}/download/`, { params: httpParams, responseType: 'blob' });
1708
1706
  }
1709
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncTaskService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1710
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncTaskService, providedIn: 'root' });
1707
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncTaskService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1708
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncTaskService, providedIn: 'root' });
1711
1709
  }
1712
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncTaskService, decorators: [{
1710
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncTaskService, decorators: [{
1713
1711
  type: Injectable,
1714
1712
  args: [{
1715
1713
  providedIn: 'root'
@@ -1729,10 +1727,10 @@ class AsyncExportService extends BaseApiService {
1729
1727
  multipleExcelTemplates(request) {
1730
1728
  return this.post(`${this.apiUrl}/async-export/multiple_excel_templates/`, request);
1731
1729
  }
1732
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncExportService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1733
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncExportService, providedIn: 'root' });
1730
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncExportService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1731
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncExportService, providedIn: 'root' });
1734
1732
  }
1735
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncExportService, decorators: [{
1733
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncExportService, decorators: [{
1736
1734
  type: Injectable,
1737
1735
  args: [{
1738
1736
  providedIn: 'root'
@@ -1918,10 +1916,10 @@ class ChunkedUploadService extends BaseApiService {
1918
1916
  uploadNextChunk();
1919
1917
  });
1920
1918
  }
1921
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ChunkedUploadService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1922
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ChunkedUploadService, providedIn: 'root' });
1919
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ChunkedUploadService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1920
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ChunkedUploadService, providedIn: 'root' });
1923
1921
  }
1924
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ChunkedUploadService, decorators: [{
1922
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ChunkedUploadService, decorators: [{
1925
1923
  type: Injectable,
1926
1924
  args: [{
1927
1925
  providedIn: 'root'
@@ -1966,10 +1964,10 @@ class AsyncImportService extends BaseApiService {
1966
1964
  result: result
1967
1965
  })));
1968
1966
  }
1969
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncImportService, deps: [{ token: ChunkedUploadService }], target: i0.ɵɵFactoryTarget.Injectable });
1970
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncImportService, providedIn: 'root' });
1967
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncImportService, deps: [{ token: ChunkedUploadService }], target: i0.ɵɵFactoryTarget.Injectable });
1968
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncImportService, providedIn: 'root' });
1971
1969
  }
1972
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncImportService, decorators: [{
1970
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncImportService, decorators: [{
1973
1971
  type: Injectable,
1974
1972
  args: [{
1975
1973
  providedIn: 'root'
@@ -1980,10 +1978,10 @@ class AsyncValidationService extends BaseApiService {
1980
1978
  metadataTable(request) {
1981
1979
  return this.post(`${this.apiUrl}/async-validation/metadata_table/`, request);
1982
1980
  }
1983
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncValidationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1984
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncValidationService, providedIn: 'root' });
1981
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncValidationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1982
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncValidationService, providedIn: 'root' });
1985
1983
  }
1986
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncValidationService, decorators: [{
1984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncValidationService, decorators: [{
1987
1985
  type: Injectable,
1988
1986
  args: [{
1989
1987
  providedIn: 'root'
@@ -2165,10 +2163,10 @@ class ChunkedImportService extends BaseApiService {
2165
2163
  };
2166
2164
  return this.post(`${this.apiUrl}/async-import/chunked_file/`, payload);
2167
2165
  }
2168
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ChunkedImportService, deps: [{ token: ChunkedUploadService }], target: i0.ɵɵFactoryTarget.Injectable });
2169
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ChunkedImportService, providedIn: 'root' });
2166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ChunkedImportService, deps: [{ token: ChunkedUploadService }], target: i0.ɵɵFactoryTarget.Injectable });
2167
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ChunkedImportService, providedIn: 'root' });
2170
2168
  }
2171
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ChunkedImportService, decorators: [{
2169
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ChunkedImportService, decorators: [{
2172
2170
  type: Injectable,
2173
2171
  args: [{
2174
2172
  providedIn: 'root'
@@ -2452,10 +2450,10 @@ class SdrfSyntaxService {
2452
2450
  getSyntheticPeptideValues() {
2453
2451
  return [...this.SYNTHETIC_PEPTIDE_VALUES];
2454
2452
  }
2455
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfSyntaxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2456
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfSyntaxService, providedIn: 'root' });
2453
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfSyntaxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2454
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfSyntaxService, providedIn: 'root' });
2457
2455
  }
2458
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfSyntaxService, decorators: [{
2456
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfSyntaxService, decorators: [{
2459
2457
  type: Injectable,
2460
2458
  args: [{
2461
2459
  providedIn: 'root'
@@ -2553,10 +2551,10 @@ class ElectronService {
2553
2551
  return () => unsubscribe();
2554
2552
  });
2555
2553
  }
2556
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ElectronService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2557
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ElectronService, providedIn: 'root' });
2554
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ElectronService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2555
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ElectronService, providedIn: 'root' });
2558
2556
  }
2559
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ElectronService, decorators: [{
2557
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ElectronService, decorators: [{
2560
2558
  type: Injectable,
2561
2559
  args: [{
2562
2560
  providedIn: 'root'
@@ -2660,10 +2658,10 @@ class SdrfAgeInput {
2660
2658
  }
2661
2659
  }
2662
2660
  }
2663
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfAgeInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
2664
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: SdrfAgeInput, isStandalone: true, selector: "ccv-sdrf-age-input", inputs: { value: "value" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"ageForm\" class=\"age-input-form\">\n <!-- Range Toggle -->\n <div class=\"form-check mb-3\">\n <input class=\"form-check-input\" type=\"checkbox\" id=\"isRange\" formControlName=\"isRange\">\n <label class=\"form-check-label\" for=\"isRange\">\n Age Range\n </label>\n </div>\n\n <!-- Single Age Input -->\n @if (!isRange) {\n <div class=\"row g-2 mb-2\">\n <div class=\"col-sm-3\">\n <label for=\"years\" class=\"form-label\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"years\" \n formControlName=\"years\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"months\" class=\"form-label\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"months\" \n formControlName=\"months\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"days\" class=\"form-label\">Days</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"days\" \n formControlName=\"days\" placeholder=\"0\" min=\"0\" max=\"31\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"weeks\" class=\"form-label\">Weeks <small class=\"text-muted\">(alternative)</small></label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"weeks\" \n formControlName=\"weeks\" placeholder=\"0\" min=\"0\" max=\"520\">\n </div>\n </div>\n <div class=\"form-text\">\n Use weeks for very young subjects (e.g., \"8W\" for 8 weeks). Otherwise use years/months/days.\n </div>\n }\n\n <!-- Age Range Input -->\n @if (isRange) {\n <div class=\"row g-2 mb-2\">\n <div class=\"col-sm-6\">\n <label class=\"form-label fw-semibold\">Start Age</label>\n <div class=\"row g-1\">\n <div class=\"col-6\">\n <label for=\"rangeStartYears\" class=\"form-label small\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeStartYears\" \n formControlName=\"rangeStartYears\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-6\">\n <label for=\"rangeStartMonths\" class=\"form-label small\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeStartMonths\" \n formControlName=\"rangeStartMonths\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n </div>\n </div>\n <div class=\"col-sm-6\">\n <label class=\"form-label fw-semibold\">End Age</label>\n <div class=\"row g-1\">\n <div class=\"col-6\">\n <label for=\"rangeEndYears\" class=\"form-label small\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeEndYears\" \n formControlName=\"rangeEndYears\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-6\">\n <label for=\"rangeEndMonths\" class=\"form-label small\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeEndMonths\" \n formControlName=\"rangeEndMonths\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n </div>\n </div>\n </div>\n <div class=\"form-text\">\n Age range format: \"25Y-30Y\" or \"2Y6M-3Y\"\n </div>\n }\n\n <!-- Validation Messages -->\n @if (ageForm.invalid && ageForm.touched) {\n <div class=\"alert alert-warning alert-sm mt-2\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n Please enter valid age values (0-200 years, 0-11 months, 0-31 days)\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
2661
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfAgeInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
2662
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: SdrfAgeInput, isStandalone: true, selector: "ccv-sdrf-age-input", inputs: { value: "value" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"ageForm\" class=\"age-input-form\">\n <!-- Range Toggle -->\n <div class=\"form-check mb-3\">\n <input class=\"form-check-input\" type=\"checkbox\" id=\"isRange\" formControlName=\"isRange\">\n <label class=\"form-check-label\" for=\"isRange\">\n Age Range\n </label>\n </div>\n\n <!-- Single Age Input -->\n @if (!isRange) {\n <div class=\"row g-2 mb-2\">\n <div class=\"col-sm-3\">\n <label for=\"years\" class=\"form-label\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"years\" \n formControlName=\"years\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"months\" class=\"form-label\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"months\" \n formControlName=\"months\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"days\" class=\"form-label\">Days</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"days\" \n formControlName=\"days\" placeholder=\"0\" min=\"0\" max=\"31\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"weeks\" class=\"form-label\">Weeks <small class=\"text-muted\">(alternative)</small></label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"weeks\" \n formControlName=\"weeks\" placeholder=\"0\" min=\"0\" max=\"520\">\n </div>\n </div>\n <div class=\"form-text\">\n Use weeks for very young subjects (e.g., \"8W\" for 8 weeks). Otherwise use years/months/days.\n </div>\n }\n\n <!-- Age Range Input -->\n @if (isRange) {\n <div class=\"row g-2 mb-2\">\n <div class=\"col-sm-6\">\n <label class=\"form-label fw-semibold\">Start Age</label>\n <div class=\"row g-1\">\n <div class=\"col-6\">\n <label for=\"rangeStartYears\" class=\"form-label small\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeStartYears\" \n formControlName=\"rangeStartYears\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-6\">\n <label for=\"rangeStartMonths\" class=\"form-label small\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeStartMonths\" \n formControlName=\"rangeStartMonths\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n </div>\n </div>\n <div class=\"col-sm-6\">\n <label class=\"form-label fw-semibold\">End Age</label>\n <div class=\"row g-1\">\n <div class=\"col-6\">\n <label for=\"rangeEndYears\" class=\"form-label small\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeEndYears\" \n formControlName=\"rangeEndYears\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-6\">\n <label for=\"rangeEndMonths\" class=\"form-label small\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeEndMonths\" \n formControlName=\"rangeEndMonths\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n </div>\n </div>\n </div>\n <div class=\"form-text\">\n Age range format: \"25Y-30Y\" or \"2Y6M-3Y\"\n </div>\n }\n\n <!-- Validation Messages -->\n @if (ageForm.invalid && ageForm.touched) {\n <div class=\"alert alert-warning alert-sm mt-2\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n Please enter valid age values (0-200 years, 0-11 months, 0-31 days)\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
2665
2663
  }
2666
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfAgeInput, decorators: [{
2664
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfAgeInput, decorators: [{
2667
2665
  type: Component,
2668
2666
  args: [{ selector: 'ccv-sdrf-age-input', imports: [CommonModule, ReactiveFormsModule], template: "<form [formGroup]=\"ageForm\" class=\"age-input-form\">\n <!-- Range Toggle -->\n <div class=\"form-check mb-3\">\n <input class=\"form-check-input\" type=\"checkbox\" id=\"isRange\" formControlName=\"isRange\">\n <label class=\"form-check-label\" for=\"isRange\">\n Age Range\n </label>\n </div>\n\n <!-- Single Age Input -->\n @if (!isRange) {\n <div class=\"row g-2 mb-2\">\n <div class=\"col-sm-3\">\n <label for=\"years\" class=\"form-label\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"years\" \n formControlName=\"years\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"months\" class=\"form-label\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"months\" \n formControlName=\"months\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"days\" class=\"form-label\">Days</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"days\" \n formControlName=\"days\" placeholder=\"0\" min=\"0\" max=\"31\">\n </div>\n <div class=\"col-sm-3\">\n <label for=\"weeks\" class=\"form-label\">Weeks <small class=\"text-muted\">(alternative)</small></label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"weeks\" \n formControlName=\"weeks\" placeholder=\"0\" min=\"0\" max=\"520\">\n </div>\n </div>\n <div class=\"form-text\">\n Use weeks for very young subjects (e.g., \"8W\" for 8 weeks). Otherwise use years/months/days.\n </div>\n }\n\n <!-- Age Range Input -->\n @if (isRange) {\n <div class=\"row g-2 mb-2\">\n <div class=\"col-sm-6\">\n <label class=\"form-label fw-semibold\">Start Age</label>\n <div class=\"row g-1\">\n <div class=\"col-6\">\n <label for=\"rangeStartYears\" class=\"form-label small\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeStartYears\" \n formControlName=\"rangeStartYears\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-6\">\n <label for=\"rangeStartMonths\" class=\"form-label small\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeStartMonths\" \n formControlName=\"rangeStartMonths\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n </div>\n </div>\n <div class=\"col-sm-6\">\n <label class=\"form-label fw-semibold\">End Age</label>\n <div class=\"row g-1\">\n <div class=\"col-6\">\n <label for=\"rangeEndYears\" class=\"form-label small\">Years</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeEndYears\" \n formControlName=\"rangeEndYears\" placeholder=\"0\" min=\"0\" max=\"200\">\n </div>\n <div class=\"col-6\">\n <label for=\"rangeEndMonths\" class=\"form-label small\">Months</label>\n <input type=\"number\" class=\"form-control form-control-sm\" id=\"rangeEndMonths\" \n formControlName=\"rangeEndMonths\" placeholder=\"0\" min=\"0\" max=\"11\">\n </div>\n </div>\n </div>\n </div>\n <div class=\"form-text\">\n Age range format: \"25Y-30Y\" or \"2Y6M-3Y\"\n </div>\n }\n\n <!-- Validation Messages -->\n @if (ageForm.invalid && ageForm.touched) {\n <div class=\"alert alert-warning alert-sm mt-2\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n Please enter valid age values (0-200 years, 0-11 months, 0-31 days)\n </small>\n </div>\n }\n</form>\n" }]
2669
2667
  }], propDecorators: { value: [{
@@ -2954,10 +2952,10 @@ class SdrfModificationInput {
2954
2952
  this.updateAvailableSpecifications(mockSuggestion);
2955
2953
  }
2956
2954
  }
2957
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfModificationInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
2958
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: SdrfModificationInput, isStandalone: true, selector: "ccv-sdrf-modification-input", inputs: { value: "value", templateId: "templateId", columnId: "columnId" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"modificationForm\" class=\"modification-input-form\">\n\n <!-- Search Type Selection for Typeahead -->\n @if (templateId || columnId) {\n <div class=\"mb-3\">\n <small class=\"text-muted d-block mb-1\">Search Mode for Name of Term:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n }\n\n <div class=\"row g-3\">\n\n <!-- Name of Term (NT) - Required with Typeahead -->\n <div class=\"col-md-6\">\n <label for=\"NT\" class=\"form-label\">\n Name of Term <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(NT)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"NT\"\n formControlName=\"NT\"\n placeholder=\"e.g., Oxidation, Acetyl, Phospho\"\n [ngbTypeahead]=\"searchModificationTerms\"\n [resultFormatter]=\"formatOntologySuggestion\"\n [inputFormatter]=\"formatOntologySuggestion\"\n (selectItem)=\"onOntologySuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\"\n [class.is-invalid]=\"modificationForm.get('NT')?.invalid && modificationForm.get('NT')?.touched\">\n <div class=\"form-text\">\n @if (templateId || columnId) {\n Typeahead search: {{ searchType() === 'icontains' ? 'Contains' : 'Starts with' }} mode\n } @else {\n Common modifications: Oxidation, Carbamidomethyl, Acetyl\n }\n </div>\n <div class=\"invalid-feedback\">\n Name of Term is required\n </div>\n </div>\n\n <!-- Unimod Specification Selection -->\n @if (showSpecifications() && availableSpecifications().length > 0) {\n <div class=\"col-md-12\">\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <label class=\"form-label mb-0\">\n Unimod Specifications\n <small class=\"text-muted\">(Select to auto-fill fields)</small>\n </label>\n \n @if (hiddenSpecificationCount() > 0) {\n <div class=\"form-check form-switch\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"showHiddenSpecs\"\n [checked]=\"showHiddenSpecifications()\"\n (change)=\"toggleHiddenSpecifications()\">\n <label class=\"form-check-label small text-muted\" for=\"showHiddenSpecs\">\n Show {{ hiddenSpecificationCount() }} hidden spec{{ hiddenSpecificationCount() === 1 ? '' : 's' }}\n </label>\n </div>\n }\n </div>\n \n <div class=\"border rounded p-2 bg-body-secondary\">\n <div class=\"mb-2\">\n <small class=\"text-info fw-semibold\">\n <i class=\"bi bi-info-circle me-1\"></i>\n {{ showHiddenSpecifications() ? 'All' : 'Visible' }} specifications for {{ selectedUnimodData()?.name }}\n @if (!showHiddenSpecifications() && hiddenSpecificationCount() > 0) {\n <span class=\"text-muted\">({{ hiddenSpecificationCount() }} hidden)</span>\n }\n </small>\n </div>\n <div class=\"row g-2\">\n @for (spec of availableSpecifications(); track spec.specNumber) {\n <div class=\"col-md-6\">\n <div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"radio\"\n [id]=\"'spec_' + spec.specNumber\"\n [value]=\"spec.specNumber\"\n [checked]=\"selectedSpecification() === spec.specNumber\"\n (change)=\"onSpecificationSelected(spec.specNumber)\">\n <label\n class=\"form-check-label small\"\n [for]=\"'spec_' + spec.specNumber\">\n <div class=\"fw-semibold\">\n Spec {{ spec.specNumber }}\n @if (spec['hidden'] === '1') {\n <span class=\"badge bg-warning text-dark ms-1\" title=\"Hidden specification\">Hidden</span>\n }\n </div>\n <div class=\"text-muted\">{{ getSpecificationDisplay(spec) }}</div>\n </label>\n </div>\n </div>\n }\n </div>\n @if (selectedSpecification()) {\n <div class=\"mt-2 p-2 bg-body border rounded\">\n <small class=\"text-success\">\n <i class=\"bi bi-check-circle me-1\"></i>\n Fields auto-filled from specification {{ selectedSpecification() }}\n </small>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Target Amino Acid (TA) - Required -->\n <div class=\"col-md-6\">\n <label for=\"TA\" class=\"form-label\">\n Target Amino Acid <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(TA)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"TA\"\n formControlName=\"TA\"\n placeholder=\"e.g., M, S,T,Y, C\"\n (input)=\"onTargetAminoAcidChange($event)\"\n [class.is-invalid]=\"modificationForm.get('TA')?.invalid && modificationForm.get('TA')?.touched\">\n <div class=\"form-text\">Single letter amino acid codes, comma-separated</div>\n <div class=\"invalid-feedback\">\n Target amino acid is required\n </div>\n </div>\n\n <!-- Accession (AC) -->\n <div class=\"col-md-6\">\n <label for=\"AC\" class=\"form-label\">\n Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., UNIMOD:35, PSI-MOD:00719\">\n <div class=\"form-text\">UNIMOD or PSI-MOD accession</div>\n </div>\n\n <!-- Modification Type (MT) -->\n <div class=\"col-md-6\">\n <label for=\"MT\" class=\"form-label\">\n Modification Type <small class=\"text-muted\">(MT)</small>\n </label>\n <select class=\"form-select\" id=\"MT\" formControlName=\"MT\">\n <option value=\"\">Select type...</option>\n @for (type of modificationTypes; track type) {\n <option [value]=\"type\">{{ type }}</option>\n }\n </select>\n <div class=\"form-text\">Fixed, Variable, or Annotated</div>\n </div>\n\n <!-- Position (PP) -->\n <div class=\"col-md-6\">\n <label for=\"PP\" class=\"form-label\">\n Position <small class=\"text-muted\">(PP)</small>\n </label>\n <select class=\"form-select\" id=\"PP\" formControlName=\"PP\">\n <option value=\"\">Select position...</option>\n @for (position of positions; track position) {\n <option [value]=\"position\">{{ position }}</option>\n }\n </select>\n <div class=\"form-text\">Where the modification occurs</div>\n </div>\n\n <!-- Chemical Formula (CF) -->\n <div class=\"col-md-6\">\n <label for=\"CF\" class=\"form-label\">\n Chemical Formula <small class=\"text-muted\">(CF)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"CF\"\n formControlName=\"CF\"\n placeholder=\"e.g., H(2)C(2)O\">\n <div class=\"form-text\">Follow UNIMOD format: H(2)C(2)O</div>\n </div>\n\n <!-- Monoisotopic Mass (MM) -->\n <div class=\"col-md-6\">\n <label for=\"MM\" class=\"form-label\">\n Monoisotopic Mass <small class=\"text-muted\">(MM)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"MM\"\n formControlName=\"MM\"\n placeholder=\"e.g., 42.010565\"\n [class.is-invalid]=\"modificationForm.get('MM')?.invalid && modificationForm.get('MM')?.touched\">\n <div class=\"form-text\">Mass shift in Da (5+ decimal places)</div>\n <div class=\"invalid-feedback\">\n Please enter a valid mass (numbers and decimal point only)\n </div>\n </div>\n\n <!-- Target Site (TS) -->\n <div class=\"col-md-6\">\n <label for=\"TS\" class=\"form-label\">\n Target Site <small class=\"text-muted\">(TS)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"TS\"\n formControlName=\"TS\"\n placeholder=\"e.g., N[^P][ST]\">\n <div class=\"form-text\">Regular expression for complex site rules</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (modificationForm.valid && (modificationForm.get('NT')?.value || modificationForm.get('TA')?.value)) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (modificationForm.invalid && modificationForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required fields:</strong> Name of Term (NT) and Target Amino Acid (TA) must be filled.\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: NgbModule }] });
2955
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfModificationInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
2956
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: SdrfModificationInput, isStandalone: true, selector: "ccv-sdrf-modification-input", inputs: { value: "value", templateId: "templateId", columnId: "columnId" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"modificationForm\" class=\"modification-input-form\">\n\n <!-- Search Type Selection for Typeahead -->\n @if (templateId || columnId) {\n <div class=\"mb-3\">\n <small class=\"text-muted d-block mb-1\">Search Mode for Name of Term:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n }\n\n <div class=\"row g-3\">\n\n <!-- Name of Term (NT) - Required with Typeahead -->\n <div class=\"col-md-6\">\n <label for=\"NT\" class=\"form-label\">\n Name of Term <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(NT)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"NT\"\n formControlName=\"NT\"\n placeholder=\"e.g., Oxidation, Acetyl, Phospho\"\n [ngbTypeahead]=\"searchModificationTerms\"\n [resultFormatter]=\"formatOntologySuggestion\"\n [inputFormatter]=\"formatOntologySuggestion\"\n (selectItem)=\"onOntologySuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\"\n [class.is-invalid]=\"modificationForm.get('NT')?.invalid && modificationForm.get('NT')?.touched\">\n <div class=\"form-text\">\n @if (templateId || columnId) {\n Typeahead search: {{ searchType() === 'icontains' ? 'Contains' : 'Starts with' }} mode\n } @else {\n Common modifications: Oxidation, Carbamidomethyl, Acetyl\n }\n </div>\n <div class=\"invalid-feedback\">\n Name of Term is required\n </div>\n </div>\n\n <!-- Unimod Specification Selection -->\n @if (showSpecifications() && availableSpecifications().length > 0) {\n <div class=\"col-md-12\">\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <label class=\"form-label mb-0\">\n Unimod Specifications\n <small class=\"text-muted\">(Select to auto-fill fields)</small>\n </label>\n \n @if (hiddenSpecificationCount() > 0) {\n <div class=\"form-check form-switch\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"showHiddenSpecs\"\n [checked]=\"showHiddenSpecifications()\"\n (change)=\"toggleHiddenSpecifications()\">\n <label class=\"form-check-label small text-muted\" for=\"showHiddenSpecs\">\n Show {{ hiddenSpecificationCount() }} hidden spec{{ hiddenSpecificationCount() === 1 ? '' : 's' }}\n </label>\n </div>\n }\n </div>\n \n <div class=\"border rounded p-2 bg-body-secondary\">\n <div class=\"mb-2\">\n <small class=\"text-info fw-semibold\">\n <i class=\"bi bi-info-circle me-1\"></i>\n {{ showHiddenSpecifications() ? 'All' : 'Visible' }} specifications for {{ selectedUnimodData()?.name }}\n @if (!showHiddenSpecifications() && hiddenSpecificationCount() > 0) {\n <span class=\"text-muted\">({{ hiddenSpecificationCount() }} hidden)</span>\n }\n </small>\n </div>\n <div class=\"row g-2\">\n @for (spec of availableSpecifications(); track spec.specNumber) {\n <div class=\"col-md-6\">\n <div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"radio\"\n [id]=\"'spec_' + spec.specNumber\"\n [value]=\"spec.specNumber\"\n [checked]=\"selectedSpecification() === spec.specNumber\"\n (change)=\"onSpecificationSelected(spec.specNumber)\">\n <label\n class=\"form-check-label small\"\n [for]=\"'spec_' + spec.specNumber\">\n <div class=\"fw-semibold\">\n Spec {{ spec.specNumber }}\n @if (spec['hidden'] === '1') {\n <span class=\"badge bg-warning text-dark ms-1\" title=\"Hidden specification\">Hidden</span>\n }\n </div>\n <div class=\"text-muted\">{{ getSpecificationDisplay(spec) }}</div>\n </label>\n </div>\n </div>\n }\n </div>\n @if (selectedSpecification()) {\n <div class=\"mt-2 p-2 bg-body border rounded\">\n <small class=\"text-success\">\n <i class=\"bi bi-check-circle me-1\"></i>\n Fields auto-filled from specification {{ selectedSpecification() }}\n </small>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Target Amino Acid (TA) - Required -->\n <div class=\"col-md-6\">\n <label for=\"TA\" class=\"form-label\">\n Target Amino Acid <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(TA)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"TA\"\n formControlName=\"TA\"\n placeholder=\"e.g., M, S,T,Y, C\"\n (input)=\"onTargetAminoAcidChange($event)\"\n [class.is-invalid]=\"modificationForm.get('TA')?.invalid && modificationForm.get('TA')?.touched\">\n <div class=\"form-text\">Single letter amino acid codes, comma-separated</div>\n <div class=\"invalid-feedback\">\n Target amino acid is required\n </div>\n </div>\n\n <!-- Accession (AC) -->\n <div class=\"col-md-6\">\n <label for=\"AC\" class=\"form-label\">\n Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., UNIMOD:35, PSI-MOD:00719\">\n <div class=\"form-text\">UNIMOD or PSI-MOD accession</div>\n </div>\n\n <!-- Modification Type (MT) -->\n <div class=\"col-md-6\">\n <label for=\"MT\" class=\"form-label\">\n Modification Type <small class=\"text-muted\">(MT)</small>\n </label>\n <select class=\"form-select\" id=\"MT\" formControlName=\"MT\">\n <option value=\"\">Select type...</option>\n @for (type of modificationTypes; track type) {\n <option [value]=\"type\">{{ type }}</option>\n }\n </select>\n <div class=\"form-text\">Fixed, Variable, or Annotated</div>\n </div>\n\n <!-- Position (PP) -->\n <div class=\"col-md-6\">\n <label for=\"PP\" class=\"form-label\">\n Position <small class=\"text-muted\">(PP)</small>\n </label>\n <select class=\"form-select\" id=\"PP\" formControlName=\"PP\">\n <option value=\"\">Select position...</option>\n @for (position of positions; track position) {\n <option [value]=\"position\">{{ position }}</option>\n }\n </select>\n <div class=\"form-text\">Where the modification occurs</div>\n </div>\n\n <!-- Chemical Formula (CF) -->\n <div class=\"col-md-6\">\n <label for=\"CF\" class=\"form-label\">\n Chemical Formula <small class=\"text-muted\">(CF)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"CF\"\n formControlName=\"CF\"\n placeholder=\"e.g., H(2)C(2)O\">\n <div class=\"form-text\">Follow UNIMOD format: H(2)C(2)O</div>\n </div>\n\n <!-- Monoisotopic Mass (MM) -->\n <div class=\"col-md-6\">\n <label for=\"MM\" class=\"form-label\">\n Monoisotopic Mass <small class=\"text-muted\">(MM)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"MM\"\n formControlName=\"MM\"\n placeholder=\"e.g., 42.010565\"\n [class.is-invalid]=\"modificationForm.get('MM')?.invalid && modificationForm.get('MM')?.touched\">\n <div class=\"form-text\">Mass shift in Da (5+ decimal places)</div>\n <div class=\"invalid-feedback\">\n Please enter a valid mass (numbers and decimal point only)\n </div>\n </div>\n\n <!-- Target Site (TS) -->\n <div class=\"col-md-6\">\n <label for=\"TS\" class=\"form-label\">\n Target Site <small class=\"text-muted\">(TS)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"TS\"\n formControlName=\"TS\"\n placeholder=\"e.g., N[^P][ST]\">\n <div class=\"form-text\">Regular expression for complex site rules</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (modificationForm.valid && (modificationForm.get('NT')?.value || modificationForm.get('TA')?.value)) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (modificationForm.invalid && modificationForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required fields:</strong> Name of Term (NT) and Target Amino Acid (TA) must be filled.\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: NgbModule }] });
2959
2957
  }
2960
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfModificationInput, decorators: [{
2958
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfModificationInput, decorators: [{
2961
2959
  type: Component,
2962
2960
  args: [{ selector: 'ccv-sdrf-modification-input', imports: [CommonModule, ReactiveFormsModule, NgbTypeahead, NgbModule], template: "<form [formGroup]=\"modificationForm\" class=\"modification-input-form\">\n\n <!-- Search Type Selection for Typeahead -->\n @if (templateId || columnId) {\n <div class=\"mb-3\">\n <small class=\"text-muted d-block mb-1\">Search Mode for Name of Term:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n }\n\n <div class=\"row g-3\">\n\n <!-- Name of Term (NT) - Required with Typeahead -->\n <div class=\"col-md-6\">\n <label for=\"NT\" class=\"form-label\">\n Name of Term <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(NT)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"NT\"\n formControlName=\"NT\"\n placeholder=\"e.g., Oxidation, Acetyl, Phospho\"\n [ngbTypeahead]=\"searchModificationTerms\"\n [resultFormatter]=\"formatOntologySuggestion\"\n [inputFormatter]=\"formatOntologySuggestion\"\n (selectItem)=\"onOntologySuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\"\n [class.is-invalid]=\"modificationForm.get('NT')?.invalid && modificationForm.get('NT')?.touched\">\n <div class=\"form-text\">\n @if (templateId || columnId) {\n Typeahead search: {{ searchType() === 'icontains' ? 'Contains' : 'Starts with' }} mode\n } @else {\n Common modifications: Oxidation, Carbamidomethyl, Acetyl\n }\n </div>\n <div class=\"invalid-feedback\">\n Name of Term is required\n </div>\n </div>\n\n <!-- Unimod Specification Selection -->\n @if (showSpecifications() && availableSpecifications().length > 0) {\n <div class=\"col-md-12\">\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <label class=\"form-label mb-0\">\n Unimod Specifications\n <small class=\"text-muted\">(Select to auto-fill fields)</small>\n </label>\n \n @if (hiddenSpecificationCount() > 0) {\n <div class=\"form-check form-switch\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"showHiddenSpecs\"\n [checked]=\"showHiddenSpecifications()\"\n (change)=\"toggleHiddenSpecifications()\">\n <label class=\"form-check-label small text-muted\" for=\"showHiddenSpecs\">\n Show {{ hiddenSpecificationCount() }} hidden spec{{ hiddenSpecificationCount() === 1 ? '' : 's' }}\n </label>\n </div>\n }\n </div>\n \n <div class=\"border rounded p-2 bg-body-secondary\">\n <div class=\"mb-2\">\n <small class=\"text-info fw-semibold\">\n <i class=\"bi bi-info-circle me-1\"></i>\n {{ showHiddenSpecifications() ? 'All' : 'Visible' }} specifications for {{ selectedUnimodData()?.name }}\n @if (!showHiddenSpecifications() && hiddenSpecificationCount() > 0) {\n <span class=\"text-muted\">({{ hiddenSpecificationCount() }} hidden)</span>\n }\n </small>\n </div>\n <div class=\"row g-2\">\n @for (spec of availableSpecifications(); track spec.specNumber) {\n <div class=\"col-md-6\">\n <div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"radio\"\n [id]=\"'spec_' + spec.specNumber\"\n [value]=\"spec.specNumber\"\n [checked]=\"selectedSpecification() === spec.specNumber\"\n (change)=\"onSpecificationSelected(spec.specNumber)\">\n <label\n class=\"form-check-label small\"\n [for]=\"'spec_' + spec.specNumber\">\n <div class=\"fw-semibold\">\n Spec {{ spec.specNumber }}\n @if (spec['hidden'] === '1') {\n <span class=\"badge bg-warning text-dark ms-1\" title=\"Hidden specification\">Hidden</span>\n }\n </div>\n <div class=\"text-muted\">{{ getSpecificationDisplay(spec) }}</div>\n </label>\n </div>\n </div>\n }\n </div>\n @if (selectedSpecification()) {\n <div class=\"mt-2 p-2 bg-body border rounded\">\n <small class=\"text-success\">\n <i class=\"bi bi-check-circle me-1\"></i>\n Fields auto-filled from specification {{ selectedSpecification() }}\n </small>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Target Amino Acid (TA) - Required -->\n <div class=\"col-md-6\">\n <label for=\"TA\" class=\"form-label\">\n Target Amino Acid <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(TA)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"TA\"\n formControlName=\"TA\"\n placeholder=\"e.g., M, S,T,Y, C\"\n (input)=\"onTargetAminoAcidChange($event)\"\n [class.is-invalid]=\"modificationForm.get('TA')?.invalid && modificationForm.get('TA')?.touched\">\n <div class=\"form-text\">Single letter amino acid codes, comma-separated</div>\n <div class=\"invalid-feedback\">\n Target amino acid is required\n </div>\n </div>\n\n <!-- Accession (AC) -->\n <div class=\"col-md-6\">\n <label for=\"AC\" class=\"form-label\">\n Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., UNIMOD:35, PSI-MOD:00719\">\n <div class=\"form-text\">UNIMOD or PSI-MOD accession</div>\n </div>\n\n <!-- Modification Type (MT) -->\n <div class=\"col-md-6\">\n <label for=\"MT\" class=\"form-label\">\n Modification Type <small class=\"text-muted\">(MT)</small>\n </label>\n <select class=\"form-select\" id=\"MT\" formControlName=\"MT\">\n <option value=\"\">Select type...</option>\n @for (type of modificationTypes; track type) {\n <option [value]=\"type\">{{ type }}</option>\n }\n </select>\n <div class=\"form-text\">Fixed, Variable, or Annotated</div>\n </div>\n\n <!-- Position (PP) -->\n <div class=\"col-md-6\">\n <label for=\"PP\" class=\"form-label\">\n Position <small class=\"text-muted\">(PP)</small>\n </label>\n <select class=\"form-select\" id=\"PP\" formControlName=\"PP\">\n <option value=\"\">Select position...</option>\n @for (position of positions; track position) {\n <option [value]=\"position\">{{ position }}</option>\n }\n </select>\n <div class=\"form-text\">Where the modification occurs</div>\n </div>\n\n <!-- Chemical Formula (CF) -->\n <div class=\"col-md-6\">\n <label for=\"CF\" class=\"form-label\">\n Chemical Formula <small class=\"text-muted\">(CF)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"CF\"\n formControlName=\"CF\"\n placeholder=\"e.g., H(2)C(2)O\">\n <div class=\"form-text\">Follow UNIMOD format: H(2)C(2)O</div>\n </div>\n\n <!-- Monoisotopic Mass (MM) -->\n <div class=\"col-md-6\">\n <label for=\"MM\" class=\"form-label\">\n Monoisotopic Mass <small class=\"text-muted\">(MM)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"MM\"\n formControlName=\"MM\"\n placeholder=\"e.g., 42.010565\"\n [class.is-invalid]=\"modificationForm.get('MM')?.invalid && modificationForm.get('MM')?.touched\">\n <div class=\"form-text\">Mass shift in Da (5+ decimal places)</div>\n <div class=\"invalid-feedback\">\n Please enter a valid mass (numbers and decimal point only)\n </div>\n </div>\n\n <!-- Target Site (TS) -->\n <div class=\"col-md-6\">\n <label for=\"TS\" class=\"form-label\">\n Target Site <small class=\"text-muted\">(TS)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"TS\"\n formControlName=\"TS\"\n placeholder=\"e.g., N[^P][ST]\">\n <div class=\"form-text\">Regular expression for complex site rules</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (modificationForm.valid && (modificationForm.get('NT')?.value || modificationForm.get('TA')?.value)) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (modificationForm.invalid && modificationForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required fields:</strong> Name of Term (NT) and Target Amino Acid (TA) must be filled.\n </small>\n </div>\n }\n</form>\n" }]
2963
2961
  }], propDecorators: { value: [{
@@ -3050,10 +3048,10 @@ class SdrfSpikedCompoundInput {
3050
3048
  get showPeptideFields() {
3051
3049
  return this.spikedForm.get('CT')?.value === 'peptide';
3052
3050
  }
3053
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfSpikedCompoundInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
3054
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: SdrfSpikedCompoundInput, isStandalone: true, selector: "ccv-sdrf-spiked-compound-input", inputs: { value: "value" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"spikedForm\" class=\"spiked-compound-input-form\">\n <div class=\"row g-3\">\n \n <!-- Compound Type (CT) - Required -->\n <div class=\"col-md-6\">\n <label for=\"CT\" class=\"form-label\">\n Compound Type <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(CT)</small>\n </label>\n <select class=\"form-select\" id=\"CT\" formControlName=\"CT\"\n [class.is-invalid]=\"spikedForm.get('CT')?.invalid && spikedForm.get('CT')?.touched\">\n <option value=\"\">Select compound type...</option>\n @for (type of compoundTypes; track type) {\n <option [value]=\"type\">{{ type }}</option>\n }\n </select>\n <div class=\"form-text\">Type of spiked compound</div>\n <div class=\"invalid-feedback\">\n Compound type is required\n </div>\n </div>\n\n <!-- Quantity (QY) - Required -->\n <div class=\"col-md-6\">\n <label for=\"QY\" class=\"form-label\">\n Quantity <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(QY)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"QY\"\n formControlName=\"QY\"\n placeholder=\"e.g., 10 fmol, 50 ng\"\n [class.is-invalid]=\"spikedForm.get('QY')?.invalid && spikedForm.get('QY')?.touched\">\n <div class=\"form-text\">Quantity with units (molar or mass)</div>\n <div class=\"invalid-feedback\">\n Quantity is required\n </div>\n </div>\n\n <!-- Species (SP) -->\n <div class=\"col-md-6\">\n <label for=\"SP\" class=\"form-label\">\n Species <small class=\"text-muted\">(SP)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"SP\"\n formControlName=\"SP\"\n placeholder=\"e.g., homo sapiens\">\n <div class=\"form-text\">Species of origin</div>\n </div>\n\n <!-- Compound Name (CN) -->\n <div class=\"col-md-6\">\n <label for=\"CN\" class=\"form-label\">\n Compound Name <small class=\"text-muted\">(CN)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CN\"\n formControlName=\"CN\"\n placeholder=\"e.g., Myoglobin\">\n <div class=\"form-text\">Name of the compound</div>\n </div>\n\n <!-- Protein-specific fields -->\n @if (showProteinFields) {\n <div class=\"col-md-12\">\n <label for=\"AC\" class=\"form-label\">\n Uniprot Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., P68871\">\n <div class=\"form-text\">Uniprot accession for protein compounds</div>\n </div>\n }\n\n <!-- Peptide-specific fields -->\n @if (showPeptideFields) {\n <div class=\"col-md-12\">\n <label for=\"PS\" class=\"form-label\">\n Peptide Sequence <small class=\"text-muted\">(PS)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"PS\"\n formControlName=\"PS\"\n placeholder=\"e.g., PEPTIDESEQ\">\n <div class=\"form-text\">Peptide sequence for peptide compounds</div>\n </div>\n }\n\n <!-- Compound Vendor (CV) -->\n <div class=\"col-md-6\">\n <label for=\"CV\" class=\"form-label\">\n Compound Vendor <small class=\"text-muted\">(CV)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CV\"\n formControlName=\"CV\"\n placeholder=\"e.g., Sigma-Aldrich\">\n <div class=\"form-text\">Vendor or supplier name</div>\n </div>\n\n <!-- Compound Formula (CF) -->\n <div class=\"col-md-6\">\n <label for=\"CF\" class=\"form-label\">\n Compound Formula <small class=\"text-muted\">(CF)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CF\"\n formControlName=\"CF\"\n placeholder=\"e.g., C8H14N4O2\">\n <div class=\"form-text\">Chemical formula</div>\n </div>\n\n <!-- Compound Specification URI (CS) -->\n <div class=\"col-md-12\">\n <label for=\"CS\" class=\"form-label\">\n Compound Specification URI <small class=\"text-muted\">(CS)</small>\n </label>\n <input \n type=\"url\" \n class=\"form-control\" \n id=\"CS\"\n formControlName=\"CS\"\n placeholder=\"https://example.com/compound-spec\">\n <div class=\"form-text\">URI to compound specification or documentation</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (spikedForm.valid && (spikedForm.get('CT')?.value && spikedForm.get('QY')?.value)) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (spikedForm.invalid && spikedForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required fields:</strong> Compound Type (CT) and Quantity (QY) must be filled.\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
3051
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfSpikedCompoundInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
3052
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: SdrfSpikedCompoundInput, isStandalone: true, selector: "ccv-sdrf-spiked-compound-input", inputs: { value: "value" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"spikedForm\" class=\"spiked-compound-input-form\">\n <div class=\"row g-3\">\n \n <!-- Compound Type (CT) - Required -->\n <div class=\"col-md-6\">\n <label for=\"CT\" class=\"form-label\">\n Compound Type <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(CT)</small>\n </label>\n <select class=\"form-select\" id=\"CT\" formControlName=\"CT\"\n [class.is-invalid]=\"spikedForm.get('CT')?.invalid && spikedForm.get('CT')?.touched\">\n <option value=\"\">Select compound type...</option>\n @for (type of compoundTypes; track type) {\n <option [value]=\"type\">{{ type }}</option>\n }\n </select>\n <div class=\"form-text\">Type of spiked compound</div>\n <div class=\"invalid-feedback\">\n Compound type is required\n </div>\n </div>\n\n <!-- Quantity (QY) - Required -->\n <div class=\"col-md-6\">\n <label for=\"QY\" class=\"form-label\">\n Quantity <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(QY)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"QY\"\n formControlName=\"QY\"\n placeholder=\"e.g., 10 fmol, 50 ng\"\n [class.is-invalid]=\"spikedForm.get('QY')?.invalid && spikedForm.get('QY')?.touched\">\n <div class=\"form-text\">Quantity with units (molar or mass)</div>\n <div class=\"invalid-feedback\">\n Quantity is required\n </div>\n </div>\n\n <!-- Species (SP) -->\n <div class=\"col-md-6\">\n <label for=\"SP\" class=\"form-label\">\n Species <small class=\"text-muted\">(SP)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"SP\"\n formControlName=\"SP\"\n placeholder=\"e.g., homo sapiens\">\n <div class=\"form-text\">Species of origin</div>\n </div>\n\n <!-- Compound Name (CN) -->\n <div class=\"col-md-6\">\n <label for=\"CN\" class=\"form-label\">\n Compound Name <small class=\"text-muted\">(CN)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CN\"\n formControlName=\"CN\"\n placeholder=\"e.g., Myoglobin\">\n <div class=\"form-text\">Name of the compound</div>\n </div>\n\n <!-- Protein-specific fields -->\n @if (showProteinFields) {\n <div class=\"col-md-12\">\n <label for=\"AC\" class=\"form-label\">\n Uniprot Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., P68871\">\n <div class=\"form-text\">Uniprot accession for protein compounds</div>\n </div>\n }\n\n <!-- Peptide-specific fields -->\n @if (showPeptideFields) {\n <div class=\"col-md-12\">\n <label for=\"PS\" class=\"form-label\">\n Peptide Sequence <small class=\"text-muted\">(PS)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"PS\"\n formControlName=\"PS\"\n placeholder=\"e.g., PEPTIDESEQ\">\n <div class=\"form-text\">Peptide sequence for peptide compounds</div>\n </div>\n }\n\n <!-- Compound Vendor (CV) -->\n <div class=\"col-md-6\">\n <label for=\"CV\" class=\"form-label\">\n Compound Vendor <small class=\"text-muted\">(CV)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CV\"\n formControlName=\"CV\"\n placeholder=\"e.g., Sigma-Aldrich\">\n <div class=\"form-text\">Vendor or supplier name</div>\n </div>\n\n <!-- Compound Formula (CF) -->\n <div class=\"col-md-6\">\n <label for=\"CF\" class=\"form-label\">\n Compound Formula <small class=\"text-muted\">(CF)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CF\"\n formControlName=\"CF\"\n placeholder=\"e.g., C8H14N4O2\">\n <div class=\"form-text\">Chemical formula</div>\n </div>\n\n <!-- Compound Specification URI (CS) -->\n <div class=\"col-md-12\">\n <label for=\"CS\" class=\"form-label\">\n Compound Specification URI <small class=\"text-muted\">(CS)</small>\n </label>\n <input \n type=\"url\" \n class=\"form-control\" \n id=\"CS\"\n formControlName=\"CS\"\n placeholder=\"https://example.com/compound-spec\">\n <div class=\"form-text\">URI to compound specification or documentation</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (spikedForm.valid && (spikedForm.get('CT')?.value && spikedForm.get('QY')?.value)) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (spikedForm.invalid && spikedForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required fields:</strong> Compound Type (CT) and Quantity (QY) must be filled.\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
3055
3053
  }
3056
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfSpikedCompoundInput, decorators: [{
3054
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfSpikedCompoundInput, decorators: [{
3057
3055
  type: Component,
3058
3056
  args: [{ selector: 'ccv-sdrf-spiked-compound-input', imports: [CommonModule, ReactiveFormsModule], template: "<form [formGroup]=\"spikedForm\" class=\"spiked-compound-input-form\">\n <div class=\"row g-3\">\n \n <!-- Compound Type (CT) - Required -->\n <div class=\"col-md-6\">\n <label for=\"CT\" class=\"form-label\">\n Compound Type <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(CT)</small>\n </label>\n <select class=\"form-select\" id=\"CT\" formControlName=\"CT\"\n [class.is-invalid]=\"spikedForm.get('CT')?.invalid && spikedForm.get('CT')?.touched\">\n <option value=\"\">Select compound type...</option>\n @for (type of compoundTypes; track type) {\n <option [value]=\"type\">{{ type }}</option>\n }\n </select>\n <div class=\"form-text\">Type of spiked compound</div>\n <div class=\"invalid-feedback\">\n Compound type is required\n </div>\n </div>\n\n <!-- Quantity (QY) - Required -->\n <div class=\"col-md-6\">\n <label for=\"QY\" class=\"form-label\">\n Quantity <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(QY)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"QY\"\n formControlName=\"QY\"\n placeholder=\"e.g., 10 fmol, 50 ng\"\n [class.is-invalid]=\"spikedForm.get('QY')?.invalid && spikedForm.get('QY')?.touched\">\n <div class=\"form-text\">Quantity with units (molar or mass)</div>\n <div class=\"invalid-feedback\">\n Quantity is required\n </div>\n </div>\n\n <!-- Species (SP) -->\n <div class=\"col-md-6\">\n <label for=\"SP\" class=\"form-label\">\n Species <small class=\"text-muted\">(SP)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"SP\"\n formControlName=\"SP\"\n placeholder=\"e.g., homo sapiens\">\n <div class=\"form-text\">Species of origin</div>\n </div>\n\n <!-- Compound Name (CN) -->\n <div class=\"col-md-6\">\n <label for=\"CN\" class=\"form-label\">\n Compound Name <small class=\"text-muted\">(CN)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CN\"\n formControlName=\"CN\"\n placeholder=\"e.g., Myoglobin\">\n <div class=\"form-text\">Name of the compound</div>\n </div>\n\n <!-- Protein-specific fields -->\n @if (showProteinFields) {\n <div class=\"col-md-12\">\n <label for=\"AC\" class=\"form-label\">\n Uniprot Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., P68871\">\n <div class=\"form-text\">Uniprot accession for protein compounds</div>\n </div>\n }\n\n <!-- Peptide-specific fields -->\n @if (showPeptideFields) {\n <div class=\"col-md-12\">\n <label for=\"PS\" class=\"form-label\">\n Peptide Sequence <small class=\"text-muted\">(PS)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"PS\"\n formControlName=\"PS\"\n placeholder=\"e.g., PEPTIDESEQ\">\n <div class=\"form-text\">Peptide sequence for peptide compounds</div>\n </div>\n }\n\n <!-- Compound Vendor (CV) -->\n <div class=\"col-md-6\">\n <label for=\"CV\" class=\"form-label\">\n Compound Vendor <small class=\"text-muted\">(CV)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CV\"\n formControlName=\"CV\"\n placeholder=\"e.g., Sigma-Aldrich\">\n <div class=\"form-text\">Vendor or supplier name</div>\n </div>\n\n <!-- Compound Formula (CF) -->\n <div class=\"col-md-6\">\n <label for=\"CF\" class=\"form-label\">\n Compound Formula <small class=\"text-muted\">(CF)</small>\n </label>\n <input \n type=\"text\" \n class=\"form-control\" \n id=\"CF\"\n formControlName=\"CF\"\n placeholder=\"e.g., C8H14N4O2\">\n <div class=\"form-text\">Chemical formula</div>\n </div>\n\n <!-- Compound Specification URI (CS) -->\n <div class=\"col-md-12\">\n <label for=\"CS\" class=\"form-label\">\n Compound Specification URI <small class=\"text-muted\">(CS)</small>\n </label>\n <input \n type=\"url\" \n class=\"form-control\" \n id=\"CS\"\n formControlName=\"CS\"\n placeholder=\"https://example.com/compound-spec\">\n <div class=\"form-text\">URI to compound specification or documentation</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (spikedForm.valid && (spikedForm.get('CT')?.value && spikedForm.get('QY')?.value)) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (spikedForm.invalid && spikedForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required fields:</strong> Compound Type (CT) and Quantity (QY) must be filled.\n </small>\n </div>\n }\n</form>\n" }]
3059
3057
  }], propDecorators: { value: [{
@@ -3186,10 +3184,10 @@ class SdrfCleavageInput {
3186
3184
  onSearchTypeChange(type) {
3187
3185
  this.searchType.set(type);
3188
3186
  }
3189
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfCleavageInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
3190
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: SdrfCleavageInput, isStandalone: true, selector: "ccv-sdrf-cleavage-input", inputs: { value: "value", templateId: "templateId", columnId: "columnId" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"cleavageForm\" class=\"cleavage-input-form\">\n\n <!-- Search Type Selection for Typeahead -->\n @if (templateId || columnId) {\n <div class=\"mb-3\">\n <small class=\"text-muted d-block mb-1\">Search Mode for Name of Term:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n }\n\n <div class=\"row g-3\">\n\n <!-- Name of Term (NT) - Required -->\n <div class=\"col-md-6\">\n <label for=\"NT\" class=\"form-label\">\n Name of Term <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(NT)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"NT\"\n formControlName=\"NT\"\n placeholder=\"e.g., Trypsin, Chymotrypsin\"\n [ngbTypeahead]=\"searchCleavageTerms\"\n [resultFormatter]=\"formatOntologySuggestion\"\n [inputFormatter]=\"formatOntologySuggestion\"\n (selectItem)=\"onOntologySuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\"\n [class.is-invalid]=\"cleavageForm.get('NT')?.invalid && cleavageForm.get('NT')?.touched\">\n <div class=\"form-text\">\n @if (templateId || columnId) {\n Typeahead search: {{ searchType() === 'icontains' ? 'Contains' : 'Starts with' }} mode\n } @else {\n Cleavage agent name (uses ontology typeahead when available)\n }\n </div>\n <div class=\"invalid-feedback\">\n Name of Term is required\n </div>\n </div>\n\n <!-- Accession (AC) -->\n <div class=\"col-md-6\">\n <label for=\"AC\" class=\"form-label\">\n Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., PSI-MS:1001251\">\n <div class=\"form-text\">PSI-MS or other ontology accession</div>\n </div>\n\n <!-- Cleavage Site (CS) -->\n <div class=\"col-md-12\">\n <label for=\"CS\" class=\"form-label\">\n Cleavage Site <small class=\"text-muted\">(CS)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"CS\"\n formControlName=\"CS\"\n placeholder=\"e.g., (?<=[KR])(?![P])\">\n <div class=\"form-text\">Regular expression pattern for cleavage site specificity</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (cleavageForm.valid && cleavageForm.get('NT')?.value) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (cleavageForm.invalid && cleavageForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required field:</strong> Name of Term (NT) must be filled.\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: NgbModule }] });
3187
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfCleavageInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
3188
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: SdrfCleavageInput, isStandalone: true, selector: "ccv-sdrf-cleavage-input", inputs: { value: "value", templateId: "templateId", columnId: "columnId" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<form [formGroup]=\"cleavageForm\" class=\"cleavage-input-form\">\n\n <!-- Search Type Selection for Typeahead -->\n @if (templateId || columnId) {\n <div class=\"mb-3\">\n <small class=\"text-muted d-block mb-1\">Search Mode for Name of Term:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n }\n\n <div class=\"row g-3\">\n\n <!-- Name of Term (NT) - Required -->\n <div class=\"col-md-6\">\n <label for=\"NT\" class=\"form-label\">\n Name of Term <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(NT)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"NT\"\n formControlName=\"NT\"\n placeholder=\"e.g., Trypsin, Chymotrypsin\"\n [ngbTypeahead]=\"searchCleavageTerms\"\n [resultFormatter]=\"formatOntologySuggestion\"\n [inputFormatter]=\"formatOntologySuggestion\"\n (selectItem)=\"onOntologySuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\"\n [class.is-invalid]=\"cleavageForm.get('NT')?.invalid && cleavageForm.get('NT')?.touched\">\n <div class=\"form-text\">\n @if (templateId || columnId) {\n Typeahead search: {{ searchType() === 'icontains' ? 'Contains' : 'Starts with' }} mode\n } @else {\n Cleavage agent name (uses ontology typeahead when available)\n }\n </div>\n <div class=\"invalid-feedback\">\n Name of Term is required\n </div>\n </div>\n\n <!-- Accession (AC) -->\n <div class=\"col-md-6\">\n <label for=\"AC\" class=\"form-label\">\n Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., PSI-MS:1001251\">\n <div class=\"form-text\">PSI-MS or other ontology accession</div>\n </div>\n\n <!-- Cleavage Site (CS) -->\n <div class=\"col-md-12\">\n <label for=\"CS\" class=\"form-label\">\n Cleavage Site <small class=\"text-muted\">(CS)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"CS\"\n formControlName=\"CS\"\n placeholder=\"e.g., (?<=[KR])(?![P])\">\n <div class=\"form-text\">Regular expression pattern for cleavage site specificity</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (cleavageForm.valid && cleavageForm.get('NT')?.value) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (cleavageForm.invalid && cleavageForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required field:</strong> Name of Term (NT) must be filled.\n </small>\n </div>\n }\n</form>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: NgbModule }] });
3191
3189
  }
3192
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SdrfCleavageInput, decorators: [{
3190
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SdrfCleavageInput, decorators: [{
3193
3191
  type: Component,
3194
3192
  args: [{ selector: 'ccv-sdrf-cleavage-input', imports: [CommonModule, ReactiveFormsModule, NgbTypeahead, NgbModule], template: "<form [formGroup]=\"cleavageForm\" class=\"cleavage-input-form\">\n\n <!-- Search Type Selection for Typeahead -->\n @if (templateId || columnId) {\n <div class=\"mb-3\">\n <small class=\"text-muted d-block mb-1\">Search Mode for Name of Term:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n }\n\n <div class=\"row g-3\">\n\n <!-- Name of Term (NT) - Required -->\n <div class=\"col-md-6\">\n <label for=\"NT\" class=\"form-label\">\n Name of Term <span class=\"text-danger\">*</span>\n <small class=\"text-muted\">(NT)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"NT\"\n formControlName=\"NT\"\n placeholder=\"e.g., Trypsin, Chymotrypsin\"\n [ngbTypeahead]=\"searchCleavageTerms\"\n [resultFormatter]=\"formatOntologySuggestion\"\n [inputFormatter]=\"formatOntologySuggestion\"\n (selectItem)=\"onOntologySuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\"\n [class.is-invalid]=\"cleavageForm.get('NT')?.invalid && cleavageForm.get('NT')?.touched\">\n <div class=\"form-text\">\n @if (templateId || columnId) {\n Typeahead search: {{ searchType() === 'icontains' ? 'Contains' : 'Starts with' }} mode\n } @else {\n Cleavage agent name (uses ontology typeahead when available)\n }\n </div>\n <div class=\"invalid-feedback\">\n Name of Term is required\n </div>\n </div>\n\n <!-- Accession (AC) -->\n <div class=\"col-md-6\">\n <label for=\"AC\" class=\"form-label\">\n Accession <small class=\"text-muted\">(AC)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"AC\"\n formControlName=\"AC\"\n placeholder=\"e.g., PSI-MS:1001251\">\n <div class=\"form-text\">PSI-MS or other ontology accession</div>\n </div>\n\n <!-- Cleavage Site (CS) -->\n <div class=\"col-md-12\">\n <label for=\"CS\" class=\"form-label\">\n Cleavage Site <small class=\"text-muted\">(CS)</small>\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"CS\"\n formControlName=\"CS\"\n placeholder=\"e.g., (?<=[KR])(?![P])\">\n <div class=\"form-text\">Regular expression pattern for cleavage site specificity</div>\n </div>\n\n </div>\n\n <!-- Preview -->\n @if (cleavageForm.valid && cleavageForm.get('NT')?.value) {\n <div class=\"mt-3 p-2 bg-body-secondary border rounded\">\n <small class=\"text-muted fw-semibold\">Preview:</small>\n <div class=\"font-monospace small\">{{ value || 'Building...' }}</div>\n </div>\n }\n\n <!-- Validation Summary -->\n @if (cleavageForm.invalid && cleavageForm.touched) {\n <div class=\"alert alert-warning mt-3\">\n <small>\n <i class=\"bi bi-exclamation-triangle me-1\"></i>\n <strong>Required field:</strong> Name of Term (NT) must be filled.\n </small>\n </div>\n }\n</form>\n" }]
3195
3193
  }], propDecorators: { value: [{
@@ -3224,7 +3222,7 @@ class MetadataValueEditModal {
3224
3222
  currentUser = signal(null, ...(ngDevMode ? [{ debugName: "currentUser" }] : []));
3225
3223
  userLabGroups = signal([], ...(ngDevMode ? [{ debugName: "userLabGroups" }] : []));
3226
3224
  activeFavoritesTab = 'user';
3227
- sdrfSyntaxService = inject(SdrfSyntaxService$1);
3225
+ sdrfSyntaxService = inject(SdrfSyntaxService);
3228
3226
  specialSyntaxType = signal(null, ...(ngDevMode ? [{ debugName: "specialSyntaxType" }] : []));
3229
3227
  showSpecialInput = signal(false, ...(ngDevMode ? [{ debugName: "showSpecialInput" }] : []));
3230
3228
  specialInputValue = signal('', ...(ngDevMode ? [{ debugName: "specialInputValue" }] : []));
@@ -3332,7 +3330,7 @@ class MetadataValueEditModal {
3332
3330
  return suggestion.displayName || suggestion.value;
3333
3331
  };
3334
3332
  inputFormatter = (suggestion) => {
3335
- if (suggestion && suggestion.ontologyType === OntologyType$1.MS_UNIQUE_VOCABULARIES && suggestion.fullData && suggestion.fullData.name) {
3333
+ if (suggestion && suggestion.ontologyType === OntologyType.MS_UNIQUE_VOCABULARIES && suggestion.fullData && suggestion.fullData.name) {
3336
3334
  if (suggestion.fullData.accession) {
3337
3335
  return `NT=${suggestion.fullData.name};AC=${suggestion.fullData.accession}`;
3338
3336
  }
@@ -3624,7 +3622,7 @@ class MetadataValueEditModal {
3624
3622
  let value;
3625
3623
  if (typeof rawValue === 'object' && rawValue !== null) {
3626
3624
  // For MS Unique Vocabularies, use SDRF formatting
3627
- if (rawValue.ontologyType === OntologyType$1.MS_UNIQUE_VOCABULARIES && rawValue.fullData && rawValue.fullData.name) {
3625
+ if (rawValue.ontologyType === OntologyType.MS_UNIQUE_VOCABULARIES && rawValue.fullData && rawValue.fullData.name) {
3628
3626
  if (rawValue.fullData.accession) {
3629
3627
  value = `NT=${rawValue.fullData.name};AC=${rawValue.fullData.accession}`;
3630
3628
  }
@@ -3650,22 +3648,22 @@ class MetadataValueEditModal {
3650
3648
  }
3651
3649
  }
3652
3650
  }
3653
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataValueEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i3.AuthService }, { token: i4.FavouriteMetadataOptionService }, { token: i4.MetadataColumnService }, { token: i4.MetadataColumnTemplateService }, { token: i3.LabGroupService }], target: i0.ɵɵFactoryTarget.Component });
3654
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: MetadataValueEditModal, isStandalone: true, selector: "ccv-metadata-value-edit-modal", inputs: { config: "config" }, outputs: { valueSaved: "valueSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-pencil-square me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <div class=\"row\">\n <!-- Main Content -->\n <div [class]=\"hasMultiSampleEdit ? 'col-md-8' : 'col-12'\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n\n <!-- Column Information -->\n <div class=\"mb-3\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <small class=\"text-muted\">Column Name:</small>\n <div class=\"fw-semibold\">{{ config.columnName }}</div>\n </div>\n <div class=\"col-md-6\">\n <small class=\"text-muted\">Column Type:</small>\n <div><span class=\"badge bg-light text-dark\">{{ config.columnType }}</span></div>\n </div>\n </div>\n @if (hasOntologyType) {\n <div class=\"mt-2\">\n <small class=\"text-muted\">Ontology Type:</small>\n <div><span class=\"badge bg-info\">{{ ontologyTypeLabel }}</span></div>\n </div>\n }\n </div>\n\n <hr class=\"mb-4\">\n\n <!-- Favorite Options Section -->\n @if (hasFavorites && !isLoadingFavorites()) {\n <div class=\"mb-4\">\n <h6 class=\"text-muted mb-3\">\n <i class=\"bi bi-heart-fill me-2 text-danger\"></i>Recommended Values\n </h6>\n\n <!-- Favorites Navigation Tabs -->\n <ul ngbNav #favoritesNav=\"ngbNav\" [(activeId)]=\"activeFavoritesTab\" class=\"nav-tabs\">\n @if (userFavorites().length > 0) {\n <li [ngbNavItem]=\"'user'\">\n <button ngbNavLink>\n <i class=\"bi bi-person-fill me-1\"></i>Your Favorites\n <span class=\"badge bg-primary ms-1\">{{ userFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, userFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of userFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Your personal favorite values for this column\n </div>\n </div>\n </ng-template>\n </li>\n }\n\n @if (labGroupFavorites().length > 0) {\n <li [ngbNavItem]=\"'lab_group'\">\n <button ngbNavLink>\n <i class=\"bi bi-people-fill me-1\"></i>Lab Group\n <span class=\"badge bg-info ms-1\">{{ labGroupFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, labGroupFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of labGroupFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Favorite values shared by your lab group\n </div>\n </div>\n </ng-template>\n </li>\n }\n\n @if (globalFavorites().length > 0) {\n <li [ngbNavItem]=\"'global'\">\n <button ngbNavLink>\n <i class=\"bi bi-globe me-1\"></i>Global\n <span class=\"badge bg-success ms-1\">{{ globalFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, globalFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of globalFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Globally recommended values for this column\n </div>\n </div>\n </ng-template>\n </li>\n }\n </ul>\n\n <div [ngbNavOutlet]=\"favoritesNav\" class=\"mt-3\"></div>\n\n <hr class=\"my-3\">\n </div>\n }\n\n @if (isLoadingFavorites()) {\n <div class=\"text-center mb-4\">\n <div class=\"spinner-border spinner-border-sm text-primary me-2\"></div>\n <small class=\"text-muted\">Loading favorite options...</small>\n </div>\n <hr class=\"my-3\">\n }\n\n <!-- Value Input Section -->\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <label for=\"metadataValue\" class=\"form-label mb-0\">\n Metadata Value\n @if (hasOntologyType) {\n <span class=\"badge bg-success ms-2\">\n <i class=\"bi bi-search me-1\"></i>Autocomplete Available\n </span>\n }\n @if (specialSyntaxType()) {\n <span class=\"badge bg-info ms-2\">\n <i class=\"bi bi-gear me-1\"></i>{{ getSpecialInputTitle() }}\n </span>\n }\n </label>\n\n @if (specialSyntaxType()) {\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"specialInputToggle\"\n [checked]=\"showSpecialInput()\"\n (change)=\"toggleSpecialInput()\">\n <label class=\"form-check-label small text-muted\" for=\"specialInputToggle\">\n Enhanced Editor\n </label>\n </div>\n }\n </div>\n\n <!-- Special SDRF Syntax Input Components -->\n @if (showSpecialInput() && specialSyntaxType()) {\n <div class=\"border rounded p-3 mb-3 bg-body-secondary\">\n <div class=\"mb-2\">\n <small class=\"text-muted fw-semibold\">\n <i class=\"bi bi-gear me-1\"></i>{{ getSpecialInputTitle() }}\n </small>\n </div>\n\n @switch (specialSyntaxType()) {\n @case ('age') {\n <ccv-sdrf-age-input\n [value]=\"specialInputValue()\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-age-input>\n }\n @case ('modification') {\n <ccv-sdrf-modification-input\n [value]=\"specialInputValue()\"\n [columnId]=\"config.columnId ?? null\"\n [templateId]=\"config.templateId ?? null\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-modification-input>\n }\n @case ('cleavage') {\n <ccv-sdrf-cleavage-input\n [value]=\"specialInputValue()\"\n [columnId]=\"config.columnId ?? null\"\n [templateId]=\"config.templateId ?? null\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-cleavage-input>\n }\n @case ('spiked_compound') {\n <ccv-sdrf-spiked-compound-input\n [value]=\"specialInputValue()\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-spiked-compound-input>\n }\n }\n\n <div class=\"form-text mt-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Use the enhanced editor for structured input, or toggle off for manual text entry.\n </div>\n </div>\n }\n\n @if (hasOntologyType && !showSpecialInput()) {\n <!-- Search type selection for ontology -->\n <div class=\"mb-2\">\n <small class=\"text-muted d-block mb-1\">Search Mode:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n\n <!-- Typeahead input for ontology suggestions -->\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.border-success]=\"selectedFavorite() !== null\"\n id=\"metadataValue\"\n formControlName=\"value\"\n placeholder=\"Type to search ontology suggestions...\"\n [ngbTypeahead]=\"searchOntology\"\n [resultFormatter]=\"formatSuggestion\"\n [inputFormatter]=\"inputFormatter\"\n (selectItem)=\"onSuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\">\n @if (isLoadingSuggestions()) {\n <span class=\"input-group-text\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </span>\n } @else {\n <span class=\"input-group-text\">\n <i class=\"bi bi-search text-muted\"></i>\n </span>\n }\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearValue()\"\n title=\"Clear value\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Search {{ searchType() === 'icontains' ? 'contains' : 'starts with' }} for {{ ontologyTypeLabel }} ontology\n </div>\n } @else if (!showSpecialInput()) {\n <!-- Regular input when no ontology type or special input is disabled -->\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.border-success]=\"selectedFavorite() !== null\"\n id=\"metadataValue\"\n formControlName=\"value\"\n placeholder=\"Enter metadata value\">\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearValue()\"\n title=\"Clear value\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n <div class=\"form-text text-muted\">\n @if (specialSyntaxType()) {\n Manual text entry mode. Use the Enhanced Editor toggle for structured {{ specialSyntaxType() }} input.\n } @else {\n Enter the value manually (no ontology suggestions available for this column type)\n }\n </div>\n }\n </div>\n\n <!-- Character count and value info -->\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Characters: {{ editForm.get('value')?.value?.length || 0 }}/500\n </small>\n @if (getSelectedFavoriteInfo()) {\n <small class=\"text-info\">\n <i class=\"bi bi-info-circle me-1\"></i>{{ getSelectedFavoriteInfo() }}\n </small>\n }\n </div>\n </div>\n\n </form>\n </div>\n\n <!-- Sample Selection Panel -->\n @if (hasMultiSampleEdit) {\n <div class=\"col-md-4\">\n <div class=\"border-start ps-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h6 class=\"mb-0 text-muted\">\n <i class=\"bi bi-list-check me-1\"></i>Apply to Samples\n </h6>\n <span class=\"badge bg-primary\">\n {{ selectedSampleCount }}/{{ totalSampleCount }}\n </span>\n </div>\n\n <!-- Select All/Clear All Controls -->\n <div class=\"d-flex gap-1 mb-3\">\n <button \n type=\"button\" \n class=\"btn btn-outline-primary btn-sm flex-fill\"\n (click)=\"selectAllSamples()\"\n [disabled]=\"selectedSampleCount === totalSampleCount\">\n <i class=\"bi bi-check-all me-1\"></i>All\n </button>\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm flex-fill\"\n (click)=\"clearSampleSelection()\"\n [disabled]=\"selectedSampleCount === 0\">\n <i class=\"bi bi-x me-1\"></i>Clear\n </button>\n </div>\n\n <!-- Sample List -->\n <div class=\"sample-list\" style=\"max-height: 400px; overflow-y: auto;\">\n @for (sample of sampleData; track sample.index) {\n <div class=\"form-check d-flex align-items-center mb-2 p-2 rounded border-bottom\">\n <input \n class=\"form-check-input me-2\" \n type=\"checkbox\" \n [id]=\"'sample-' + sample.index\"\n [checked]=\"isSampleSelected(sample.index)\"\n (change)=\"toggleSampleSelection(sample.index)\">\n <label \n [for]=\"'sample-' + sample.index\" \n class=\"form-check-label flex-grow-1 cursor-pointer\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <div>\n <div class=\"fw-semibold\">Sample {{ sample.index }}</div>\n @if (sample.sourceName) {\n <small class=\"text-muted\">{{ sample.sourceName }}</small>\n }\n </div>\n <div class=\"text-end\">\n <small class=\"text-muted\">Current:</small>\n <div class=\"small fw-mono\" [title]=\"sample.value || ''\">\n {{ (sample.value && sample.value.length > 15) ? (sample.value | slice:0:15) + '...' : (sample.value || '-') }}\n </div>\n </div>\n </div>\n </label>\n </div>\n }\n </div>\n\n @if (selectedSampleCount > 0) {\n <div class=\"alert alert-info mt-3 p-2\">\n <small>\n <i class=\"bi bi-info-circle me-1\"></i>\n The value will be applied to {{ selectedSampleCount }} selected sample{{ selectedSampleCount !== 1 ? 's' : '' }}.\n </small>\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n\n<div class=\"modal-footer\">\n <div class=\"me-auto\">\n @if (editForm.get('value')?.value && !isLoading() && !isFromFavoriteManagement) {\n <div class=\"btn-group dropup\" ngbDropdown>\n <button\n type=\"button\"\n class=\"btn btn-outline-warning btn-sm dropdown-toggle\"\n ngbDropdownToggle>\n <i class=\"bi bi-heart me-1\"></i>Add to Favorites\n </button>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('user')\">\n <i class=\"bi bi-person me-2\"></i>Personal Favorite\n </button>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('lab_group')\">\n <i class=\"bi bi-people me-2\"></i>Lab Group Favorite\n </button>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('global')\">\n <i class=\"bi bi-globe me-2\"></i>Global Recommendation\n </button>\n </div>\n </div>\n }\n </div>\n\n <div class=\"d-flex gap-2\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n Save Value\n </button>\n </div>\n</div>\n", styles: [".sample-list .form-check{border:1px solid transparent;transition:all .2s ease}.sample-list .form-check:hover{background-color:rgba(var(--bs-primary-rgb),.05);border-color:rgba(var(--bs-primary-rgb),.2)}.sample-list .form-check .form-check-input:checked~.form-check-label .fw-semibold{color:var(--bs-primary)}.cursor-pointer{cursor:pointer}.fw-mono{font-family:var(--bs-font-monospace)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }, { kind: "directive", type: i2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i2.NgbNavContent, selector: "ng-template[ngbNavContent]" }, { kind: "directive", type: i2.NgbNav, selector: "[ngbNav]", inputs: ["activeId", "animation", "destroyOnHide", "orientation", "roles", "keyboard"], outputs: ["activeIdChange", "shown", "hidden", "navChange"], exportAs: ["ngbNav"] }, { kind: "directive", type: i2.NgbNavItem, selector: "[ngbNavItem]", inputs: ["destroyOnHide", "disabled", "domId", "ngbNavItem"], outputs: ["shown", "hidden"], exportAs: ["ngbNavItem"] }, { kind: "directive", type: i2.NgbNavItemRole, selector: "[ngbNavItem]:not(ng-container)" }, { kind: "directive", type: i2.NgbNavLinkButton, selector: "button[ngbNavLink]" }, { kind: "directive", type: i2.NgbNavLinkBase, selector: "[ngbNavLink]" }, { kind: "component", type: i2.NgbNavOutlet, selector: "[ngbNavOutlet]", inputs: ["paneRole", "ngbNavOutlet"] }, { kind: "directive", type: i2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: NgbNavModule }, { kind: "component", type: SdrfAgeInput$1, selector: "ccv-sdrf-age-input", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: SdrfModificationInput$1, selector: "ccv-sdrf-modification-input", inputs: ["value", "templateId", "columnId"], outputs: ["valueChange"] }, { kind: "component", type: SdrfCleavageInput$1, selector: "ccv-sdrf-cleavage-input", inputs: ["value", "templateId", "columnId"], outputs: ["valueChange"] }, { kind: "component", type: SdrfSpikedCompoundInput$1, selector: "ccv-sdrf-spiked-compound-input", inputs: ["value"], outputs: ["valueChange"] }, { kind: "pipe", type: i5.SlicePipe, name: "slice" }] });
3651
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataValueEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i3.AuthService }, { token: FavouriteMetadataOptionService }, { token: MetadataColumnService }, { token: MetadataColumnTemplateService }, { token: i3.LabGroupService }], target: i0.ɵɵFactoryTarget.Component });
3652
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: MetadataValueEditModal, isStandalone: true, selector: "ccv-metadata-value-edit-modal", inputs: { config: "config" }, outputs: { valueSaved: "valueSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-pencil-square me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <div class=\"row\">\n <!-- Main Content -->\n <div [class]=\"hasMultiSampleEdit ? 'col-md-8' : 'col-12'\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n\n <!-- Column Information -->\n <div class=\"mb-3\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <small class=\"text-muted\">Column Name:</small>\n <div class=\"fw-semibold\">{{ config.columnName }}</div>\n </div>\n <div class=\"col-md-6\">\n <small class=\"text-muted\">Column Type:</small>\n <div><span class=\"badge bg-light text-dark\">{{ config.columnType }}</span></div>\n </div>\n </div>\n @if (hasOntologyType) {\n <div class=\"mt-2\">\n <small class=\"text-muted\">Ontology Type:</small>\n <div><span class=\"badge bg-info\">{{ ontologyTypeLabel }}</span></div>\n </div>\n }\n </div>\n\n <hr class=\"mb-4\">\n\n <!-- Favorite Options Section -->\n @if (hasFavorites && !isLoadingFavorites()) {\n <div class=\"mb-4\">\n <h6 class=\"text-muted mb-3\">\n <i class=\"bi bi-heart-fill me-2 text-danger\"></i>Recommended Values\n </h6>\n\n <!-- Favorites Navigation Tabs -->\n <ul ngbNav #favoritesNav=\"ngbNav\" [(activeId)]=\"activeFavoritesTab\" class=\"nav-tabs\">\n @if (userFavorites().length > 0) {\n <li [ngbNavItem]=\"'user'\">\n <button ngbNavLink>\n <i class=\"bi bi-person-fill me-1\"></i>Your Favorites\n <span class=\"badge bg-primary ms-1\">{{ userFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, userFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of userFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Your personal favorite values for this column\n </div>\n </div>\n </ng-template>\n </li>\n }\n\n @if (labGroupFavorites().length > 0) {\n <li [ngbNavItem]=\"'lab_group'\">\n <button ngbNavLink>\n <i class=\"bi bi-people-fill me-1\"></i>Lab Group\n <span class=\"badge bg-info ms-1\">{{ labGroupFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, labGroupFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of labGroupFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Favorite values shared by your lab group\n </div>\n </div>\n </ng-template>\n </li>\n }\n\n @if (globalFavorites().length > 0) {\n <li [ngbNavItem]=\"'global'\">\n <button ngbNavLink>\n <i class=\"bi bi-globe me-1\"></i>Global\n <span class=\"badge bg-success ms-1\">{{ globalFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, globalFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of globalFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Globally recommended values for this column\n </div>\n </div>\n </ng-template>\n </li>\n }\n </ul>\n\n <div [ngbNavOutlet]=\"favoritesNav\" class=\"mt-3\"></div>\n\n <hr class=\"my-3\">\n </div>\n }\n\n @if (isLoadingFavorites()) {\n <div class=\"text-center mb-4\">\n <div class=\"spinner-border spinner-border-sm text-primary me-2\"></div>\n <small class=\"text-muted\">Loading favorite options...</small>\n </div>\n <hr class=\"my-3\">\n }\n\n <!-- Value Input Section -->\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <label for=\"metadataValue\" class=\"form-label mb-0\">\n Metadata Value\n @if (hasOntologyType) {\n <span class=\"badge bg-success ms-2\">\n <i class=\"bi bi-search me-1\"></i>Autocomplete Available\n </span>\n }\n @if (specialSyntaxType()) {\n <span class=\"badge bg-info ms-2\">\n <i class=\"bi bi-gear me-1\"></i>{{ getSpecialInputTitle() }}\n </span>\n }\n </label>\n\n @if (specialSyntaxType()) {\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"specialInputToggle\"\n [checked]=\"showSpecialInput()\"\n (change)=\"toggleSpecialInput()\">\n <label class=\"form-check-label small text-muted\" for=\"specialInputToggle\">\n Enhanced Editor\n </label>\n </div>\n }\n </div>\n\n <!-- Special SDRF Syntax Input Components -->\n @if (showSpecialInput() && specialSyntaxType()) {\n <div class=\"border rounded p-3 mb-3 bg-body-secondary\">\n <div class=\"mb-2\">\n <small class=\"text-muted fw-semibold\">\n <i class=\"bi bi-gear me-1\"></i>{{ getSpecialInputTitle() }}\n </small>\n </div>\n\n @switch (specialSyntaxType()) {\n @case ('age') {\n <ccv-sdrf-age-input\n [value]=\"specialInputValue()\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-age-input>\n }\n @case ('modification') {\n <ccv-sdrf-modification-input\n [value]=\"specialInputValue()\"\n [columnId]=\"config.columnId ?? null\"\n [templateId]=\"config.templateId ?? null\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-modification-input>\n }\n @case ('cleavage') {\n <ccv-sdrf-cleavage-input\n [value]=\"specialInputValue()\"\n [columnId]=\"config.columnId ?? null\"\n [templateId]=\"config.templateId ?? null\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-cleavage-input>\n }\n @case ('spiked_compound') {\n <ccv-sdrf-spiked-compound-input\n [value]=\"specialInputValue()\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-spiked-compound-input>\n }\n }\n\n <div class=\"form-text mt-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Use the enhanced editor for structured input, or toggle off for manual text entry.\n </div>\n </div>\n }\n\n @if (hasOntologyType && !showSpecialInput()) {\n <!-- Search type selection for ontology -->\n <div class=\"mb-2\">\n <small class=\"text-muted d-block mb-1\">Search Mode:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n\n <!-- Typeahead input for ontology suggestions -->\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.border-success]=\"selectedFavorite() !== null\"\n id=\"metadataValue\"\n formControlName=\"value\"\n placeholder=\"Type to search ontology suggestions...\"\n [ngbTypeahead]=\"searchOntology\"\n [resultFormatter]=\"formatSuggestion\"\n [inputFormatter]=\"inputFormatter\"\n (selectItem)=\"onSuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\">\n @if (isLoadingSuggestions()) {\n <span class=\"input-group-text\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </span>\n } @else {\n <span class=\"input-group-text\">\n <i class=\"bi bi-search text-muted\"></i>\n </span>\n }\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearValue()\"\n title=\"Clear value\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Search {{ searchType() === 'icontains' ? 'contains' : 'starts with' }} for {{ ontologyTypeLabel }} ontology\n </div>\n } @else if (!showSpecialInput()) {\n <!-- Regular input when no ontology type or special input is disabled -->\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.border-success]=\"selectedFavorite() !== null\"\n id=\"metadataValue\"\n formControlName=\"value\"\n placeholder=\"Enter metadata value\">\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearValue()\"\n title=\"Clear value\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n <div class=\"form-text text-muted\">\n @if (specialSyntaxType()) {\n Manual text entry mode. Use the Enhanced Editor toggle for structured {{ specialSyntaxType() }} input.\n } @else {\n Enter the value manually (no ontology suggestions available for this column type)\n }\n </div>\n }\n </div>\n\n <!-- Character count and value info -->\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Characters: {{ editForm.get('value')?.value?.length || 0 }}/500\n </small>\n @if (getSelectedFavoriteInfo()) {\n <small class=\"text-info\">\n <i class=\"bi bi-info-circle me-1\"></i>{{ getSelectedFavoriteInfo() }}\n </small>\n }\n </div>\n </div>\n\n </form>\n </div>\n\n <!-- Sample Selection Panel -->\n @if (hasMultiSampleEdit) {\n <div class=\"col-md-4\">\n <div class=\"border-start ps-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h6 class=\"mb-0 text-muted\">\n <i class=\"bi bi-list-check me-1\"></i>Apply to Samples\n </h6>\n <span class=\"badge bg-primary\">\n {{ selectedSampleCount }}/{{ totalSampleCount }}\n </span>\n </div>\n\n <!-- Select All/Clear All Controls -->\n <div class=\"d-flex gap-1 mb-3\">\n <button \n type=\"button\" \n class=\"btn btn-outline-primary btn-sm flex-fill\"\n (click)=\"selectAllSamples()\"\n [disabled]=\"selectedSampleCount === totalSampleCount\">\n <i class=\"bi bi-check-all me-1\"></i>All\n </button>\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm flex-fill\"\n (click)=\"clearSampleSelection()\"\n [disabled]=\"selectedSampleCount === 0\">\n <i class=\"bi bi-x me-1\"></i>Clear\n </button>\n </div>\n\n <!-- Sample List -->\n <div class=\"sample-list\" style=\"max-height: 400px; overflow-y: auto;\">\n @for (sample of sampleData; track sample.index) {\n <div class=\"form-check d-flex align-items-center mb-2 p-2 rounded border-bottom\">\n <input \n class=\"form-check-input me-2\" \n type=\"checkbox\" \n [id]=\"'sample-' + sample.index\"\n [checked]=\"isSampleSelected(sample.index)\"\n (change)=\"toggleSampleSelection(sample.index)\">\n <label \n [for]=\"'sample-' + sample.index\" \n class=\"form-check-label flex-grow-1 cursor-pointer\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <div>\n <div class=\"fw-semibold\">Sample {{ sample.index }}</div>\n @if (sample.sourceName) {\n <small class=\"text-muted\">{{ sample.sourceName }}</small>\n }\n </div>\n <div class=\"text-end\">\n <small class=\"text-muted\">Current:</small>\n <div class=\"small fw-mono\" [title]=\"sample.value || ''\">\n {{ (sample.value && sample.value.length > 15) ? (sample.value | slice:0:15) + '...' : (sample.value || '-') }}\n </div>\n </div>\n </div>\n </label>\n </div>\n }\n </div>\n\n @if (selectedSampleCount > 0) {\n <div class=\"alert alert-info mt-3 p-2\">\n <small>\n <i class=\"bi bi-info-circle me-1\"></i>\n The value will be applied to {{ selectedSampleCount }} selected sample{{ selectedSampleCount !== 1 ? 's' : '' }}.\n </small>\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n\n<div class=\"modal-footer\">\n <div class=\"me-auto\">\n @if (editForm.get('value')?.value && !isLoading() && !isFromFavoriteManagement) {\n <div class=\"btn-group dropup\" ngbDropdown>\n <button\n type=\"button\"\n class=\"btn btn-outline-warning btn-sm dropdown-toggle\"\n ngbDropdownToggle>\n <i class=\"bi bi-heart me-1\"></i>Add to Favorites\n </button>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('user')\">\n <i class=\"bi bi-person me-2\"></i>Personal Favorite\n </button>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('lab_group')\">\n <i class=\"bi bi-people me-2\"></i>Lab Group Favorite\n </button>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('global')\">\n <i class=\"bi bi-globe me-2\"></i>Global Recommendation\n </button>\n </div>\n </div>\n }\n </div>\n\n <div class=\"d-flex gap-2\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n Save Value\n </button>\n </div>\n</div>\n", styles: [".sample-list .form-check{border:1px solid transparent;transition:all .2s ease}.sample-list .form-check:hover{background-color:rgba(var(--bs-primary-rgb),.05);border-color:rgba(var(--bs-primary-rgb),.2)}.sample-list .form-check .form-check-input:checked~.form-check-label .fw-semibold{color:var(--bs-primary)}.cursor-pointer{cursor:pointer}.fw-mono{font-family:var(--bs-font-monospace)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }, { kind: "directive", type: i2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "directive", type: i2.NgbNavContent, selector: "ng-template[ngbNavContent]" }, { kind: "directive", type: i2.NgbNav, selector: "[ngbNav]", inputs: ["activeId", "animation", "destroyOnHide", "orientation", "roles", "keyboard"], outputs: ["activeIdChange", "shown", "hidden", "navChange"], exportAs: ["ngbNav"] }, { kind: "directive", type: i2.NgbNavItem, selector: "[ngbNavItem]", inputs: ["destroyOnHide", "disabled", "domId", "ngbNavItem"], outputs: ["shown", "hidden"], exportAs: ["ngbNavItem"] }, { kind: "directive", type: i2.NgbNavItemRole, selector: "[ngbNavItem]:not(ng-container)" }, { kind: "directive", type: i2.NgbNavLinkButton, selector: "button[ngbNavLink]" }, { kind: "directive", type: i2.NgbNavLinkBase, selector: "[ngbNavLink]" }, { kind: "component", type: i2.NgbNavOutlet, selector: "[ngbNavOutlet]", inputs: ["paneRole", "ngbNavOutlet"] }, { kind: "directive", type: i2.NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "ngmodule", type: NgbNavModule }, { kind: "component", type: SdrfAgeInput, selector: "ccv-sdrf-age-input", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: SdrfModificationInput, selector: "ccv-sdrf-modification-input", inputs: ["value", "templateId", "columnId"], outputs: ["valueChange"] }, { kind: "component", type: SdrfCleavageInput, selector: "ccv-sdrf-cleavage-input", inputs: ["value", "templateId", "columnId"], outputs: ["valueChange"] }, { kind: "component", type: SdrfSpikedCompoundInput, selector: "ccv-sdrf-spiked-compound-input", inputs: ["value"], outputs: ["valueChange"] }, { kind: "pipe", type: i5.SlicePipe, name: "slice" }] });
3655
3653
  }
3656
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataValueEditModal, decorators: [{
3654
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataValueEditModal, decorators: [{
3657
3655
  type: Component,
3658
3656
  args: [{ selector: 'ccv-metadata-value-edit-modal', standalone: true, imports: [
3659
3657
  CommonModule,
3660
3658
  ReactiveFormsModule,
3661
3659
  NgbModule,
3662
3660
  NgbNavModule,
3663
- SdrfAgeInput$1,
3664
- SdrfModificationInput$1,
3665
- SdrfCleavageInput$1,
3666
- SdrfSpikedCompoundInput$1
3661
+ SdrfAgeInput,
3662
+ SdrfModificationInput,
3663
+ SdrfCleavageInput,
3664
+ SdrfSpikedCompoundInput
3667
3665
  ], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-pencil-square me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <div class=\"row\">\n <!-- Main Content -->\n <div [class]=\"hasMultiSampleEdit ? 'col-md-8' : 'col-12'\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n\n <!-- Column Information -->\n <div class=\"mb-3\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <small class=\"text-muted\">Column Name:</small>\n <div class=\"fw-semibold\">{{ config.columnName }}</div>\n </div>\n <div class=\"col-md-6\">\n <small class=\"text-muted\">Column Type:</small>\n <div><span class=\"badge bg-light text-dark\">{{ config.columnType }}</span></div>\n </div>\n </div>\n @if (hasOntologyType) {\n <div class=\"mt-2\">\n <small class=\"text-muted\">Ontology Type:</small>\n <div><span class=\"badge bg-info\">{{ ontologyTypeLabel }}</span></div>\n </div>\n }\n </div>\n\n <hr class=\"mb-4\">\n\n <!-- Favorite Options Section -->\n @if (hasFavorites && !isLoadingFavorites()) {\n <div class=\"mb-4\">\n <h6 class=\"text-muted mb-3\">\n <i class=\"bi bi-heart-fill me-2 text-danger\"></i>Recommended Values\n </h6>\n\n <!-- Favorites Navigation Tabs -->\n <ul ngbNav #favoritesNav=\"ngbNav\" [(activeId)]=\"activeFavoritesTab\" class=\"nav-tabs\">\n @if (userFavorites().length > 0) {\n <li [ngbNavItem]=\"'user'\">\n <button ngbNavLink>\n <i class=\"bi bi-person-fill me-1\"></i>Your Favorites\n <span class=\"badge bg-primary ms-1\">{{ userFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, userFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of userFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Your personal favorite values for this column\n </div>\n </div>\n </ng-template>\n </li>\n }\n\n @if (labGroupFavorites().length > 0) {\n <li [ngbNavItem]=\"'lab_group'\">\n <button ngbNavLink>\n <i class=\"bi bi-people-fill me-1\"></i>Lab Group\n <span class=\"badge bg-info ms-1\">{{ labGroupFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, labGroupFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of labGroupFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Favorite values shared by your lab group\n </div>\n </div>\n </ng-template>\n </li>\n }\n\n @if (globalFavorites().length > 0) {\n <li [ngbNavItem]=\"'global'\">\n <button ngbNavLink>\n <i class=\"bi bi-globe me-1\"></i>Global\n <span class=\"badge bg-success ms-1\">{{ globalFavorites().length }}</span>\n </button>\n <ng-template ngbNavContent>\n <div class=\"mt-3\">\n <select \n class=\"form-select\" \n size=\"5\" \n (change)=\"onFavoriteSelected($event, globalFavorites())\">\n <option value=\"\">Select a favorite value...</option>\n @for (favorite of globalFavorites(); track favorite.id) {\n <option [value]=\"favorite.id\">\n {{ getFavoriteDisplayValue(favorite) }}\n @if (hasCustomDisplayValue(favorite)) { (Custom Display) }\n </option>\n }\n </select>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>Globally recommended values for this column\n </div>\n </div>\n </ng-template>\n </li>\n }\n </ul>\n\n <div [ngbNavOutlet]=\"favoritesNav\" class=\"mt-3\"></div>\n\n <hr class=\"my-3\">\n </div>\n }\n\n @if (isLoadingFavorites()) {\n <div class=\"text-center mb-4\">\n <div class=\"spinner-border spinner-border-sm text-primary me-2\"></div>\n <small class=\"text-muted\">Loading favorite options...</small>\n </div>\n <hr class=\"my-3\">\n }\n\n <!-- Value Input Section -->\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <label for=\"metadataValue\" class=\"form-label mb-0\">\n Metadata Value\n @if (hasOntologyType) {\n <span class=\"badge bg-success ms-2\">\n <i class=\"bi bi-search me-1\"></i>Autocomplete Available\n </span>\n }\n @if (specialSyntaxType()) {\n <span class=\"badge bg-info ms-2\">\n <i class=\"bi bi-gear me-1\"></i>{{ getSpecialInputTitle() }}\n </span>\n }\n </label>\n\n @if (specialSyntaxType()) {\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"specialInputToggle\"\n [checked]=\"showSpecialInput()\"\n (change)=\"toggleSpecialInput()\">\n <label class=\"form-check-label small text-muted\" for=\"specialInputToggle\">\n Enhanced Editor\n </label>\n </div>\n }\n </div>\n\n <!-- Special SDRF Syntax Input Components -->\n @if (showSpecialInput() && specialSyntaxType()) {\n <div class=\"border rounded p-3 mb-3 bg-body-secondary\">\n <div class=\"mb-2\">\n <small class=\"text-muted fw-semibold\">\n <i class=\"bi bi-gear me-1\"></i>{{ getSpecialInputTitle() }}\n </small>\n </div>\n\n @switch (specialSyntaxType()) {\n @case ('age') {\n <ccv-sdrf-age-input\n [value]=\"specialInputValue()\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-age-input>\n }\n @case ('modification') {\n <ccv-sdrf-modification-input\n [value]=\"specialInputValue()\"\n [columnId]=\"config.columnId ?? null\"\n [templateId]=\"config.templateId ?? null\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-modification-input>\n }\n @case ('cleavage') {\n <ccv-sdrf-cleavage-input\n [value]=\"specialInputValue()\"\n [columnId]=\"config.columnId ?? null\"\n [templateId]=\"config.templateId ?? null\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-cleavage-input>\n }\n @case ('spiked_compound') {\n <ccv-sdrf-spiked-compound-input\n [value]=\"specialInputValue()\"\n (valueChange)=\"onSpecialInputChange($event)\">\n </ccv-sdrf-spiked-compound-input>\n }\n }\n\n <div class=\"form-text mt-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Use the enhanced editor for structured input, or toggle off for manual text entry.\n </div>\n </div>\n }\n\n @if (hasOntologyType && !showSpecialInput()) {\n <!-- Search type selection for ontology -->\n <div class=\"mb-2\">\n <small class=\"text-muted d-block mb-1\">Search Mode:</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'icontains' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('icontains')\">\n <i class=\"bi bi-search me-1\"></i>Contains\n </button>\n <button\n type=\"button\"\n class=\"btn\"\n [class]=\"searchType() === 'istartswith' ? 'btn-primary' : 'btn-outline-primary'\"\n (click)=\"onSearchTypeChange('istartswith')\">\n <i class=\"bi bi-arrow-right me-1\"></i>Starts with\n </button>\n </div>\n </div>\n\n <!-- Typeahead input for ontology suggestions -->\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.border-success]=\"selectedFavorite() !== null\"\n id=\"metadataValue\"\n formControlName=\"value\"\n placeholder=\"Type to search ontology suggestions...\"\n [ngbTypeahead]=\"searchOntology\"\n [resultFormatter]=\"formatSuggestion\"\n [inputFormatter]=\"inputFormatter\"\n (selectItem)=\"onSuggestionSelected($event)\"\n [editable]=\"true\"\n [showHint]=\"true\"\n [focusFirst]=\"false\"\n container=\"body\">\n @if (isLoadingSuggestions()) {\n <span class=\"input-group-text\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </span>\n } @else {\n <span class=\"input-group-text\">\n <i class=\"bi bi-search text-muted\"></i>\n </span>\n }\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearValue()\"\n title=\"Clear value\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Search {{ searchType() === 'icontains' ? 'contains' : 'starts with' }} for {{ ontologyTypeLabel }} ontology\n </div>\n } @else if (!showSpecialInput()) {\n <!-- Regular input when no ontology type or special input is disabled -->\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.border-success]=\"selectedFavorite() !== null\"\n id=\"metadataValue\"\n formControlName=\"value\"\n placeholder=\"Enter metadata value\">\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearValue()\"\n title=\"Clear value\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n <div class=\"form-text text-muted\">\n @if (specialSyntaxType()) {\n Manual text entry mode. Use the Enhanced Editor toggle for structured {{ specialSyntaxType() }} input.\n } @else {\n Enter the value manually (no ontology suggestions available for this column type)\n }\n </div>\n }\n </div>\n\n <!-- Character count and value info -->\n <div class=\"mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Characters: {{ editForm.get('value')?.value?.length || 0 }}/500\n </small>\n @if (getSelectedFavoriteInfo()) {\n <small class=\"text-info\">\n <i class=\"bi bi-info-circle me-1\"></i>{{ getSelectedFavoriteInfo() }}\n </small>\n }\n </div>\n </div>\n\n </form>\n </div>\n\n <!-- Sample Selection Panel -->\n @if (hasMultiSampleEdit) {\n <div class=\"col-md-4\">\n <div class=\"border-start ps-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h6 class=\"mb-0 text-muted\">\n <i class=\"bi bi-list-check me-1\"></i>Apply to Samples\n </h6>\n <span class=\"badge bg-primary\">\n {{ selectedSampleCount }}/{{ totalSampleCount }}\n </span>\n </div>\n\n <!-- Select All/Clear All Controls -->\n <div class=\"d-flex gap-1 mb-3\">\n <button \n type=\"button\" \n class=\"btn btn-outline-primary btn-sm flex-fill\"\n (click)=\"selectAllSamples()\"\n [disabled]=\"selectedSampleCount === totalSampleCount\">\n <i class=\"bi bi-check-all me-1\"></i>All\n </button>\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm flex-fill\"\n (click)=\"clearSampleSelection()\"\n [disabled]=\"selectedSampleCount === 0\">\n <i class=\"bi bi-x me-1\"></i>Clear\n </button>\n </div>\n\n <!-- Sample List -->\n <div class=\"sample-list\" style=\"max-height: 400px; overflow-y: auto;\">\n @for (sample of sampleData; track sample.index) {\n <div class=\"form-check d-flex align-items-center mb-2 p-2 rounded border-bottom\">\n <input \n class=\"form-check-input me-2\" \n type=\"checkbox\" \n [id]=\"'sample-' + sample.index\"\n [checked]=\"isSampleSelected(sample.index)\"\n (change)=\"toggleSampleSelection(sample.index)\">\n <label \n [for]=\"'sample-' + sample.index\" \n class=\"form-check-label flex-grow-1 cursor-pointer\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <div>\n <div class=\"fw-semibold\">Sample {{ sample.index }}</div>\n @if (sample.sourceName) {\n <small class=\"text-muted\">{{ sample.sourceName }}</small>\n }\n </div>\n <div class=\"text-end\">\n <small class=\"text-muted\">Current:</small>\n <div class=\"small fw-mono\" [title]=\"sample.value || ''\">\n {{ (sample.value && sample.value.length > 15) ? (sample.value | slice:0:15) + '...' : (sample.value || '-') }}\n </div>\n </div>\n </div>\n </label>\n </div>\n }\n </div>\n\n @if (selectedSampleCount > 0) {\n <div class=\"alert alert-info mt-3 p-2\">\n <small>\n <i class=\"bi bi-info-circle me-1\"></i>\n The value will be applied to {{ selectedSampleCount }} selected sample{{ selectedSampleCount !== 1 ? 's' : '' }}.\n </small>\n </div>\n }\n </div>\n </div>\n }\n </div>\n</div>\n\n<div class=\"modal-footer\">\n <div class=\"me-auto\">\n @if (editForm.get('value')?.value && !isLoading() && !isFromFavoriteManagement) {\n <div class=\"btn-group dropup\" ngbDropdown>\n <button\n type=\"button\"\n class=\"btn btn-outline-warning btn-sm dropdown-toggle\"\n ngbDropdownToggle>\n <i class=\"bi bi-heart me-1\"></i>Add to Favorites\n </button>\n <div class=\"dropdown-menu\" ngbDropdownMenu>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('user')\">\n <i class=\"bi bi-person me-2\"></i>Personal Favorite\n </button>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('lab_group')\">\n <i class=\"bi bi-people me-2\"></i>Lab Group Favorite\n </button>\n <button\n type=\"button\"\n class=\"dropdown-item\"\n (click)=\"addToFavorites('global')\">\n <i class=\"bi bi-globe me-2\"></i>Global Recommendation\n </button>\n </div>\n </div>\n }\n </div>\n\n <div class=\"d-flex gap-2\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n Save Value\n </button>\n </div>\n</div>\n", styles: [".sample-list .form-check{border:1px solid transparent;transition:all .2s ease}.sample-list .form-check:hover{background-color:rgba(var(--bs-primary-rgb),.05);border-color:rgba(var(--bs-primary-rgb),.2)}.sample-list .form-check .form-check-input:checked~.form-check-label .fw-semibold{color:var(--bs-primary)}.cursor-pointer{cursor:pointer}.fw-mono{font-family:var(--bs-font-monospace)}\n"] }]
3668
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i3.AuthService }, { type: i4.FavouriteMetadataOptionService }, { type: i4.MetadataColumnService }, { type: i4.MetadataColumnTemplateService }, { type: i3.LabGroupService }], propDecorators: { config: [{
3666
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i3.AuthService }, { type: FavouriteMetadataOptionService }, { type: MetadataColumnService }, { type: MetadataColumnTemplateService }, { type: i3.LabGroupService }], propDecorators: { config: [{
3669
3667
  type: Input
3670
3668
  }], valueSaved: [{
3671
3669
  type: Output
@@ -3743,10 +3741,10 @@ class ExcelExportModalComponent {
3743
3741
  cancel() {
3744
3742
  this.activeModal.dismiss();
3745
3743
  }
3746
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ExcelExportModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3747
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: ExcelExportModalComponent, isStandalone: true, selector: "ccv-excel-export-modal", ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">Excel Export Options</h4>\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\" (click)=\"cancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <!-- Lab Groups Section -->\n <div class=\"mb-4\">\n <h6 class=\"fw-semibold mb-3\">\n <i class=\"bi bi-people me-2\"></i>Lab Group Recommendations\n </h6>\n \n <div class=\"form-check mb-2\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_none\"\n [checked]=\"includeLabGroups() === 'none'\"\n (change)=\"onIncludeLabGroupsChange('none')\">\n <label class=\"form-check-label\" for=\"labGroups_none\">\n <strong>None</strong> - Only user-specific and global recommendations\n </label>\n </div>\n\n <div class=\"form-check mb-2\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_selected\"\n [checked]=\"includeLabGroups() === 'selected'\"\n (change)=\"onIncludeLabGroupsChange('selected')\">\n <label class=\"form-check-label\" for=\"labGroups_selected\">\n <strong>Selected groups</strong> - Choose specific lab groups\n </label>\n </div>\n\n <div class=\"form-check mb-3\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_all\"\n [checked]=\"includeLabGroups() === 'all'\"\n (change)=\"onIncludeLabGroupsChange('all')\">\n <label class=\"form-check-label\" for=\"labGroups_all\">\n <strong>All groups</strong> - Include all available lab group recommendations\n </label>\n </div>\n\n <!-- Lab Group Selection -->\n @if (showLabGroupSelection()) {\n <div class=\"border rounded p-3 bg-light\">\n @if (isLoadingLabGroups()) {\n <div class=\"text-center\">\n <div class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></div>\n Loading lab groups...\n </div>\n } @else if (availableLabGroups().length === 0) {\n <div class=\"text-muted text-center\">\n <i class=\"bi bi-info-circle me-2\"></i>\n No lab groups available\n </div>\n } @else {\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h6 class=\"mb-0\">Select Lab Groups:</h6>\n <div class=\"btn-group btn-group-sm\">\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm\"\n (click)=\"selectAllLabGroups()\"\n [disabled]=\"selectedLabGroupIds().length === availableLabGroups().length\">\n Select All\n </button>\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm\"\n (click)=\"deselectAllLabGroups()\"\n [disabled]=\"selectedLabGroupIds().length === 0\">\n Clear All\n </button>\n </div>\n </div>\n\n <div class=\"row\">\n @for (labGroup of availableLabGroups(); track labGroup.id) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"form-check\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n [id]=\"'labGroup_' + labGroup.id\"\n [checked]=\"isLabGroupSelected(labGroup.id)\"\n (change)=\"onLabGroupToggle(labGroup.id, $event.target.checked)\">\n <label class=\"form-check-label\" [for]=\"'labGroup_' + labGroup.id\">\n {{ labGroup.name }}\n @if (labGroup.description) {\n <small class=\"text-muted d-block\">{{ labGroup.description }}</small>\n }\n </label>\n </div>\n </div>\n }\n </div>\n\n @if (selectedLabGroupIds().length > 0) {\n <div class=\"mt-2\">\n <small class=\"text-success\">\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ selectedLabGroupIds().length }} group{{ selectedLabGroupIds().length > 1 ? 's' : '' }} selected\n </small>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Pools Section -->\n <div class=\"mb-4\">\n <h6 class=\"fw-semibold mb-3\">\n <i class=\"bi bi-collection me-2\"></i>Sample Pools\n </h6>\n <div class=\"form-check\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"includePools\"\n [checked]=\"includePools()\"\n (change)=\"includePools.set($event.target.checked)\">\n <label class=\"form-check-label\" for=\"includePools\">\n Include sample pools in export\n </label>\n <small class=\"form-text text-muted d-block mt-1\">\n Creates additional worksheets with pool information and mappings\n </small>\n </div>\n </div>\n\n <!-- Summary -->\n <div class=\"alert alert-info\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-info-circle me-2\"></i>Export Summary\n </h6>\n <ul class=\"mb-0\">\n <li>\n <strong>Lab Groups:</strong>\n @switch (includeLabGroups()) {\n @case ('none') { None - only user and global recommendations }\n @case ('selected') { \n @if (selectedLabGroupIds().length > 0) {\n {{ selectedLabGroupIds().length }} selected group{{ selectedLabGroupIds().length > 1 ? 's' : '' }}\n } @else {\n No groups selected\n }\n }\n @case ('all') { All available lab groups }\n }\n </li>\n <li><strong>Sample Pools:</strong> {{ includePools() ? 'Included' : 'Excluded' }}</li>\n </ul>\n </div>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"cancel()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\"\n [disabled]=\"!canConfirm()\"\n (click)=\"confirm()\">\n <i class=\"bi bi-file-earmark-spreadsheet me-2\"></i>\n Export Excel\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] });
3744
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ExcelExportModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3745
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: ExcelExportModalComponent, isStandalone: true, selector: "ccv-excel-export-modal", ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">Excel Export Options</h4>\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\" (click)=\"cancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <!-- Lab Groups Section -->\n <div class=\"mb-4\">\n <h6 class=\"fw-semibold mb-3\">\n <i class=\"bi bi-people me-2\"></i>Lab Group Recommendations\n </h6>\n \n <div class=\"form-check mb-2\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_none\"\n [checked]=\"includeLabGroups() === 'none'\"\n (change)=\"onIncludeLabGroupsChange('none')\">\n <label class=\"form-check-label\" for=\"labGroups_none\">\n <strong>None</strong> - Only user-specific and global recommendations\n </label>\n </div>\n\n <div class=\"form-check mb-2\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_selected\"\n [checked]=\"includeLabGroups() === 'selected'\"\n (change)=\"onIncludeLabGroupsChange('selected')\">\n <label class=\"form-check-label\" for=\"labGroups_selected\">\n <strong>Selected groups</strong> - Choose specific lab groups\n </label>\n </div>\n\n <div class=\"form-check mb-3\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_all\"\n [checked]=\"includeLabGroups() === 'all'\"\n (change)=\"onIncludeLabGroupsChange('all')\">\n <label class=\"form-check-label\" for=\"labGroups_all\">\n <strong>All groups</strong> - Include all available lab group recommendations\n </label>\n </div>\n\n <!-- Lab Group Selection -->\n @if (showLabGroupSelection()) {\n <div class=\"border rounded p-3 bg-light\">\n @if (isLoadingLabGroups()) {\n <div class=\"text-center\">\n <div class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></div>\n Loading lab groups...\n </div>\n } @else if (availableLabGroups().length === 0) {\n <div class=\"text-muted text-center\">\n <i class=\"bi bi-info-circle me-2\"></i>\n No lab groups available\n </div>\n } @else {\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h6 class=\"mb-0\">Select Lab Groups:</h6>\n <div class=\"btn-group btn-group-sm\">\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm\"\n (click)=\"selectAllLabGroups()\"\n [disabled]=\"selectedLabGroupIds().length === availableLabGroups().length\">\n Select All\n </button>\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm\"\n (click)=\"deselectAllLabGroups()\"\n [disabled]=\"selectedLabGroupIds().length === 0\">\n Clear All\n </button>\n </div>\n </div>\n\n <div class=\"row\">\n @for (labGroup of availableLabGroups(); track labGroup.id) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"form-check\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n [id]=\"'labGroup_' + labGroup.id\"\n [checked]=\"isLabGroupSelected(labGroup.id)\"\n (change)=\"onLabGroupToggle(labGroup.id, $event.target.checked)\">\n <label class=\"form-check-label\" [for]=\"'labGroup_' + labGroup.id\">\n {{ labGroup.name }}\n @if (labGroup.description) {\n <small class=\"text-muted d-block\">{{ labGroup.description }}</small>\n }\n </label>\n </div>\n </div>\n }\n </div>\n\n @if (selectedLabGroupIds().length > 0) {\n <div class=\"mt-2\">\n <small class=\"text-success\">\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ selectedLabGroupIds().length }} group{{ selectedLabGroupIds().length > 1 ? 's' : '' }} selected\n </small>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Pools Section -->\n <div class=\"mb-4\">\n <h6 class=\"fw-semibold mb-3\">\n <i class=\"bi bi-collection me-2\"></i>Sample Pools\n </h6>\n <div class=\"form-check\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"includePools\"\n [checked]=\"includePools()\"\n (change)=\"includePools.set($event.target.checked)\">\n <label class=\"form-check-label\" for=\"includePools\">\n Include sample pools in export\n </label>\n <small class=\"form-text text-muted d-block mt-1\">\n Creates additional worksheets with pool information and mappings\n </small>\n </div>\n </div>\n\n <!-- Summary -->\n <div class=\"alert alert-info\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-info-circle me-2\"></i>Export Summary\n </h6>\n <ul class=\"mb-0\">\n <li>\n <strong>Lab Groups:</strong>\n @switch (includeLabGroups()) {\n @case ('none') { None - only user and global recommendations }\n @case ('selected') { \n @if (selectedLabGroupIds().length > 0) {\n {{ selectedLabGroupIds().length }} selected group{{ selectedLabGroupIds().length > 1 ? 's' : '' }}\n } @else {\n No groups selected\n }\n }\n @case ('all') { All available lab groups }\n }\n </li>\n <li><strong>Sample Pools:</strong> {{ includePools() ? 'Included' : 'Excluded' }}</li>\n </ul>\n </div>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"cancel()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\"\n [disabled]=\"!canConfirm()\"\n (click)=\"confirm()\">\n <i class=\"bi bi-file-earmark-spreadsheet me-2\"></i>\n Export Excel\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] });
3748
3746
  }
3749
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ExcelExportModalComponent, decorators: [{
3747
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ExcelExportModalComponent, decorators: [{
3750
3748
  type: Component,
3751
3749
  args: [{ selector: 'ccv-excel-export-modal', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">Excel Export Options</h4>\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\" (click)=\"cancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <!-- Lab Groups Section -->\n <div class=\"mb-4\">\n <h6 class=\"fw-semibold mb-3\">\n <i class=\"bi bi-people me-2\"></i>Lab Group Recommendations\n </h6>\n \n <div class=\"form-check mb-2\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_none\"\n [checked]=\"includeLabGroups() === 'none'\"\n (change)=\"onIncludeLabGroupsChange('none')\">\n <label class=\"form-check-label\" for=\"labGroups_none\">\n <strong>None</strong> - Only user-specific and global recommendations\n </label>\n </div>\n\n <div class=\"form-check mb-2\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_selected\"\n [checked]=\"includeLabGroups() === 'selected'\"\n (change)=\"onIncludeLabGroupsChange('selected')\">\n <label class=\"form-check-label\" for=\"labGroups_selected\">\n <strong>Selected groups</strong> - Choose specific lab groups\n </label>\n </div>\n\n <div class=\"form-check mb-3\">\n <input \n class=\"form-check-input\" \n type=\"radio\" \n name=\"includeLabGroups\"\n id=\"labGroups_all\"\n [checked]=\"includeLabGroups() === 'all'\"\n (change)=\"onIncludeLabGroupsChange('all')\">\n <label class=\"form-check-label\" for=\"labGroups_all\">\n <strong>All groups</strong> - Include all available lab group recommendations\n </label>\n </div>\n\n <!-- Lab Group Selection -->\n @if (showLabGroupSelection()) {\n <div class=\"border rounded p-3 bg-light\">\n @if (isLoadingLabGroups()) {\n <div class=\"text-center\">\n <div class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></div>\n Loading lab groups...\n </div>\n } @else if (availableLabGroups().length === 0) {\n <div class=\"text-muted text-center\">\n <i class=\"bi bi-info-circle me-2\"></i>\n No lab groups available\n </div>\n } @else {\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h6 class=\"mb-0\">Select Lab Groups:</h6>\n <div class=\"btn-group btn-group-sm\">\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm\"\n (click)=\"selectAllLabGroups()\"\n [disabled]=\"selectedLabGroupIds().length === availableLabGroups().length\">\n Select All\n </button>\n <button \n type=\"button\" \n class=\"btn btn-outline-secondary btn-sm\"\n (click)=\"deselectAllLabGroups()\"\n [disabled]=\"selectedLabGroupIds().length === 0\">\n Clear All\n </button>\n </div>\n </div>\n\n <div class=\"row\">\n @for (labGroup of availableLabGroups(); track labGroup.id) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"form-check\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n [id]=\"'labGroup_' + labGroup.id\"\n [checked]=\"isLabGroupSelected(labGroup.id)\"\n (change)=\"onLabGroupToggle(labGroup.id, $event.target.checked)\">\n <label class=\"form-check-label\" [for]=\"'labGroup_' + labGroup.id\">\n {{ labGroup.name }}\n @if (labGroup.description) {\n <small class=\"text-muted d-block\">{{ labGroup.description }}</small>\n }\n </label>\n </div>\n </div>\n }\n </div>\n\n @if (selectedLabGroupIds().length > 0) {\n <div class=\"mt-2\">\n <small class=\"text-success\">\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ selectedLabGroupIds().length }} group{{ selectedLabGroupIds().length > 1 ? 's' : '' }} selected\n </small>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Pools Section -->\n <div class=\"mb-4\">\n <h6 class=\"fw-semibold mb-3\">\n <i class=\"bi bi-collection me-2\"></i>Sample Pools\n </h6>\n <div class=\"form-check\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"includePools\"\n [checked]=\"includePools()\"\n (change)=\"includePools.set($event.target.checked)\">\n <label class=\"form-check-label\" for=\"includePools\">\n Include sample pools in export\n </label>\n <small class=\"form-text text-muted d-block mt-1\">\n Creates additional worksheets with pool information and mappings\n </small>\n </div>\n </div>\n\n <!-- Summary -->\n <div class=\"alert alert-info\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-info-circle me-2\"></i>Export Summary\n </h6>\n <ul class=\"mb-0\">\n <li>\n <strong>Lab Groups:</strong>\n @switch (includeLabGroups()) {\n @case ('none') { None - only user and global recommendations }\n @case ('selected') { \n @if (selectedLabGroupIds().length > 0) {\n {{ selectedLabGroupIds().length }} selected group{{ selectedLabGroupIds().length > 1 ? 's' : '' }}\n } @else {\n No groups selected\n }\n }\n @case ('all') { All available lab groups }\n }\n </li>\n <li><strong>Sample Pools:</strong> {{ includePools() ? 'Included' : 'Excluded' }}</li>\n </ul>\n </div>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"cancel()\">\n Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\"\n [disabled]=\"!canConfirm()\"\n (click)=\"confirm()\">\n <i class=\"bi bi-file-earmark-spreadsheet me-2\"></i>\n Export Excel\n </button>\n</div>\n" }]
3752
3750
  }] });
@@ -3763,10 +3761,10 @@ class ValidationResultsModal {
3763
3761
  return timestamp;
3764
3762
  }
3765
3763
  }
3766
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ValidationResultsModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
3767
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: ValidationResultsModal, isStandalone: true, selector: "ccv-validation-results-modal", inputs: { results: "results" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n @if (results?.success) {\n <i class=\"fa fa-check-circle me-2 text-success\"></i>\n Validation Passed\n } @else {\n <i class=\"fa fa-exclamation-triangle me-2 text-danger\"></i>\n Validation Failed\n }\n </h4>\n <button \n type=\"button\" \n class=\"btn-close\" \n aria-label=\"Close\"\n (click)=\"activeModal.dismiss()\">\n </button>\n</div>\n\n<div class=\"modal-body\">\n @if (results) {\n <div class=\"mb-3\">\n <h6 class=\"mb-2\">\n <i class=\"fa fa-table me-2 text-primary\"></i>\n {{ results.metadata_table_name }}\n </h6>\n <small class=\"text-muted\">\n <i class=\"fa fa-clock me-1\"></i>\n Validated: {{ formatTimestamp(results.validation_timestamp) }}\n </small>\n </div>\n\n @if (results.success) {\n <!-- Success message -->\n <div class=\"alert alert-success\">\n <i class=\"fa fa-check-circle me-2\"></i>\n <strong>Validation Successful!</strong>\n <p class=\"mb-0 mt-2\">\n Your metadata table complies with SDRF format standards and contains no validation errors.\n </p>\n </div>\n } @else {\n <!-- Validation failed - show errors and warnings -->\n <div class=\"alert alert-danger mb-3\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n <strong>Validation Failed</strong>\n <p class=\"mb-0 mt-2\">\n Your metadata table contains validation issues that need to be resolved.\n </p>\n </div>\n\n <!-- Summary -->\n <div class=\"row mb-3\">\n <div class=\"col-md-6\">\n <div class=\"card border-danger\">\n <div class=\"card-body text-center\">\n <h5 class=\"card-title text-danger\">{{ results.errors.length }}</h5>\n <p class=\"card-text\">Error{{ results.errors.length !== 1 ? 's' : '' }}</p>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"card border-warning\">\n <div class=\"card-body text-center\">\n <h5 class=\"card-title text-warning\">{{ results.warnings.length }}</h5>\n <p class=\"card-text\">Warning{{ results.warnings.length !== 1 ? 's' : '' }}</p>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Errors -->\n @if (results.errors.length > 0) {\n <div class=\"mb-3\">\n <h6 class=\"text-danger\">\n <i class=\"fa fa-times-circle me-2\"></i>\n Errors ({{ results.errors.length }})\n </h6>\n <div class=\"validation-issues\">\n @for (error of results.errors; track $index) {\n <div class=\"alert alert-danger py-2 px-3\">\n <i class=\"fa fa-times-circle me-2\"></i>\n {{ error }}\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Warnings -->\n @if (results.warnings.length > 0) {\n <div class=\"mb-3\">\n <h6 class=\"text-warning\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n Warnings ({{ results.warnings.length }})\n </h6>\n <div class=\"validation-issues\">\n @for (warning of results.warnings; track $index) {\n <div class=\"alert alert-warning py-2 px-3\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n {{ warning }}\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Help text -->\n <div class=\"alert alert-info\">\n <i class=\"fa fa-info-circle me-2\"></i>\n <strong>Need Help?</strong>\n <ul class=\"mb-0 mt-2\">\n <li>Errors must be fixed before your metadata can be exported as valid SDRF</li>\n <li>Warnings indicate potential issues but don't prevent SDRF export</li>\n <li>Check the <a href=\"/help/sdrf-validation\" target=\"_blank\">SDRF validation guide</a> for detailed explanations</li>\n </ul>\n </div>\n }\n }\n</div>\n\n<div class=\"modal-footer\">\n <button \n type=\"button\" \n class=\"btn btn-secondary\"\n (click)=\"activeModal.dismiss()\">\n Close\n </button>\n</div>\n", styles: [".validation-issues{max-height:300px;overflow-y:auto}.validation-issues .alert{font-size:.875rem;line-height:1.4;word-break:break-word}.card{height:100%}.modal-body{max-height:70vh;overflow-y:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: NgbModule }] });
3764
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ValidationResultsModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
3765
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: ValidationResultsModal, isStandalone: true, selector: "ccv-validation-results-modal", inputs: { results: "results" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n @if (results?.success) {\n <i class=\"fa fa-check-circle me-2 text-success\"></i>\n Validation Passed\n } @else {\n <i class=\"fa fa-exclamation-triangle me-2 text-danger\"></i>\n Validation Failed\n }\n </h4>\n <button \n type=\"button\" \n class=\"btn-close\" \n aria-label=\"Close\"\n (click)=\"activeModal.dismiss()\">\n </button>\n</div>\n\n<div class=\"modal-body\">\n @if (results) {\n <div class=\"mb-3\">\n <h6 class=\"mb-2\">\n <i class=\"fa fa-table me-2 text-primary\"></i>\n {{ results.metadata_table_name }}\n </h6>\n <small class=\"text-muted\">\n <i class=\"fa fa-clock me-1\"></i>\n Validated: {{ formatTimestamp(results.validation_timestamp) }}\n </small>\n </div>\n\n @if (results.success) {\n <!-- Success message -->\n <div class=\"alert alert-success\">\n <i class=\"fa fa-check-circle me-2\"></i>\n <strong>Validation Successful!</strong>\n <p class=\"mb-0 mt-2\">\n Your metadata table complies with SDRF format standards and contains no validation errors.\n </p>\n </div>\n } @else {\n <!-- Validation failed - show errors and warnings -->\n <div class=\"alert alert-danger mb-3\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n <strong>Validation Failed</strong>\n <p class=\"mb-0 mt-2\">\n Your metadata table contains validation issues that need to be resolved.\n </p>\n </div>\n\n <!-- Summary -->\n <div class=\"row mb-3\">\n <div class=\"col-md-6\">\n <div class=\"card border-danger\">\n <div class=\"card-body text-center\">\n <h5 class=\"card-title text-danger\">{{ results.errors.length }}</h5>\n <p class=\"card-text\">Error{{ results.errors.length !== 1 ? 's' : '' }}</p>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"card border-warning\">\n <div class=\"card-body text-center\">\n <h5 class=\"card-title text-warning\">{{ results.warnings.length }}</h5>\n <p class=\"card-text\">Warning{{ results.warnings.length !== 1 ? 's' : '' }}</p>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Errors -->\n @if (results.errors.length > 0) {\n <div class=\"mb-3\">\n <h6 class=\"text-danger\">\n <i class=\"fa fa-times-circle me-2\"></i>\n Errors ({{ results.errors.length }})\n </h6>\n <div class=\"validation-issues\">\n @for (error of results.errors; track $index) {\n <div class=\"alert alert-danger py-2 px-3\">\n <i class=\"fa fa-times-circle me-2\"></i>\n {{ error }}\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Warnings -->\n @if (results.warnings.length > 0) {\n <div class=\"mb-3\">\n <h6 class=\"text-warning\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n Warnings ({{ results.warnings.length }})\n </h6>\n <div class=\"validation-issues\">\n @for (warning of results.warnings; track $index) {\n <div class=\"alert alert-warning py-2 px-3\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n {{ warning }}\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Help text -->\n <div class=\"alert alert-info\">\n <i class=\"fa fa-info-circle me-2\"></i>\n <strong>Need Help?</strong>\n <ul class=\"mb-0 mt-2\">\n <li>Errors must be fixed before your metadata can be exported as valid SDRF</li>\n <li>Warnings indicate potential issues but don't prevent SDRF export</li>\n <li>Check the <a href=\"/help/sdrf-validation\" target=\"_blank\">SDRF validation guide</a> for detailed explanations</li>\n </ul>\n </div>\n }\n }\n</div>\n\n<div class=\"modal-footer\">\n <button \n type=\"button\" \n class=\"btn btn-secondary\"\n (click)=\"activeModal.dismiss()\">\n Close\n </button>\n</div>\n", styles: [".validation-issues{max-height:300px;overflow-y:auto}.validation-issues .alert{font-size:.875rem;line-height:1.4;word-break:break-word}.card{height:100%}.modal-body{max-height:70vh;overflow-y:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: NgbModule }] });
3768
3766
  }
3769
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ValidationResultsModal, decorators: [{
3767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ValidationResultsModal, decorators: [{
3770
3768
  type: Component,
3771
3769
  args: [{ selector: 'ccv-validation-results-modal', imports: [CommonModule, NgbModule], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n @if (results?.success) {\n <i class=\"fa fa-check-circle me-2 text-success\"></i>\n Validation Passed\n } @else {\n <i class=\"fa fa-exclamation-triangle me-2 text-danger\"></i>\n Validation Failed\n }\n </h4>\n <button \n type=\"button\" \n class=\"btn-close\" \n aria-label=\"Close\"\n (click)=\"activeModal.dismiss()\">\n </button>\n</div>\n\n<div class=\"modal-body\">\n @if (results) {\n <div class=\"mb-3\">\n <h6 class=\"mb-2\">\n <i class=\"fa fa-table me-2 text-primary\"></i>\n {{ results.metadata_table_name }}\n </h6>\n <small class=\"text-muted\">\n <i class=\"fa fa-clock me-1\"></i>\n Validated: {{ formatTimestamp(results.validation_timestamp) }}\n </small>\n </div>\n\n @if (results.success) {\n <!-- Success message -->\n <div class=\"alert alert-success\">\n <i class=\"fa fa-check-circle me-2\"></i>\n <strong>Validation Successful!</strong>\n <p class=\"mb-0 mt-2\">\n Your metadata table complies with SDRF format standards and contains no validation errors.\n </p>\n </div>\n } @else {\n <!-- Validation failed - show errors and warnings -->\n <div class=\"alert alert-danger mb-3\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n <strong>Validation Failed</strong>\n <p class=\"mb-0 mt-2\">\n Your metadata table contains validation issues that need to be resolved.\n </p>\n </div>\n\n <!-- Summary -->\n <div class=\"row mb-3\">\n <div class=\"col-md-6\">\n <div class=\"card border-danger\">\n <div class=\"card-body text-center\">\n <h5 class=\"card-title text-danger\">{{ results.errors.length }}</h5>\n <p class=\"card-text\">Error{{ results.errors.length !== 1 ? 's' : '' }}</p>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"card border-warning\">\n <div class=\"card-body text-center\">\n <h5 class=\"card-title text-warning\">{{ results.warnings.length }}</h5>\n <p class=\"card-text\">Warning{{ results.warnings.length !== 1 ? 's' : '' }}</p>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Errors -->\n @if (results.errors.length > 0) {\n <div class=\"mb-3\">\n <h6 class=\"text-danger\">\n <i class=\"fa fa-times-circle me-2\"></i>\n Errors ({{ results.errors.length }})\n </h6>\n <div class=\"validation-issues\">\n @for (error of results.errors; track $index) {\n <div class=\"alert alert-danger py-2 px-3\">\n <i class=\"fa fa-times-circle me-2\"></i>\n {{ error }}\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Warnings -->\n @if (results.warnings.length > 0) {\n <div class=\"mb-3\">\n <h6 class=\"text-warning\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n Warnings ({{ results.warnings.length }})\n </h6>\n <div class=\"validation-issues\">\n @for (warning of results.warnings; track $index) {\n <div class=\"alert alert-warning py-2 px-3\">\n <i class=\"fa fa-exclamation-triangle me-2\"></i>\n {{ warning }}\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Help text -->\n <div class=\"alert alert-info\">\n <i class=\"fa fa-info-circle me-2\"></i>\n <strong>Need Help?</strong>\n <ul class=\"mb-0 mt-2\">\n <li>Errors must be fixed before your metadata can be exported as valid SDRF</li>\n <li>Warnings indicate potential issues but don't prevent SDRF export</li>\n <li>Check the <a href=\"/help/sdrf-validation\" target=\"_blank\">SDRF validation guide</a> for detailed explanations</li>\n </ul>\n </div>\n }\n }\n</div>\n\n<div class=\"modal-footer\">\n <button \n type=\"button\" \n class=\"btn btn-secondary\"\n (click)=\"activeModal.dismiss()\">\n Close\n </button>\n</div>\n", styles: [".validation-issues{max-height:300px;overflow-y:auto}.validation-issues .alert{font-size:.875rem;line-height:1.4;word-break:break-word}.card{height:100%}.modal-body{max-height:70vh;overflow-y:auto}\n"] }]
3772
3770
  }], propDecorators: { results: [{
@@ -4222,10 +4220,10 @@ class AsyncTaskUIService {
4222
4220
  break;
4223
4221
  }
4224
4222
  }
4225
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncTaskUIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4226
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncTaskUIService, providedIn: 'root' });
4223
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncTaskUIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4224
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncTaskUIService, providedIn: 'root' });
4227
4225
  }
4228
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: AsyncTaskUIService, decorators: [{
4226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AsyncTaskUIService, decorators: [{
4229
4227
  type: Injectable,
4230
4228
  args: [{
4231
4229
  providedIn: 'root'
@@ -4271,10 +4269,10 @@ class MetadataValidationModal {
4271
4269
  }
4272
4270
  });
4273
4271
  }
4274
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataValidationModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
4275
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: MetadataValidationModal, isStandalone: true, selector: "ccv-metadata-validation-modal", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"modal-header\">\r\n <h4 class=\"modal-title\">\r\n <i class=\"bi bi-check-circle me-2 text-success\"></i>\r\n Validate Metadata Table\r\n </h4>\r\n <button \r\n type=\"button\" \r\n class=\"btn-close\" \r\n aria-label=\"Close\"\r\n (click)=\"activeModal.dismiss()\"\r\n [disabled]=\"isValidating()\">\r\n </button>\r\n</div>\r\n\r\n<form [formGroup]=\"validationForm\" (ngSubmit)=\"onSubmit()\">\r\n <div class=\"modal-body\">\r\n <div class=\"alert alert-info\">\r\n <i class=\"bi bi-info-circle me-2\"></i>\r\n <strong>{{ config?.metadataTableName }}</strong> will be validated against SDRF format standards using SDRFDataFrame.\r\n </div>\r\n\r\n <div class=\"row\">\r\n <div class=\"col-md-6\">\r\n <h6 class=\"mb-3\">Validation Options</h6>\r\n \r\n <div class=\"form-check mb-3\">\r\n <input \r\n class=\"form-check-input\" \r\n type=\"checkbox\" \r\n id=\"validate_sdrf_format\"\r\n formControlName=\"validate_sdrf_format\">\r\n <label class=\"form-check-label\" for=\"validate_sdrf_format\">\r\n <i class=\"bi bi-file-earmark-text me-1 text-warning\"></i>\r\n SDRF Format Validation\r\n </label>\r\n <small class=\"form-text text-muted d-block\">\r\n Validate against MAGE-TAB SDRF standard format using SDRFDataFrame\r\n </small>\r\n </div>\r\n </div>\r\n\r\n <div class=\"col-md-6\">\r\n <h6 class=\"mb-3\">What gets checked?</h6>\r\n \r\n <div class=\"validation-info\">\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Required SDRF columns presence\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n SDRF format compliance\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Column header validation\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Data structure integrity\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (validationError()) {\r\n <div class=\"alert alert-danger mt-3\">\r\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\r\n {{ validationError() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"modal-footer\">\r\n <button \r\n type=\"button\" \r\n class=\"btn btn-secondary\"\r\n (click)=\"activeModal.dismiss()\"\r\n [disabled]=\"isValidating()\">\r\n Cancel\r\n </button>\r\n \r\n <button \r\n type=\"submit\" \r\n class=\"btn btn-success\"\r\n [disabled]=\"validationForm.invalid || isValidating()\">\r\n @if (isValidating()) {\r\n <span class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></span>\r\n Starting Validation...\r\n } @else {\r\n <i class=\"bi bi-play me-2\"></i>\r\n Start Validation\r\n }\r\n </button>\r\n </div>\r\n</form>", styles: [".validation-info{border-radius:.375rem;padding:1rem;border:1px solid var(--bs-border-color)}.form-check-label{font-weight:500}.modal-title{font-weight:600}.alert{border-radius:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }] });
4272
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataValidationModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
4273
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: MetadataValidationModal, isStandalone: true, selector: "ccv-metadata-validation-modal", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"modal-header\">\r\n <h4 class=\"modal-title\">\r\n <i class=\"bi bi-check-circle me-2 text-success\"></i>\r\n Validate Metadata Table\r\n </h4>\r\n <button \r\n type=\"button\" \r\n class=\"btn-close\" \r\n aria-label=\"Close\"\r\n (click)=\"activeModal.dismiss()\"\r\n [disabled]=\"isValidating()\">\r\n </button>\r\n</div>\r\n\r\n<form [formGroup]=\"validationForm\" (ngSubmit)=\"onSubmit()\">\r\n <div class=\"modal-body\">\r\n <div class=\"alert alert-info\">\r\n <i class=\"bi bi-info-circle me-2\"></i>\r\n <strong>{{ config?.metadataTableName }}</strong> will be validated against SDRF format standards using SDRFDataFrame.\r\n </div>\r\n\r\n <div class=\"row\">\r\n <div class=\"col-md-6\">\r\n <h6 class=\"mb-3\">Validation Options</h6>\r\n \r\n <div class=\"form-check mb-3\">\r\n <input \r\n class=\"form-check-input\" \r\n type=\"checkbox\" \r\n id=\"validate_sdrf_format\"\r\n formControlName=\"validate_sdrf_format\">\r\n <label class=\"form-check-label\" for=\"validate_sdrf_format\">\r\n <i class=\"bi bi-file-earmark-text me-1 text-warning\"></i>\r\n SDRF Format Validation\r\n </label>\r\n <small class=\"form-text text-muted d-block\">\r\n Validate against MAGE-TAB SDRF standard format using SDRFDataFrame\r\n </small>\r\n </div>\r\n </div>\r\n\r\n <div class=\"col-md-6\">\r\n <h6 class=\"mb-3\">What gets checked?</h6>\r\n \r\n <div class=\"validation-info\">\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Required SDRF columns presence\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n SDRF format compliance\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Column header validation\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Data structure integrity\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (validationError()) {\r\n <div class=\"alert alert-danger mt-3\">\r\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\r\n {{ validationError() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"modal-footer\">\r\n <button \r\n type=\"button\" \r\n class=\"btn btn-secondary\"\r\n (click)=\"activeModal.dismiss()\"\r\n [disabled]=\"isValidating()\">\r\n Cancel\r\n </button>\r\n \r\n <button \r\n type=\"submit\" \r\n class=\"btn btn-success\"\r\n [disabled]=\"validationForm.invalid || isValidating()\">\r\n @if (isValidating()) {\r\n <span class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></span>\r\n Starting Validation...\r\n } @else {\r\n <i class=\"bi bi-play me-2\"></i>\r\n Start Validation\r\n }\r\n </button>\r\n </div>\r\n</form>", styles: [".validation-info{border-radius:.375rem;padding:1rem;border:1px solid var(--bs-border-color)}.form-check-label{font-weight:500}.modal-title{font-weight:600}.alert{border-radius:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }] });
4276
4274
  }
4277
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataValidationModal, decorators: [{
4275
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataValidationModal, decorators: [{
4278
4276
  type: Component,
4279
4277
  args: [{ selector: 'ccv-metadata-validation-modal', standalone: true, imports: [CommonModule, ReactiveFormsModule, NgbModule], template: "<div class=\"modal-header\">\r\n <h4 class=\"modal-title\">\r\n <i class=\"bi bi-check-circle me-2 text-success\"></i>\r\n Validate Metadata Table\r\n </h4>\r\n <button \r\n type=\"button\" \r\n class=\"btn-close\" \r\n aria-label=\"Close\"\r\n (click)=\"activeModal.dismiss()\"\r\n [disabled]=\"isValidating()\">\r\n </button>\r\n</div>\r\n\r\n<form [formGroup]=\"validationForm\" (ngSubmit)=\"onSubmit()\">\r\n <div class=\"modal-body\">\r\n <div class=\"alert alert-info\">\r\n <i class=\"bi bi-info-circle me-2\"></i>\r\n <strong>{{ config?.metadataTableName }}</strong> will be validated against SDRF format standards using SDRFDataFrame.\r\n </div>\r\n\r\n <div class=\"row\">\r\n <div class=\"col-md-6\">\r\n <h6 class=\"mb-3\">Validation Options</h6>\r\n \r\n <div class=\"form-check mb-3\">\r\n <input \r\n class=\"form-check-input\" \r\n type=\"checkbox\" \r\n id=\"validate_sdrf_format\"\r\n formControlName=\"validate_sdrf_format\">\r\n <label class=\"form-check-label\" for=\"validate_sdrf_format\">\r\n <i class=\"bi bi-file-earmark-text me-1 text-warning\"></i>\r\n SDRF Format Validation\r\n </label>\r\n <small class=\"form-text text-muted d-block\">\r\n Validate against MAGE-TAB SDRF standard format using SDRFDataFrame\r\n </small>\r\n </div>\r\n </div>\r\n\r\n <div class=\"col-md-6\">\r\n <h6 class=\"mb-3\">What gets checked?</h6>\r\n \r\n <div class=\"validation-info\">\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Required SDRF columns presence\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n SDRF format compliance\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Column header validation\r\n </div>\r\n <div class=\"mb-2\">\r\n <i class=\"bi bi-check-circle text-success me-2\"></i>\r\n Data structure integrity\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (validationError()) {\r\n <div class=\"alert alert-danger mt-3\">\r\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\r\n {{ validationError() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"modal-footer\">\r\n <button \r\n type=\"button\" \r\n class=\"btn btn-secondary\"\r\n (click)=\"activeModal.dismiss()\"\r\n [disabled]=\"isValidating()\">\r\n Cancel\r\n </button>\r\n \r\n <button \r\n type=\"submit\" \r\n class=\"btn btn-success\"\r\n [disabled]=\"validationForm.invalid || isValidating()\">\r\n @if (isValidating()) {\r\n <span class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></span>\r\n Starting Validation...\r\n } @else {\r\n <i class=\"bi bi-play me-2\"></i>\r\n Start Validation\r\n }\r\n </button>\r\n </div>\r\n</form>", styles: [".validation-info{border-radius:.375rem;padding:1rem;border:1px solid var(--bs-border-color)}.form-check-label{font-weight:500}.modal-title{font-weight:600}.alert{border-radius:.5rem}\n"] }]
4280
4278
  }], propDecorators: { config: [{
@@ -4492,13 +4490,13 @@ Do you want to continue?`;
4492
4490
  };
4493
4491
  return displayNames[fieldName] || fieldName;
4494
4492
  }
4495
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i2.NgbModal }, { token: i4.MetadataTableService }, { token: i3.LabGroupService }], target: i0.ɵɵFactoryTarget.Component });
4496
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: MetadataTableEditModal, isStandalone: true, selector: "ccv-metadata-table-edit-modal", inputs: { table: "table", isCreateMode: "isCreateMode" }, outputs: { tableSaved: "tableSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-table me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n\n <!-- Table Name -->\n <div class=\"mb-3\">\n <label for=\"tableName\" class=\"form-label\">\n Table Name *\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"tableName\"\n formControlName=\"name\"\n [class.is-invalid]=\"isFieldInvalid('name')\"\n placeholder=\"Enter table name\"\n maxlength=\"255\">\n @if (isFieldInvalid('name')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('name') }}\n </div>\n }\n </div>\n\n <!-- Description -->\n <div class=\"mb-3\">\n <label for=\"tableDescription\" class=\"form-label\">\n Description\n </label>\n <textarea\n class=\"form-control\"\n id=\"tableDescription\"\n formControlName=\"description\"\n rows=\"3\"\n placeholder=\"Optional description of the metadata table\"></textarea>\n <div class=\"form-text\">\n Provide a brief description of what this metadata table contains or its purpose.\n </div>\n </div>\n\n <!-- Sample Count and Version Row -->\n <div class=\"row\">\n <div class=\"col-md-6 mb-3\">\n <label for=\"sampleCount\" class=\"form-label\">\n Sample Count *\n </label>\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"sampleCount\"\n formControlName=\"sampleCount\"\n [class.is-invalid]=\"isFieldInvalid('sampleCount')\"\n placeholder=\"Number of samples\"\n min=\"1\"\n max=\"10000\">\n @if (isFieldInvalid('sampleCount')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('sampleCount') }}\n </div>\n }\n <div class=\"form-text\">\n Number of samples in this metadata table (1-10,000).\n </div>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"version\" class=\"form-label\">\n Version\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"version\"\n formControlName=\"version\"\n [class.is-invalid]=\"isFieldInvalid('version')\"\n placeholder=\"e.g., 1.0, v2.1\"\n maxlength=\"50\">\n @if (isFieldInvalid('version')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('version') }}\n </div>\n }\n <div class=\"form-text\">\n Version identifier for this table.\n </div>\n </div>\n </div>\n\n <!-- Lab Group Assignment -->\n <div class=\"mb-3\">\n <label for=\"labGroup\" class=\"form-label\">\n Lab Group Assignment\n </label>\n @if (isLoadingLabGroups()) {\n <div class=\"d-flex align-items-center\">\n <div class=\"spinner-border spinner-border-sm me-2\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <span class=\"text-muted\">Loading lab groups...</span>\n </div>\n } @else {\n <select\n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroup\">\n <option [value]=\"null\">No lab group (Personal)</option>\n @for (group of availableLabGroups(); track group.id) {\n <option [value]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n }\n <div class=\"form-text\">\n Assign this table to a lab group for shared access, or leave unassigned for personal use.\n </div>\n </div>\n\n <!-- Table Status Info (Read-only) -->\n @if (table) {\n <div class=\"mb-3\">\n <label class=\"form-label\">Current Status</label>\n <div class=\"d-flex gap-2\">\n @if (table.isLocked) {\n <span class=\"badge bg-warning\">\n <i class=\"bi bi-lock me-1\"></i>Locked\n </span>\n } @else {\n <span class=\"badge bg-success\">\n <i class=\"bi bi-unlock me-1\"></i>Unlocked\n </span>\n }\n @if (table.isPublished) {\n <span class=\"badge bg-info\">\n <i class=\"bi bi-check-circle me-1\"></i>Published\n </span>\n } @else {\n <span class=\"badge bg-secondary\">\n <i class=\"bi bi-clock me-1\"></i>Draft\n </span>\n }\n </div>\n <div class=\"form-text\">\n Lock status and publication state can be managed through advanced options.\n </div>\n </div>\n }\n\n <!-- Character Count -->\n <div class=\"mb-2\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Name: {{ editForm.get('name')?.value?.length || 0 }}/255 characters\n </small>\n @if (hasChanges) {\n <small class=\"text-info\">\n <i class=\"bi bi-info-circle me-1\"></i>Changes detected\n </small>\n }\n </div>\n </div>\n\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading() || (!isCreateMode && !hasChanges)\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n @if (isCreateMode) {\n <i class=\"bi bi-plus-circle me-1\"></i>\n Create Table\n } @else {\n <i class=\"bi bi-check-circle me-1\"></i>\n Save Changes\n }\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }] });
4493
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i2.NgbModal }, { token: MetadataTableService }, { token: i3.LabGroupService }], target: i0.ɵɵFactoryTarget.Component });
4494
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: MetadataTableEditModal, isStandalone: true, selector: "ccv-metadata-table-edit-modal", inputs: { table: "table", isCreateMode: "isCreateMode" }, outputs: { tableSaved: "tableSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-table me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n\n <!-- Table Name -->\n <div class=\"mb-3\">\n <label for=\"tableName\" class=\"form-label\">\n Table Name *\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"tableName\"\n formControlName=\"name\"\n [class.is-invalid]=\"isFieldInvalid('name')\"\n placeholder=\"Enter table name\"\n maxlength=\"255\">\n @if (isFieldInvalid('name')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('name') }}\n </div>\n }\n </div>\n\n <!-- Description -->\n <div class=\"mb-3\">\n <label for=\"tableDescription\" class=\"form-label\">\n Description\n </label>\n <textarea\n class=\"form-control\"\n id=\"tableDescription\"\n formControlName=\"description\"\n rows=\"3\"\n placeholder=\"Optional description of the metadata table\"></textarea>\n <div class=\"form-text\">\n Provide a brief description of what this metadata table contains or its purpose.\n </div>\n </div>\n\n <!-- Sample Count and Version Row -->\n <div class=\"row\">\n <div class=\"col-md-6 mb-3\">\n <label for=\"sampleCount\" class=\"form-label\">\n Sample Count *\n </label>\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"sampleCount\"\n formControlName=\"sampleCount\"\n [class.is-invalid]=\"isFieldInvalid('sampleCount')\"\n placeholder=\"Number of samples\"\n min=\"1\"\n max=\"10000\">\n @if (isFieldInvalid('sampleCount')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('sampleCount') }}\n </div>\n }\n <div class=\"form-text\">\n Number of samples in this metadata table (1-10,000).\n </div>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"version\" class=\"form-label\">\n Version\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"version\"\n formControlName=\"version\"\n [class.is-invalid]=\"isFieldInvalid('version')\"\n placeholder=\"e.g., 1.0, v2.1\"\n maxlength=\"50\">\n @if (isFieldInvalid('version')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('version') }}\n </div>\n }\n <div class=\"form-text\">\n Version identifier for this table.\n </div>\n </div>\n </div>\n\n <!-- Lab Group Assignment -->\n <div class=\"mb-3\">\n <label for=\"labGroup\" class=\"form-label\">\n Lab Group Assignment\n </label>\n @if (isLoadingLabGroups()) {\n <div class=\"d-flex align-items-center\">\n <div class=\"spinner-border spinner-border-sm me-2\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <span class=\"text-muted\">Loading lab groups...</span>\n </div>\n } @else {\n <select\n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroup\">\n <option [value]=\"null\">No lab group (Personal)</option>\n @for (group of availableLabGroups(); track group.id) {\n <option [value]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n }\n <div class=\"form-text\">\n Assign this table to a lab group for shared access, or leave unassigned for personal use.\n </div>\n </div>\n\n <!-- Table Status Info (Read-only) -->\n @if (table) {\n <div class=\"mb-3\">\n <label class=\"form-label\">Current Status</label>\n <div class=\"d-flex gap-2\">\n @if (table.isLocked) {\n <span class=\"badge bg-warning\">\n <i class=\"bi bi-lock me-1\"></i>Locked\n </span>\n } @else {\n <span class=\"badge bg-success\">\n <i class=\"bi bi-unlock me-1\"></i>Unlocked\n </span>\n }\n @if (table.isPublished) {\n <span class=\"badge bg-info\">\n <i class=\"bi bi-check-circle me-1\"></i>Published\n </span>\n } @else {\n <span class=\"badge bg-secondary\">\n <i class=\"bi bi-clock me-1\"></i>Draft\n </span>\n }\n </div>\n <div class=\"form-text\">\n Lock status and publication state can be managed through advanced options.\n </div>\n </div>\n }\n\n <!-- Character Count -->\n <div class=\"mb-2\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Name: {{ editForm.get('name')?.value?.length || 0 }}/255 characters\n </small>\n @if (hasChanges) {\n <small class=\"text-info\">\n <i class=\"bi bi-info-circle me-1\"></i>Changes detected\n </small>\n }\n </div>\n </div>\n\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading() || (!isCreateMode && !hasChanges)\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n @if (isCreateMode) {\n <i class=\"bi bi-plus-circle me-1\"></i>\n Create Table\n } @else {\n <i class=\"bi bi-check-circle me-1\"></i>\n Save Changes\n }\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }] });
4497
4495
  }
4498
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableEditModal, decorators: [{
4496
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableEditModal, decorators: [{
4499
4497
  type: Component,
4500
4498
  args: [{ selector: 'ccv-metadata-table-edit-modal', standalone: true, imports: [CommonModule, ReactiveFormsModule, NgbModule], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-table me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n\n <!-- Table Name -->\n <div class=\"mb-3\">\n <label for=\"tableName\" class=\"form-label\">\n Table Name *\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"tableName\"\n formControlName=\"name\"\n [class.is-invalid]=\"isFieldInvalid('name')\"\n placeholder=\"Enter table name\"\n maxlength=\"255\">\n @if (isFieldInvalid('name')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('name') }}\n </div>\n }\n </div>\n\n <!-- Description -->\n <div class=\"mb-3\">\n <label for=\"tableDescription\" class=\"form-label\">\n Description\n </label>\n <textarea\n class=\"form-control\"\n id=\"tableDescription\"\n formControlName=\"description\"\n rows=\"3\"\n placeholder=\"Optional description of the metadata table\"></textarea>\n <div class=\"form-text\">\n Provide a brief description of what this metadata table contains or its purpose.\n </div>\n </div>\n\n <!-- Sample Count and Version Row -->\n <div class=\"row\">\n <div class=\"col-md-6 mb-3\">\n <label for=\"sampleCount\" class=\"form-label\">\n Sample Count *\n </label>\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"sampleCount\"\n formControlName=\"sampleCount\"\n [class.is-invalid]=\"isFieldInvalid('sampleCount')\"\n placeholder=\"Number of samples\"\n min=\"1\"\n max=\"10000\">\n @if (isFieldInvalid('sampleCount')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('sampleCount') }}\n </div>\n }\n <div class=\"form-text\">\n Number of samples in this metadata table (1-10,000).\n </div>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"version\" class=\"form-label\">\n Version\n </label>\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"version\"\n formControlName=\"version\"\n [class.is-invalid]=\"isFieldInvalid('version')\"\n placeholder=\"e.g., 1.0, v2.1\"\n maxlength=\"50\">\n @if (isFieldInvalid('version')) {\n <div class=\"invalid-feedback\">\n {{ getFieldError('version') }}\n </div>\n }\n <div class=\"form-text\">\n Version identifier for this table.\n </div>\n </div>\n </div>\n\n <!-- Lab Group Assignment -->\n <div class=\"mb-3\">\n <label for=\"labGroup\" class=\"form-label\">\n Lab Group Assignment\n </label>\n @if (isLoadingLabGroups()) {\n <div class=\"d-flex align-items-center\">\n <div class=\"spinner-border spinner-border-sm me-2\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <span class=\"text-muted\">Loading lab groups...</span>\n </div>\n } @else {\n <select\n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroup\">\n <option [value]=\"null\">No lab group (Personal)</option>\n @for (group of availableLabGroups(); track group.id) {\n <option [value]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n }\n <div class=\"form-text\">\n Assign this table to a lab group for shared access, or leave unassigned for personal use.\n </div>\n </div>\n\n <!-- Table Status Info (Read-only) -->\n @if (table) {\n <div class=\"mb-3\">\n <label class=\"form-label\">Current Status</label>\n <div class=\"d-flex gap-2\">\n @if (table.isLocked) {\n <span class=\"badge bg-warning\">\n <i class=\"bi bi-lock me-1\"></i>Locked\n </span>\n } @else {\n <span class=\"badge bg-success\">\n <i class=\"bi bi-unlock me-1\"></i>Unlocked\n </span>\n }\n @if (table.isPublished) {\n <span class=\"badge bg-info\">\n <i class=\"bi bi-check-circle me-1\"></i>Published\n </span>\n } @else {\n <span class=\"badge bg-secondary\">\n <i class=\"bi bi-clock me-1\"></i>Draft\n </span>\n }\n </div>\n <div class=\"form-text\">\n Lock status and publication state can be managed through advanced options.\n </div>\n </div>\n }\n\n <!-- Character Count -->\n <div class=\"mb-2\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Name: {{ editForm.get('name')?.value?.length || 0 }}/255 characters\n </small>\n @if (hasChanges) {\n <small class=\"text-info\">\n <i class=\"bi bi-info-circle me-1\"></i>Changes detected\n </small>\n }\n </div>\n </div>\n\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading() || (!isCreateMode && !hasChanges)\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n @if (isCreateMode) {\n <i class=\"bi bi-plus-circle me-1\"></i>\n Create Table\n } @else {\n <i class=\"bi bi-check-circle me-1\"></i>\n Save Changes\n }\n </button>\n</div>\n" }]
4501
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i2.NgbModal }, { type: i4.MetadataTableService }, { type: i3.LabGroupService }], propDecorators: { table: [{
4499
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i2.NgbModal }, { type: MetadataTableService }, { type: i3.LabGroupService }], propDecorators: { table: [{
4502
4500
  type: Input
4503
4501
  }], isCreateMode: [{
4504
4502
  type: Input
@@ -4522,8 +4520,8 @@ class ColumnEditModal {
4522
4520
  // Selected ontology configuration (includes custom filters)
4523
4521
  selectedOntologyConfig = null;
4524
4522
  // Available options for dropdowns from library
4525
- columnTypes = COLUMN_TYPE_CONFIGS$1;
4526
- ontologyTypes = ONTOLOGY_TYPE_CONFIGS$1;
4523
+ columnTypes = COLUMN_TYPE_CONFIGS;
4524
+ ontologyTypes = ONTOLOGY_TYPE_CONFIGS;
4527
4525
  // Template selection properties
4528
4526
  templateFilter = '';
4529
4527
  selectedTemplateId = '';
@@ -4537,7 +4535,7 @@ class ColumnEditModal {
4537
4535
  totalTemplatePages = 0;
4538
4536
  Math = Math;
4539
4537
  // Official SDRF column configurations from library
4540
- officialSdrfColumns = OFFICIAL_SDRF_COLUMNS$1;
4538
+ officialSdrfColumns = OFFICIAL_SDRF_COLUMNS;
4541
4539
  // Column selection mode
4542
4540
  showOfficialColumns = false;
4543
4541
  selectedOfficialColumn = null;
@@ -4553,7 +4551,7 @@ class ColumnEditModal {
4553
4551
  this.sdrfSyntaxService = sdrfSyntaxService;
4554
4552
  this.editForm = this.fb.group({
4555
4553
  name: ['', [Validators.required, Validators.minLength(1)]],
4556
- type: [ColumnType$1.CHARACTERISTICS, Validators.required],
4554
+ type: [ColumnType.CHARACTERISTICS, Validators.required],
4557
4555
  value: [''],
4558
4556
  ontologyType: ['0'], // Default to 'None' (index 0)
4559
4557
  enableTypeahead: [false],
@@ -4670,7 +4668,7 @@ class ColumnEditModal {
4670
4668
  templateId: this.selectedTemplate?.id || this.templateId || undefined,
4671
4669
  customOntologyFilters: customFilters
4672
4670
  };
4673
- const modalRef = this.modalService.open(MetadataValueEditModal$1, {
4671
+ const modalRef = this.modalService.open(MetadataValueEditModal, {
4674
4672
  size: 'lg',
4675
4673
  backdrop: 'static'
4676
4674
  });
@@ -4761,7 +4759,7 @@ class ColumnEditModal {
4761
4759
  // Reset form to default values
4762
4760
  this.editForm.reset({
4763
4761
  name: '',
4764
- type: ColumnType$1.CHARACTERISTICS,
4762
+ type: ColumnType.CHARACTERISTICS,
4765
4763
  value: '',
4766
4764
  ontologyType: '',
4767
4765
  enableTypeahead: false,
@@ -4959,13 +4957,13 @@ class ColumnEditModal {
4959
4957
  }
4960
4958
  return columnType;
4961
4959
  }
4962
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ColumnEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i2.NgbModal }, { token: i4.MetadataColumnTemplateService }, { token: i4.SdrfSyntaxService }], target: i0.ɵɵFactoryTarget.Component });
4963
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: ColumnEditModal, isStandalone: true, selector: "ccv-column-edit-modal", inputs: { column: "column", templateId: "templateId", isEdit: "isEdit" }, outputs: { columnSaved: "columnSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-columns-gap me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row\">\n <!-- Selection Required Notice for New Columns -->\n @if (!isEdit) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-warning\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>Column Selection Required\n </h6>\n <p class=\"mb-0\">\n You must select from official SDRF columns or existing templates to ensure data compliance and consistency.\n </p>\n </div>\n </div>\n }\n\n <!-- Official SDRF Columns -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-file-earmark-check me-2\"></i>Official SDRF Columns\n <small class=\"text-muted ms-2\">\n @if (!isEdit) {\n (Step 1: Select from standardized SDRF-Proteomics column names)\n } @else {\n (Recommended - select from standardized SDRF-Proteomics column names)\n }\n </small>\n </h6>\n </div>\n\n @if (!isEdit) {\n <div class=\"col-12 mb-3\">\n @if (!showOfficialColumns) {\n <button\n type=\"button\"\n class=\"btn btn-primary mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-list-ul me-2\"></i>\n Select from Official SDRF Columns\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-x me-2\"></i>\n Hide Official Columns\n </button>\n }\n </div>\n } @else {\n <div class=\"col-12 mb-3\">\n <button\n type=\"button\"\n class=\"btn btn-outline-info mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-list-ul me-2\"></i>\n {{ showOfficialColumns ? 'Hide' : 'Show' }} Official Column Names\n </button>\n </div>\n }\n\n @if (showOfficialColumns) {\n <div class=\"col-12 mb-3\">\n <div class=\"border rounded p-3\">\n <p class=\"mb-3 text-muted\">\n <i class=\"bi bi-info-circle me-2\"></i>\n Select from official SDRF-Proteomics column names. Only columns without existing templates are shown.\n </p>\n\n @if (availableOfficialColumns.length === 0) {\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n All official SDRF columns already have templates created.\n </div>\n } @else {\n <div class=\"row\">\n @for (column of availableOfficialColumns; track column.name) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"card h-100 cursor-pointer border-info\"\n (click)=\"selectOfficialColumn(column)\"\n [class.border-primary]=\"selectedOfficialColumn?.name === column.name\">\n <div class=\"card-body py-2\">\n <h6 class=\"card-title mb-1 font-monospace\">\n {{ column.name }}\n </h6>\n <p class=\"card-text small mb-1\">\n <span class=\"badge\"\n [class]=\"column.type === 'characteristics' ? 'bg-primary' :\n column.type === 'comment' ? 'bg-success' :\n column.type === 'factor_value' ? 'bg-warning' : 'bg-secondary'\">\n {{ column.type }}\n </span>\n </p>\n <p class=\"card-text small mb-0\">{{ column.description }}</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Template Swapping for Edit Mode -->\n @if (isEdit && showTemplateSwap) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-info\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-arrow-left-right me-2\"></i>Swap to Different Template\n </h6>\n <p class=\"mb-3\">\n Choose a different template to change the column name and settings. Your current value will be preserved.\n </p>\n\n <div class=\"mb-3\">\n <input\n type=\"text\"\n class=\"form-control mb-2\"\n placeholder=\"Search templates...\"\n [(ngModel)]=\"templateFilter\"\n [ngModelOptions]=\"{standalone: true}\"\n (input)=\"onTemplateFilterChange()\">\n\n <select\n class=\"form-select mb-3\"\n size=\"4\"\n [disabled]=\"isLoadingTemplates\">\n @if (isLoadingTemplates) {\n <option value=\"\" disabled>Loading templates...</option>\n } @else {\n @for (template of templates; track template.id) {\n <option\n [value]=\"template.id\"\n (click)=\"swapToTemplate(template)\"\n style=\"cursor: pointer;\">\n {{ template.columnName }} - {{ template.columnType }}@if (template.ontologyType) { ({{ getOntologyTypeLabel(template.ontologyType) }})}\n </option>\n }\n }\n </select>\n </div>\n\n <div class=\"d-flex justify-content-between\">\n <button\n type=\"button\"\n class=\"btn btn-success\"\n (click)=\"showCreateCustomTemplateOption()\">\n <i class=\"bi bi-plus-circle me-1\"></i>Create Custom Template\n </button>\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n (click)=\"cancelTemplateSwap()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Custom Template Creation Option -->\n @if (isEdit && showCreateCustomTemplate) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-warning\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-plus-circle me-2\"></i>Create Custom Template\n </h6>\n <p class=\"mb-3\">\n This will create a new custom template with your desired column name and settings.\n </p>\n\n <div class=\"d-flex justify-content-between\">\n <button\n type=\"button\"\n class=\"btn btn-warning\"\n (click)=\"createCustomTemplate()\">\n <i class=\"bi bi-gear me-1\"></i>Create & Apply Template\n </button>\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n (click)=\"cancelTemplateSwap()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Template Selection -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Template Selection\n <small class=\"text-muted ms-2\">\n @if (!isEdit) {\n (Step 2: Or select from existing templates)\n } @else {\n (Optional - select a template to auto-fill column settings)\n }\n </small>\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"input-group mb-3\">\n <span class=\"input-group-text\">\n <i class=\"bi bi-search\"></i>\n </span>\n <input \n type=\"text\" \n class=\"form-control\" \n placeholder=\"Search column templates...\"\n [(ngModel)]=\"templateFilter\"\n [ngModelOptions]=\"{standalone: true}\"\n (input)=\"onTemplateFilterChange()\">\n @if (templateFilter) {\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"clearTemplateFilter()\">\n <i class=\"bi bi-x\"></i>\n </button>\n }\n </div>\n\n <select \n class=\"form-select mb-3\"\n size=\"5\"\n [(ngModel)]=\"selectedTemplateId\"\n [ngModelOptions]=\"{standalone: true}\"\n (change)=\"onTemplateSelectionChange()\"\n [disabled]=\"isLoadingTemplates\">\n <option value=\"\">No template selected</option>\n @if (isLoadingTemplates) {\n <option value=\"\" disabled>Loading templates...</option>\n } @else {\n @for (template of templates; track template.id) {\n <option [value]=\"template.id\">\n {{ template.columnName }} - {{ template.columnType }}@if (template.ontologyType) { ({{ getOntologyTypeLabel(template.ontologyType) }})}\n </option>\n }\n }\n </select>\n\n @if (totalTemplatePages > 1) {\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Showing {{ (currentTemplatePage - 1) * templatePageSize + 1 }}-{{ Math.min(currentTemplatePage * templatePageSize, totalTemplates) }}\n of {{ totalTemplates }}\n </small>\n <ngb-pagination\n [collectionSize]=\"totalTemplates\"\n [page]=\"currentTemplatePage\"\n [pageSize]=\"templatePageSize\"\n [maxSize]=\"3\"\n [boundaryLinks]=\"true\"\n [rotate]=\"true\"\n (pageChange)=\"onTemplatePageChange($event)\"\n class=\"pagination-sm mb-0\">\n </ngb-pagination>\n </div>\n }\n\n @if (selectedTemplate) {\n <div class=\"alert alert-info mt-3\">\n <i class=\"bi bi-info-circle me-2\"></i>\n Template \"{{ selectedTemplate.name }}\" selected. Settings will be applied automatically.\n <button type=\"button\" class=\"btn btn-sm btn-outline-info float-end\" (click)=\"clearTemplate()\">\n Clear Selection\n </button>\n </div>\n }\n </div>\n\n <!-- Manual Entry -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-pencil me-2\"></i>Column Details\n @if (selectedTemplate) {\n <small class=\"text-muted ms-2\">(Auto-filled from template - you can modify as needed)</small>\n }\n </h6>\n </div>\n \n <div class=\"col-12 mb-3\">\n <label for=\"columnName\" class=\"form-label\">\n Column Name *\n <span class=\"text-muted ms-2\">\n <i class=\"bi bi-info-circle\"></i>\n Column names are managed through templates\n </span>\n </label>\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"columnName\"\n formControlName=\"name\"\n [class.is-invalid]=\"editForm.get('name')?.invalid && editForm.get('name')?.touched\"\n readonly\n placeholder=\"Select from official columns or templates below\">\n @if (isEdit) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"showTemplateSwapOptions()\"\n title=\"Change to different template\">\n <i class=\"bi bi-arrow-left-right\"></i> Swap Template\n </button>\n }\n </div>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n @if (isEdit) {\n To change the column name, swap to a different template or create a new custom template.\n } @else {\n Select from official SDRF columns or existing templates below to ensure compliance.\n }\n </div>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"columnType\" class=\"form-label\">\n Column Type *\n <span class=\"text-muted ms-2\">\n <i class=\"bi bi-info-circle\"></i>\n Managed through templates\n </span>\n </label>\n <select\n class=\"form-select\"\n id=\"columnType\"\n formControlName=\"type\"\n [class.is-invalid]=\"editForm.get('type')?.invalid && editForm.get('type')?.touched\">\n @for (type of columnTypes; track type.value) {\n <option [value]=\"type.value\">{{ type.label }}</option>\n }\n </select>\n </div>\n\n <!-- Advanced Mode Toggle -->\n <div class=\"col-12 mb-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"advancedMode\"\n [checked]=\"showAdvancedMode()\"\n (change)=\"toggleAdvancedMode()\">\n <label class=\"form-check-label\" for=\"advancedMode\">\n <i class=\"bi bi-gear me-1\"></i>Advanced Mode\n </label>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Show advanced options for ontology configuration and column settings\n </div>\n </div>\n </div>\n\n @if (showAdvancedMode()) {\n <div class=\"col-md-6 mb-3\">\n <label for=\"ontologyType\" class=\"form-label\">\n Ontology Type\n <span class=\"badge bg-warning ms-2\">\n <i class=\"bi bi-exclamation-triangle me-1\"></i>Advanced\n </span>\n </label>\n <select\n class=\"form-select\"\n id=\"ontologyType\"\n formControlName=\"ontologyType\"\n (change)=\"onOntologyTypeChange($event)\">\n @for (ontology of ontologyTypes; track $index) {\n <option [value]=\"$index\">{{ ontology.label }}</option>\n }\n </select>\n\n @if (hasOntologyType) {\n <div class=\"form-text mt-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Custom ontology filters will be applied based on the selected type\n </div>\n }\n </div>\n\n @if (hasOntologyType) {\n <div class=\"col-12 mb-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"enableTypeahead\"\n formControlName=\"enableTypeahead\">\n <label class=\"form-check-label\" for=\"enableTypeahead\">\n <i class=\"bi bi-search me-1\"></i>Enable Typeahead Autocomplete\n </label>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Enable autocomplete suggestions from {{ selectedOntologyConfig?.label || 'selected' }} ontology when editing values\n </div>\n </div>\n </div>\n }\n\n <!-- Column Options - Advanced Mode Only -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-sliders me-2\"></i>Column Options\n <span class=\"badge bg-warning ms-2\">\n <i class=\"bi bi-exclamation-triangle me-1\"></i>Advanced\n </span>\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"row\">\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"mandatory\"\n formControlName=\"mandatory\">\n <label class=\"form-check-label\" for=\"mandatory\">\n <i class=\"bi bi-exclamation-circle me-1\"></i>Mandatory\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"hidden\"\n formControlName=\"hidden\">\n <label class=\"form-check-label\" for=\"hidden\">\n <i class=\"bi bi-eye-slash me-1\"></i>Hidden\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"readonly\"\n formControlName=\"readonly\">\n <label class=\"form-check-label\" for=\"readonly\">\n <i class=\"bi bi-lock me-1\"></i>Read Only\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"notApplicable\"\n formControlName=\"notApplicable\">\n <label class=\"form-check-label\" for=\"notApplicable\">\n <i class=\"bi bi-dash-circle me-1\"></i>N/A\n </label>\n </div>\n </div>\n </div>\n </div>\n }\n\n <div class=\"col-12 mb-3\">\n <label for=\"defaultValue\" class=\"form-label\">\n Default Value\n @if (hasTypeaheadEnabled) {\n <span class=\"badge bg-success ms-2\">\n <i class=\"bi bi-search me-1\"></i>Typeahead Enabled\n </span>\n }\n </label>\n \n <div class=\"input-group\">\n <input \n type=\"text\" \n class=\"form-control\"\n id=\"defaultValue\"\n formControlName=\"value\"\n placeholder=\"Enter default value\"\n readonly>\n <button \n type=\"button\" \n class=\"btn btn-outline-primary\"\n (click)=\"openValueEditModal()\"\n title=\"Open advanced value editor\">\n <i class=\"bi bi-pencil me-1\"></i>Edit\n </button>\n </div>\n \n <div class=\"form-text\">\n @if (hasTypeaheadEnabled) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to use autocomplete suggestions from {{ selectedOntologyConfig?.label || 'selected' }} ontology\n } @else if (hasOntologyType) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set default value. Enable typeahead in Advanced Mode for autocomplete suggestions.\n } @else if (showAdvancedMode()) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set the default value (select an ontology type above for smart suggestions)\n } @else {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set the default value (enable Advanced Mode for ontology autocomplete)\n }\n </div>\n </div>\n\n </div>\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\" \n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ isEdit ? 'Update' : 'Add' }} Column\n </button>\n</div>\n", styles: [".template-selection-container{border:1px solid var(--bs-border-color);border-radius:.375rem;padding:.5rem}.template-item{cursor:pointer;transition:all .2s ease}.template-item:hover{background-color:rgba(var(--bs-primary-rgb),.1);border-color:var(--bs-primary)!important}.template-item.selected{background-color:rgba(var(--bs-primary-rgb),.15);border-color:var(--bs-primary)!important;box-shadow:0 0 0 .2rem rgba(var(--bs-primary-rgb),.25)}.cursor-pointer{cursor:pointer}.card.cursor-pointer{transition:all .2s ease}.card.cursor-pointer:hover{transform:translateY(-2px);box-shadow:0 4px 8px #0000001a}.card.cursor-pointer.border-info{border-width:2px}.modal-lg{max-width:900px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NgbModule }, { kind: "component", type: i2.NgbPagination, selector: "ngb-pagination", inputs: ["disabled", "boundaryLinks", "directionLinks", "ellipses", "rotate", "collectionSize", "maxSize", "page", "pageSize", "size"], outputs: ["pageChange"] }] });
4960
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ColumnEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i2.NgbModal }, { token: MetadataColumnTemplateService }, { token: SdrfSyntaxService }], target: i0.ɵɵFactoryTarget.Component });
4961
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: ColumnEditModal, isStandalone: true, selector: "ccv-column-edit-modal", inputs: { column: "column", templateId: "templateId", isEdit: "isEdit" }, outputs: { columnSaved: "columnSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-columns-gap me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row\">\n <!-- Selection Required Notice for New Columns -->\n @if (!isEdit) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-warning\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>Column Selection Required\n </h6>\n <p class=\"mb-0\">\n You must select from official SDRF columns or existing templates to ensure data compliance and consistency.\n </p>\n </div>\n </div>\n }\n\n <!-- Official SDRF Columns -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-file-earmark-check me-2\"></i>Official SDRF Columns\n <small class=\"text-muted ms-2\">\n @if (!isEdit) {\n (Step 1: Select from standardized SDRF-Proteomics column names)\n } @else {\n (Recommended - select from standardized SDRF-Proteomics column names)\n }\n </small>\n </h6>\n </div>\n\n @if (!isEdit) {\n <div class=\"col-12 mb-3\">\n @if (!showOfficialColumns) {\n <button\n type=\"button\"\n class=\"btn btn-primary mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-list-ul me-2\"></i>\n Select from Official SDRF Columns\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-x me-2\"></i>\n Hide Official Columns\n </button>\n }\n </div>\n } @else {\n <div class=\"col-12 mb-3\">\n <button\n type=\"button\"\n class=\"btn btn-outline-info mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-list-ul me-2\"></i>\n {{ showOfficialColumns ? 'Hide' : 'Show' }} Official Column Names\n </button>\n </div>\n }\n\n @if (showOfficialColumns) {\n <div class=\"col-12 mb-3\">\n <div class=\"border rounded p-3\">\n <p class=\"mb-3 text-muted\">\n <i class=\"bi bi-info-circle me-2\"></i>\n Select from official SDRF-Proteomics column names. Only columns without existing templates are shown.\n </p>\n\n @if (availableOfficialColumns.length === 0) {\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n All official SDRF columns already have templates created.\n </div>\n } @else {\n <div class=\"row\">\n @for (column of availableOfficialColumns; track column.name) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"card h-100 cursor-pointer border-info\"\n (click)=\"selectOfficialColumn(column)\"\n [class.border-primary]=\"selectedOfficialColumn?.name === column.name\">\n <div class=\"card-body py-2\">\n <h6 class=\"card-title mb-1 font-monospace\">\n {{ column.name }}\n </h6>\n <p class=\"card-text small mb-1\">\n <span class=\"badge\"\n [class]=\"column.type === 'characteristics' ? 'bg-primary' :\n column.type === 'comment' ? 'bg-success' :\n column.type === 'factor_value' ? 'bg-warning' : 'bg-secondary'\">\n {{ column.type }}\n </span>\n </p>\n <p class=\"card-text small mb-0\">{{ column.description }}</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Template Swapping for Edit Mode -->\n @if (isEdit && showTemplateSwap) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-info\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-arrow-left-right me-2\"></i>Swap to Different Template\n </h6>\n <p class=\"mb-3\">\n Choose a different template to change the column name and settings. Your current value will be preserved.\n </p>\n\n <div class=\"mb-3\">\n <input\n type=\"text\"\n class=\"form-control mb-2\"\n placeholder=\"Search templates...\"\n [(ngModel)]=\"templateFilter\"\n [ngModelOptions]=\"{standalone: true}\"\n (input)=\"onTemplateFilterChange()\">\n\n <select\n class=\"form-select mb-3\"\n size=\"4\"\n [disabled]=\"isLoadingTemplates\">\n @if (isLoadingTemplates) {\n <option value=\"\" disabled>Loading templates...</option>\n } @else {\n @for (template of templates; track template.id) {\n <option\n [value]=\"template.id\"\n (click)=\"swapToTemplate(template)\"\n style=\"cursor: pointer;\">\n {{ template.columnName }} - {{ template.columnType }}@if (template.ontologyType) { ({{ getOntologyTypeLabel(template.ontologyType) }})}\n </option>\n }\n }\n </select>\n </div>\n\n <div class=\"d-flex justify-content-between\">\n <button\n type=\"button\"\n class=\"btn btn-success\"\n (click)=\"showCreateCustomTemplateOption()\">\n <i class=\"bi bi-plus-circle me-1\"></i>Create Custom Template\n </button>\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n (click)=\"cancelTemplateSwap()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Custom Template Creation Option -->\n @if (isEdit && showCreateCustomTemplate) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-warning\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-plus-circle me-2\"></i>Create Custom Template\n </h6>\n <p class=\"mb-3\">\n This will create a new custom template with your desired column name and settings.\n </p>\n\n <div class=\"d-flex justify-content-between\">\n <button\n type=\"button\"\n class=\"btn btn-warning\"\n (click)=\"createCustomTemplate()\">\n <i class=\"bi bi-gear me-1\"></i>Create & Apply Template\n </button>\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n (click)=\"cancelTemplateSwap()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Template Selection -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Template Selection\n <small class=\"text-muted ms-2\">\n @if (!isEdit) {\n (Step 2: Or select from existing templates)\n } @else {\n (Optional - select a template to auto-fill column settings)\n }\n </small>\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"input-group mb-3\">\n <span class=\"input-group-text\">\n <i class=\"bi bi-search\"></i>\n </span>\n <input \n type=\"text\" \n class=\"form-control\" \n placeholder=\"Search column templates...\"\n [(ngModel)]=\"templateFilter\"\n [ngModelOptions]=\"{standalone: true}\"\n (input)=\"onTemplateFilterChange()\">\n @if (templateFilter) {\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"clearTemplateFilter()\">\n <i class=\"bi bi-x\"></i>\n </button>\n }\n </div>\n\n <select \n class=\"form-select mb-3\"\n size=\"5\"\n [(ngModel)]=\"selectedTemplateId\"\n [ngModelOptions]=\"{standalone: true}\"\n (change)=\"onTemplateSelectionChange()\"\n [disabled]=\"isLoadingTemplates\">\n <option value=\"\">No template selected</option>\n @if (isLoadingTemplates) {\n <option value=\"\" disabled>Loading templates...</option>\n } @else {\n @for (template of templates; track template.id) {\n <option [value]=\"template.id\">\n {{ template.columnName }} - {{ template.columnType }}@if (template.ontologyType) { ({{ getOntologyTypeLabel(template.ontologyType) }})}\n </option>\n }\n }\n </select>\n\n @if (totalTemplatePages > 1) {\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Showing {{ (currentTemplatePage - 1) * templatePageSize + 1 }}-{{ Math.min(currentTemplatePage * templatePageSize, totalTemplates) }}\n of {{ totalTemplates }}\n </small>\n <ngb-pagination\n [collectionSize]=\"totalTemplates\"\n [page]=\"currentTemplatePage\"\n [pageSize]=\"templatePageSize\"\n [maxSize]=\"3\"\n [boundaryLinks]=\"true\"\n [rotate]=\"true\"\n (pageChange)=\"onTemplatePageChange($event)\"\n class=\"pagination-sm mb-0\">\n </ngb-pagination>\n </div>\n }\n\n @if (selectedTemplate) {\n <div class=\"alert alert-info mt-3\">\n <i class=\"bi bi-info-circle me-2\"></i>\n Template \"{{ selectedTemplate.name }}\" selected. Settings will be applied automatically.\n <button type=\"button\" class=\"btn btn-sm btn-outline-info float-end\" (click)=\"clearTemplate()\">\n Clear Selection\n </button>\n </div>\n }\n </div>\n\n <!-- Manual Entry -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-pencil me-2\"></i>Column Details\n @if (selectedTemplate) {\n <small class=\"text-muted ms-2\">(Auto-filled from template - you can modify as needed)</small>\n }\n </h6>\n </div>\n \n <div class=\"col-12 mb-3\">\n <label for=\"columnName\" class=\"form-label\">\n Column Name *\n <span class=\"text-muted ms-2\">\n <i class=\"bi bi-info-circle\"></i>\n Column names are managed through templates\n </span>\n </label>\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"columnName\"\n formControlName=\"name\"\n [class.is-invalid]=\"editForm.get('name')?.invalid && editForm.get('name')?.touched\"\n readonly\n placeholder=\"Select from official columns or templates below\">\n @if (isEdit) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"showTemplateSwapOptions()\"\n title=\"Change to different template\">\n <i class=\"bi bi-arrow-left-right\"></i> Swap Template\n </button>\n }\n </div>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n @if (isEdit) {\n To change the column name, swap to a different template or create a new custom template.\n } @else {\n Select from official SDRF columns or existing templates below to ensure compliance.\n }\n </div>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"columnType\" class=\"form-label\">\n Column Type *\n <span class=\"text-muted ms-2\">\n <i class=\"bi bi-info-circle\"></i>\n Managed through templates\n </span>\n </label>\n <select\n class=\"form-select\"\n id=\"columnType\"\n formControlName=\"type\"\n [class.is-invalid]=\"editForm.get('type')?.invalid && editForm.get('type')?.touched\">\n @for (type of columnTypes; track type.value) {\n <option [value]=\"type.value\">{{ type.label }}</option>\n }\n </select>\n </div>\n\n <!-- Advanced Mode Toggle -->\n <div class=\"col-12 mb-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"advancedMode\"\n [checked]=\"showAdvancedMode()\"\n (change)=\"toggleAdvancedMode()\">\n <label class=\"form-check-label\" for=\"advancedMode\">\n <i class=\"bi bi-gear me-1\"></i>Advanced Mode\n </label>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Show advanced options for ontology configuration and column settings\n </div>\n </div>\n </div>\n\n @if (showAdvancedMode()) {\n <div class=\"col-md-6 mb-3\">\n <label for=\"ontologyType\" class=\"form-label\">\n Ontology Type\n <span class=\"badge bg-warning ms-2\">\n <i class=\"bi bi-exclamation-triangle me-1\"></i>Advanced\n </span>\n </label>\n <select\n class=\"form-select\"\n id=\"ontologyType\"\n formControlName=\"ontologyType\"\n (change)=\"onOntologyTypeChange($event)\">\n @for (ontology of ontologyTypes; track $index) {\n <option [value]=\"$index\">{{ ontology.label }}</option>\n }\n </select>\n\n @if (hasOntologyType) {\n <div class=\"form-text mt-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Custom ontology filters will be applied based on the selected type\n </div>\n }\n </div>\n\n @if (hasOntologyType) {\n <div class=\"col-12 mb-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"enableTypeahead\"\n formControlName=\"enableTypeahead\">\n <label class=\"form-check-label\" for=\"enableTypeahead\">\n <i class=\"bi bi-search me-1\"></i>Enable Typeahead Autocomplete\n </label>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Enable autocomplete suggestions from {{ selectedOntologyConfig?.label || 'selected' }} ontology when editing values\n </div>\n </div>\n </div>\n }\n\n <!-- Column Options - Advanced Mode Only -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-sliders me-2\"></i>Column Options\n <span class=\"badge bg-warning ms-2\">\n <i class=\"bi bi-exclamation-triangle me-1\"></i>Advanced\n </span>\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"row\">\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"mandatory\"\n formControlName=\"mandatory\">\n <label class=\"form-check-label\" for=\"mandatory\">\n <i class=\"bi bi-exclamation-circle me-1\"></i>Mandatory\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"hidden\"\n formControlName=\"hidden\">\n <label class=\"form-check-label\" for=\"hidden\">\n <i class=\"bi bi-eye-slash me-1\"></i>Hidden\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"readonly\"\n formControlName=\"readonly\">\n <label class=\"form-check-label\" for=\"readonly\">\n <i class=\"bi bi-lock me-1\"></i>Read Only\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"notApplicable\"\n formControlName=\"notApplicable\">\n <label class=\"form-check-label\" for=\"notApplicable\">\n <i class=\"bi bi-dash-circle me-1\"></i>N/A\n </label>\n </div>\n </div>\n </div>\n </div>\n }\n\n <div class=\"col-12 mb-3\">\n <label for=\"defaultValue\" class=\"form-label\">\n Default Value\n @if (hasTypeaheadEnabled) {\n <span class=\"badge bg-success ms-2\">\n <i class=\"bi bi-search me-1\"></i>Typeahead Enabled\n </span>\n }\n </label>\n \n <div class=\"input-group\">\n <input \n type=\"text\" \n class=\"form-control\"\n id=\"defaultValue\"\n formControlName=\"value\"\n placeholder=\"Enter default value\"\n readonly>\n <button \n type=\"button\" \n class=\"btn btn-outline-primary\"\n (click)=\"openValueEditModal()\"\n title=\"Open advanced value editor\">\n <i class=\"bi bi-pencil me-1\"></i>Edit\n </button>\n </div>\n \n <div class=\"form-text\">\n @if (hasTypeaheadEnabled) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to use autocomplete suggestions from {{ selectedOntologyConfig?.label || 'selected' }} ontology\n } @else if (hasOntologyType) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set default value. Enable typeahead in Advanced Mode for autocomplete suggestions.\n } @else if (showAdvancedMode()) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set the default value (select an ontology type above for smart suggestions)\n } @else {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set the default value (enable Advanced Mode for ontology autocomplete)\n }\n </div>\n </div>\n\n </div>\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\" \n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ isEdit ? 'Update' : 'Add' }} Column\n </button>\n</div>\n", styles: [".template-selection-container{border:1px solid var(--bs-border-color);border-radius:.375rem;padding:.5rem}.template-item{cursor:pointer;transition:all .2s ease}.template-item:hover{background-color:rgba(var(--bs-primary-rgb),.1);border-color:var(--bs-primary)!important}.template-item.selected{background-color:rgba(var(--bs-primary-rgb),.15);border-color:var(--bs-primary)!important;box-shadow:0 0 0 .2rem rgba(var(--bs-primary-rgb),.25)}.cursor-pointer{cursor:pointer}.card.cursor-pointer{transition:all .2s ease}.card.cursor-pointer:hover{transform:translateY(-2px);box-shadow:0 4px 8px #0000001a}.card.cursor-pointer.border-info{border-width:2px}.modal-lg{max-width:900px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NgbModule }, { kind: "component", type: i2.NgbPagination, selector: "ngb-pagination", inputs: ["disabled", "boundaryLinks", "directionLinks", "ellipses", "rotate", "collectionSize", "maxSize", "page", "pageSize", "size"], outputs: ["pageChange"] }] });
4964
4962
  }
4965
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ColumnEditModal, decorators: [{
4963
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: ColumnEditModal, decorators: [{
4966
4964
  type: Component,
4967
4965
  args: [{ selector: 'ccv-column-edit-modal', imports: [CommonModule, ReactiveFormsModule, FormsModule, NgbModule], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-columns-gap me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row\">\n <!-- Selection Required Notice for New Columns -->\n @if (!isEdit) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-warning\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>Column Selection Required\n </h6>\n <p class=\"mb-0\">\n You must select from official SDRF columns or existing templates to ensure data compliance and consistency.\n </p>\n </div>\n </div>\n }\n\n <!-- Official SDRF Columns -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-file-earmark-check me-2\"></i>Official SDRF Columns\n <small class=\"text-muted ms-2\">\n @if (!isEdit) {\n (Step 1: Select from standardized SDRF-Proteomics column names)\n } @else {\n (Recommended - select from standardized SDRF-Proteomics column names)\n }\n </small>\n </h6>\n </div>\n\n @if (!isEdit) {\n <div class=\"col-12 mb-3\">\n @if (!showOfficialColumns) {\n <button\n type=\"button\"\n class=\"btn btn-primary mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-list-ul me-2\"></i>\n Select from Official SDRF Columns\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-x me-2\"></i>\n Hide Official Columns\n </button>\n }\n </div>\n } @else {\n <div class=\"col-12 mb-3\">\n <button\n type=\"button\"\n class=\"btn btn-outline-info mb-3\"\n (click)=\"toggleOfficialColumns()\">\n <i class=\"bi bi-list-ul me-2\"></i>\n {{ showOfficialColumns ? 'Hide' : 'Show' }} Official Column Names\n </button>\n </div>\n }\n\n @if (showOfficialColumns) {\n <div class=\"col-12 mb-3\">\n <div class=\"border rounded p-3\">\n <p class=\"mb-3 text-muted\">\n <i class=\"bi bi-info-circle me-2\"></i>\n Select from official SDRF-Proteomics column names. Only columns without existing templates are shown.\n </p>\n\n @if (availableOfficialColumns.length === 0) {\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n All official SDRF columns already have templates created.\n </div>\n } @else {\n <div class=\"row\">\n @for (column of availableOfficialColumns; track column.name) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"card h-100 cursor-pointer border-info\"\n (click)=\"selectOfficialColumn(column)\"\n [class.border-primary]=\"selectedOfficialColumn?.name === column.name\">\n <div class=\"card-body py-2\">\n <h6 class=\"card-title mb-1 font-monospace\">\n {{ column.name }}\n </h6>\n <p class=\"card-text small mb-1\">\n <span class=\"badge\"\n [class]=\"column.type === 'characteristics' ? 'bg-primary' :\n column.type === 'comment' ? 'bg-success' :\n column.type === 'factor_value' ? 'bg-warning' : 'bg-secondary'\">\n {{ column.type }}\n </span>\n </p>\n <p class=\"card-text small mb-0\">{{ column.description }}</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Template Swapping for Edit Mode -->\n @if (isEdit && showTemplateSwap) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-info\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-arrow-left-right me-2\"></i>Swap to Different Template\n </h6>\n <p class=\"mb-3\">\n Choose a different template to change the column name and settings. Your current value will be preserved.\n </p>\n\n <div class=\"mb-3\">\n <input\n type=\"text\"\n class=\"form-control mb-2\"\n placeholder=\"Search templates...\"\n [(ngModel)]=\"templateFilter\"\n [ngModelOptions]=\"{standalone: true}\"\n (input)=\"onTemplateFilterChange()\">\n\n <select\n class=\"form-select mb-3\"\n size=\"4\"\n [disabled]=\"isLoadingTemplates\">\n @if (isLoadingTemplates) {\n <option value=\"\" disabled>Loading templates...</option>\n } @else {\n @for (template of templates; track template.id) {\n <option\n [value]=\"template.id\"\n (click)=\"swapToTemplate(template)\"\n style=\"cursor: pointer;\">\n {{ template.columnName }} - {{ template.columnType }}@if (template.ontologyType) { ({{ getOntologyTypeLabel(template.ontologyType) }})}\n </option>\n }\n }\n </select>\n </div>\n\n <div class=\"d-flex justify-content-between\">\n <button\n type=\"button\"\n class=\"btn btn-success\"\n (click)=\"showCreateCustomTemplateOption()\">\n <i class=\"bi bi-plus-circle me-1\"></i>Create Custom Template\n </button>\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n (click)=\"cancelTemplateSwap()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Custom Template Creation Option -->\n @if (isEdit && showCreateCustomTemplate) {\n <div class=\"col-12 mb-3\">\n <div class=\"alert alert-warning\">\n <h6 class=\"alert-heading\">\n <i class=\"bi bi-plus-circle me-2\"></i>Create Custom Template\n </h6>\n <p class=\"mb-3\">\n This will create a new custom template with your desired column name and settings.\n </p>\n\n <div class=\"d-flex justify-content-between\">\n <button\n type=\"button\"\n class=\"btn btn-warning\"\n (click)=\"createCustomTemplate()\">\n <i class=\"bi bi-gear me-1\"></i>Create & Apply Template\n </button>\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n (click)=\"cancelTemplateSwap()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Template Selection -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Template Selection\n <small class=\"text-muted ms-2\">\n @if (!isEdit) {\n (Step 2: Or select from existing templates)\n } @else {\n (Optional - select a template to auto-fill column settings)\n }\n </small>\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"input-group mb-3\">\n <span class=\"input-group-text\">\n <i class=\"bi bi-search\"></i>\n </span>\n <input \n type=\"text\" \n class=\"form-control\" \n placeholder=\"Search column templates...\"\n [(ngModel)]=\"templateFilter\"\n [ngModelOptions]=\"{standalone: true}\"\n (input)=\"onTemplateFilterChange()\">\n @if (templateFilter) {\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"clearTemplateFilter()\">\n <i class=\"bi bi-x\"></i>\n </button>\n }\n </div>\n\n <select \n class=\"form-select mb-3\"\n size=\"5\"\n [(ngModel)]=\"selectedTemplateId\"\n [ngModelOptions]=\"{standalone: true}\"\n (change)=\"onTemplateSelectionChange()\"\n [disabled]=\"isLoadingTemplates\">\n <option value=\"\">No template selected</option>\n @if (isLoadingTemplates) {\n <option value=\"\" disabled>Loading templates...</option>\n } @else {\n @for (template of templates; track template.id) {\n <option [value]=\"template.id\">\n {{ template.columnName }} - {{ template.columnType }}@if (template.ontologyType) { ({{ getOntologyTypeLabel(template.ontologyType) }})}\n </option>\n }\n }\n </select>\n\n @if (totalTemplatePages > 1) {\n <div class=\"d-flex justify-content-between align-items-center\">\n <small class=\"text-muted\">\n Showing {{ (currentTemplatePage - 1) * templatePageSize + 1 }}-{{ Math.min(currentTemplatePage * templatePageSize, totalTemplates) }}\n of {{ totalTemplates }}\n </small>\n <ngb-pagination\n [collectionSize]=\"totalTemplates\"\n [page]=\"currentTemplatePage\"\n [pageSize]=\"templatePageSize\"\n [maxSize]=\"3\"\n [boundaryLinks]=\"true\"\n [rotate]=\"true\"\n (pageChange)=\"onTemplatePageChange($event)\"\n class=\"pagination-sm mb-0\">\n </ngb-pagination>\n </div>\n }\n\n @if (selectedTemplate) {\n <div class=\"alert alert-info mt-3\">\n <i class=\"bi bi-info-circle me-2\"></i>\n Template \"{{ selectedTemplate.name }}\" selected. Settings will be applied automatically.\n <button type=\"button\" class=\"btn btn-sm btn-outline-info float-end\" (click)=\"clearTemplate()\">\n Clear Selection\n </button>\n </div>\n }\n </div>\n\n <!-- Manual Entry -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-pencil me-2\"></i>Column Details\n @if (selectedTemplate) {\n <small class=\"text-muted ms-2\">(Auto-filled from template - you can modify as needed)</small>\n }\n </h6>\n </div>\n \n <div class=\"col-12 mb-3\">\n <label for=\"columnName\" class=\"form-label\">\n Column Name *\n <span class=\"text-muted ms-2\">\n <i class=\"bi bi-info-circle\"></i>\n Column names are managed through templates\n </span>\n </label>\n <div class=\"input-group\">\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"columnName\"\n formControlName=\"name\"\n [class.is-invalid]=\"editForm.get('name')?.invalid && editForm.get('name')?.touched\"\n readonly\n placeholder=\"Select from official columns or templates below\">\n @if (isEdit) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"showTemplateSwapOptions()\"\n title=\"Change to different template\">\n <i class=\"bi bi-arrow-left-right\"></i> Swap Template\n </button>\n }\n </div>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n @if (isEdit) {\n To change the column name, swap to a different template or create a new custom template.\n } @else {\n Select from official SDRF columns or existing templates below to ensure compliance.\n }\n </div>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"columnType\" class=\"form-label\">\n Column Type *\n <span class=\"text-muted ms-2\">\n <i class=\"bi bi-info-circle\"></i>\n Managed through templates\n </span>\n </label>\n <select\n class=\"form-select\"\n id=\"columnType\"\n formControlName=\"type\"\n [class.is-invalid]=\"editForm.get('type')?.invalid && editForm.get('type')?.touched\">\n @for (type of columnTypes; track type.value) {\n <option [value]=\"type.value\">{{ type.label }}</option>\n }\n </select>\n </div>\n\n <!-- Advanced Mode Toggle -->\n <div class=\"col-12 mb-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"advancedMode\"\n [checked]=\"showAdvancedMode()\"\n (change)=\"toggleAdvancedMode()\">\n <label class=\"form-check-label\" for=\"advancedMode\">\n <i class=\"bi bi-gear me-1\"></i>Advanced Mode\n </label>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Show advanced options for ontology configuration and column settings\n </div>\n </div>\n </div>\n\n @if (showAdvancedMode()) {\n <div class=\"col-md-6 mb-3\">\n <label for=\"ontologyType\" class=\"form-label\">\n Ontology Type\n <span class=\"badge bg-warning ms-2\">\n <i class=\"bi bi-exclamation-triangle me-1\"></i>Advanced\n </span>\n </label>\n <select\n class=\"form-select\"\n id=\"ontologyType\"\n formControlName=\"ontologyType\"\n (change)=\"onOntologyTypeChange($event)\">\n @for (ontology of ontologyTypes; track $index) {\n <option [value]=\"$index\">{{ ontology.label }}</option>\n }\n </select>\n\n @if (hasOntologyType) {\n <div class=\"form-text mt-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Custom ontology filters will be applied based on the selected type\n </div>\n }\n </div>\n\n @if (hasOntologyType) {\n <div class=\"col-12 mb-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"enableTypeahead\"\n formControlName=\"enableTypeahead\">\n <label class=\"form-check-label\" for=\"enableTypeahead\">\n <i class=\"bi bi-search me-1\"></i>Enable Typeahead Autocomplete\n </label>\n <div class=\"form-text\">\n <i class=\"bi bi-info-circle me-1\"></i>\n Enable autocomplete suggestions from {{ selectedOntologyConfig?.label || 'selected' }} ontology when editing values\n </div>\n </div>\n </div>\n }\n\n <!-- Column Options - Advanced Mode Only -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-sliders me-2\"></i>Column Options\n <span class=\"badge bg-warning ms-2\">\n <i class=\"bi bi-exclamation-triangle me-1\"></i>Advanced\n </span>\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"row\">\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"mandatory\"\n formControlName=\"mandatory\">\n <label class=\"form-check-label\" for=\"mandatory\">\n <i class=\"bi bi-exclamation-circle me-1\"></i>Mandatory\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"hidden\"\n formControlName=\"hidden\">\n <label class=\"form-check-label\" for=\"hidden\">\n <i class=\"bi bi-eye-slash me-1\"></i>Hidden\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"readonly\"\n formControlName=\"readonly\">\n <label class=\"form-check-label\" for=\"readonly\">\n <i class=\"bi bi-lock me-1\"></i>Read Only\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"notApplicable\"\n formControlName=\"notApplicable\">\n <label class=\"form-check-label\" for=\"notApplicable\">\n <i class=\"bi bi-dash-circle me-1\"></i>N/A\n </label>\n </div>\n </div>\n </div>\n </div>\n }\n\n <div class=\"col-12 mb-3\">\n <label for=\"defaultValue\" class=\"form-label\">\n Default Value\n @if (hasTypeaheadEnabled) {\n <span class=\"badge bg-success ms-2\">\n <i class=\"bi bi-search me-1\"></i>Typeahead Enabled\n </span>\n }\n </label>\n \n <div class=\"input-group\">\n <input \n type=\"text\" \n class=\"form-control\"\n id=\"defaultValue\"\n formControlName=\"value\"\n placeholder=\"Enter default value\"\n readonly>\n <button \n type=\"button\" \n class=\"btn btn-outline-primary\"\n (click)=\"openValueEditModal()\"\n title=\"Open advanced value editor\">\n <i class=\"bi bi-pencil me-1\"></i>Edit\n </button>\n </div>\n \n <div class=\"form-text\">\n @if (hasTypeaheadEnabled) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to use autocomplete suggestions from {{ selectedOntologyConfig?.label || 'selected' }} ontology\n } @else if (hasOntologyType) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set default value. Enable typeahead in Advanced Mode for autocomplete suggestions.\n } @else if (showAdvancedMode()) {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set the default value (select an ontology type above for smart suggestions)\n } @else {\n <i class=\"bi bi-info-circle me-1\"></i>\n Click \"Edit\" to set the default value (enable Advanced Mode for ontology autocomplete)\n }\n </div>\n </div>\n\n </div>\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\" \n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ isEdit ? 'Update' : 'Add' }} Column\n </button>\n</div>\n", styles: [".template-selection-container{border:1px solid var(--bs-border-color);border-radius:.375rem;padding:.5rem}.template-item{cursor:pointer;transition:all .2s ease}.template-item:hover{background-color:rgba(var(--bs-primary-rgb),.1);border-color:var(--bs-primary)!important}.template-item.selected{background-color:rgba(var(--bs-primary-rgb),.15);border-color:var(--bs-primary)!important;box-shadow:0 0 0 .2rem rgba(var(--bs-primary-rgb),.25)}.cursor-pointer{cursor:pointer}.card.cursor-pointer{transition:all .2s ease}.card.cursor-pointer:hover{transform:translateY(-2px);box-shadow:0 4px 8px #0000001a}.card.cursor-pointer.border-info{border-width:2px}.modal-lg{max-width:900px}\n"] }]
4968
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i2.NgbModal }, { type: i4.MetadataColumnTemplateService }, { type: i4.SdrfSyntaxService }], propDecorators: { column: [{
4966
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i2.NgbModal }, { type: MetadataColumnTemplateService }, { type: SdrfSyntaxService }], propDecorators: { column: [{
4969
4967
  type: Input
4970
4968
  }], templateId: [{
4971
4969
  type: Input
@@ -5281,10 +5279,10 @@ class MetadataTableTemplateEditModal {
5281
5279
  columns.length = 0;
5282
5280
  columns.push(...regroupedColumns);
5283
5281
  }
5284
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplateEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i2.NgbModal }, { token: MetadataTableTemplateService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Component });
5285
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: MetadataTableTemplateEditModal, isStandalone: true, selector: "ccv-metadata-table-template-edit-modal", inputs: { template: "template", labGroups: "labGroups", isEdit: "isEdit" }, outputs: { templateSaved: "templateSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row\">\n <!-- Basic Information -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-info-circle me-2\"></i>Basic Information\n </h6>\n </div>\n \n <div class=\"col-12 mb-3\">\n <label for=\"templateName\" class=\"form-label\">Template Name *</label>\n <input \n type=\"text\" \n class=\"form-control\"\n id=\"templateName\"\n formControlName=\"name\"\n [class.is-invalid]=\"editForm.get('name')?.invalid && editForm.get('name')?.touched\"\n placeholder=\"Enter template name\">\n <div class=\"invalid-feedback\">\n {{ getFieldError('name') }}\n </div>\n </div>\n\n <div class=\"col-12 mb-3\">\n <label for=\"description\" class=\"form-label\">Description</label>\n <textarea \n class=\"form-control\" \n id=\"description\"\n formControlName=\"description\"\n rows=\"3\"\n placeholder=\"Optional description\"></textarea>\n </div>\n\n <!-- Configuration -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-gear me-2\"></i>Configuration\n </h6>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"labGroup\" class=\"form-label\">Lab Group</label>\n <select \n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <label for=\"visibility\" class=\"form-label\">Visibility</label>\n <select \n class=\"form-select\"\n id=\"visibility\"\n formControlName=\"visibility\">\n @for (option of visibilityOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-check form-switch\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"isDefault\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"isDefault\">\n <i class=\"bi bi-star me-1\"></i>Default Template\n </label>\n <small class=\"form-text text-muted d-block\">Use as default for new projects</small>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Column Management -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-columns-gap me-2\"></i>Template Columns\n <button type=\"button\" class=\"btn btn-sm btn-outline-primary ms-2\" (click)=\"addColumn()\">\n <i class=\"bi bi-plus me-1\"></i>Add Column\n </button>\n @if (isEdit && template?.id) {\n <button type=\"button\" class=\"btn btn-sm btn-success ms-2\" (click)=\"addColumnWithAutoReorder()\" \n [disabled]=\"isLoading()\"\n title=\"Add column with automatic schema-based reordering\">\n <i class=\"bi bi-plus-circle me-1\"></i>Add Column (Auto-Reorder)\n </button>\n }\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n @if (templateColumns().length > 0) {\n <div class=\"table-responsive\">\n <table class=\"table table-sm table-hover\">\n <thead class=\"table-light\">\n <tr>\n <th style=\"width: 40px;\"><i class=\"bi bi-arrows-move\"></i></th>\n <th>Position</th>\n <th>Column Name</th>\n <th>Type</th>\n <th>Default Value</th>\n <th>Options</th>\n <th style=\"width: 120px;\">Actions</th>\n </tr>\n </thead>\n <tbody cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\" [cdkDropListData]=\"_templateColumns()\">\n @for (column of _templateColumns(); track column.id; let i = $index) {\n <tr cdkDrag [cdkDragData]=\"column\" class=\"column-row\" [attr.data-column-id]=\"column.id\">\n <td class=\"text-center\">\n <i class=\"bi bi-grip-vertical drag-handle\" cdkDragHandle></i>\n </td>\n <td>\n <span class=\"badge bg-secondary\">{{ i + 1 }}</span>\n </td>\n <td>\n <strong>{{ column.name }}</strong>\n </td>\n <td>\n <span class=\"badge bg-info\">{{ column.type }}</span>\n </td>\n <td>\n <span class=\"text-muted\">{{ column.value || '-' }}</span>\n </td>\n <td>\n <div class=\"d-flex gap-1\">\n @if (column.mandatory) {\n <span class=\"badge badge-pill bg-warning\" title=\"Mandatory\">\n <i class=\"bi bi-exclamation-circle\"></i>\n </span>\n }\n @if (column.hidden) {\n <span class=\"badge badge-pill bg-secondary\" title=\"Hidden\">\n <i class=\"bi bi-eye-slash\"></i>\n </span>\n }\n @if (column.readonly) {\n <span class=\"badge badge-pill bg-dark\" title=\"Read Only\">\n <i class=\"bi bi-lock\"></i>\n </span>\n }\n @if (column.ontologyType) {\n <span class=\"badge badge-pill bg-success\" title=\"Has Ontology\">\n <i class=\"bi bi-tags\"></i>\n </span>\n }\n </div>\n </td>\n <td>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button \n type=\"button\"\n class=\"btn btn-outline-primary\" \n (click)=\"editColumn(column)\"\n title=\"Edit column\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n <button \n type=\"button\"\n class=\"btn btn-outline-info\" \n (click)=\"duplicateColumn(column)\"\n title=\"Duplicate column\">\n <i class=\"bi bi-copy\"></i>\n </button>\n <button \n type=\"button\"\n class=\"btn btn-outline-danger\" \n (click)=\"removeColumn(column)\"\n title=\"Remove column\">\n <i class=\"bi bi-trash\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"text-center py-4 text-muted\">\n <i class=\"bi bi-columns-gap fs-1 mb-2\"></i>\n <p>No columns in this template yet.</p>\n <div class=\"d-flex gap-2 justify-content-center\">\n <button type=\"button\" class=\"btn btn-primary btn-sm\" (click)=\"addColumn()\">\n <i class=\"bi bi-plus me-1\"></i>Add First Column\n </button>\n @if (isEdit && template?.id) {\n <button type=\"button\" class=\"btn btn-success btn-sm\" (click)=\"addColumnWithAutoReorder()\" \n [disabled]=\"isLoading()\"\n title=\"Add column with automatic schema-based reordering\">\n <i class=\"bi bi-plus-circle me-1\"></i>Add with Auto-Reorder\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\" \n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ isEdit ? 'Update' : 'Create' }} Template\n </button>\n</div>\n", styles: [".column-row{transition:transform .2s cubic-bezier(0,0,.2,1)}.column-row.cdk-drag-disabled{opacity:.5}.column-row.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.column-row.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;background:var(--cupcake-card-bg)}.column-row.cdk-drag-preview td{border:none!important}.column-row.cdk-drag-placeholder{opacity:0}.drag-handle{cursor:grab;color:var(--cupcake-text-muted)}.drag-handle:hover{color:var(--cupcake-text)}.drag-handle:active{cursor:grabbing}.column-row[data-column-group]{position:relative}.column-row[data-column-group]:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--bs-primary)}.badge.badge-pill{border-radius:50px;font-size:.65em;padding:.25em .5em}.modal-lg{max-width:900px}.table-responsive{border-radius:.375rem;overflow:hidden}.table-sm{font-size:.875rem}.table-sm th,.table-sm td{padding:.5rem;vertical-align:middle}.text-center.py-4{background:var(--cupcake-bg-tertiary);border-radius:.375rem;margin:1rem 0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5$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: i5$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: i5$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] });
5282
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplateEditModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }, { token: i2.NgbModal }, { token: MetadataTableTemplateService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Component });
5283
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: MetadataTableTemplateEditModal, isStandalone: true, selector: "ccv-metadata-table-template-edit-modal", inputs: { template: "template", labGroups: "labGroups", isEdit: "isEdit" }, outputs: { templateSaved: "templateSaved" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row\">\n <!-- Basic Information -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-info-circle me-2\"></i>Basic Information\n </h6>\n </div>\n \n <div class=\"col-12 mb-3\">\n <label for=\"templateName\" class=\"form-label\">Template Name *</label>\n <input \n type=\"text\" \n class=\"form-control\"\n id=\"templateName\"\n formControlName=\"name\"\n [class.is-invalid]=\"editForm.get('name')?.invalid && editForm.get('name')?.touched\"\n placeholder=\"Enter template name\">\n <div class=\"invalid-feedback\">\n {{ getFieldError('name') }}\n </div>\n </div>\n\n <div class=\"col-12 mb-3\">\n <label for=\"description\" class=\"form-label\">Description</label>\n <textarea \n class=\"form-control\" \n id=\"description\"\n formControlName=\"description\"\n rows=\"3\"\n placeholder=\"Optional description\"></textarea>\n </div>\n\n <!-- Configuration -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-gear me-2\"></i>Configuration\n </h6>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"labGroup\" class=\"form-label\">Lab Group</label>\n <select \n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <label for=\"visibility\" class=\"form-label\">Visibility</label>\n <select \n class=\"form-select\"\n id=\"visibility\"\n formControlName=\"visibility\">\n @for (option of visibilityOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-check form-switch\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"isDefault\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"isDefault\">\n <i class=\"bi bi-star me-1\"></i>Default Template\n </label>\n <small class=\"form-text text-muted d-block\">Use as default for new projects</small>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Column Management -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-columns-gap me-2\"></i>Template Columns\n <button type=\"button\" class=\"btn btn-sm btn-outline-primary ms-2\" (click)=\"addColumn()\">\n <i class=\"bi bi-plus me-1\"></i>Add Column\n </button>\n @if (isEdit && template?.id) {\n <button type=\"button\" class=\"btn btn-sm btn-success ms-2\" (click)=\"addColumnWithAutoReorder()\" \n [disabled]=\"isLoading()\"\n title=\"Add column with automatic schema-based reordering\">\n <i class=\"bi bi-plus-circle me-1\"></i>Add Column (Auto-Reorder)\n </button>\n }\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n @if (templateColumns().length > 0) {\n <div class=\"table-responsive\">\n <table class=\"table table-sm table-hover\">\n <thead class=\"table-light\">\n <tr>\n <th style=\"width: 40px;\"><i class=\"bi bi-arrows-move\"></i></th>\n <th>Position</th>\n <th>Column Name</th>\n <th>Type</th>\n <th>Default Value</th>\n <th>Options</th>\n <th style=\"width: 120px;\">Actions</th>\n </tr>\n </thead>\n <tbody cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\" [cdkDropListData]=\"_templateColumns()\">\n @for (column of _templateColumns(); track column.id; let i = $index) {\n <tr cdkDrag [cdkDragData]=\"column\" class=\"column-row\" [attr.data-column-id]=\"column.id\">\n <td class=\"text-center\">\n <i class=\"bi bi-grip-vertical drag-handle\" cdkDragHandle></i>\n </td>\n <td>\n <span class=\"badge bg-secondary\">{{ i + 1 }}</span>\n </td>\n <td>\n <strong>{{ column.name }}</strong>\n </td>\n <td>\n <span class=\"badge bg-info\">{{ column.type }}</span>\n </td>\n <td>\n <span class=\"text-muted\">{{ column.value || '-' }}</span>\n </td>\n <td>\n <div class=\"d-flex gap-1\">\n @if (column.mandatory) {\n <span class=\"badge badge-pill bg-warning\" title=\"Mandatory\">\n <i class=\"bi bi-exclamation-circle\"></i>\n </span>\n }\n @if (column.hidden) {\n <span class=\"badge badge-pill bg-secondary\" title=\"Hidden\">\n <i class=\"bi bi-eye-slash\"></i>\n </span>\n }\n @if (column.readonly) {\n <span class=\"badge badge-pill bg-dark\" title=\"Read Only\">\n <i class=\"bi bi-lock\"></i>\n </span>\n }\n @if (column.ontologyType) {\n <span class=\"badge badge-pill bg-success\" title=\"Has Ontology\">\n <i class=\"bi bi-tags\"></i>\n </span>\n }\n </div>\n </td>\n <td>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button \n type=\"button\"\n class=\"btn btn-outline-primary\" \n (click)=\"editColumn(column)\"\n title=\"Edit column\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n <button \n type=\"button\"\n class=\"btn btn-outline-info\" \n (click)=\"duplicateColumn(column)\"\n title=\"Duplicate column\">\n <i class=\"bi bi-copy\"></i>\n </button>\n <button \n type=\"button\"\n class=\"btn btn-outline-danger\" \n (click)=\"removeColumn(column)\"\n title=\"Remove column\">\n <i class=\"bi bi-trash\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"text-center py-4 text-muted\">\n <i class=\"bi bi-columns-gap fs-1 mb-2\"></i>\n <p>No columns in this template yet.</p>\n <div class=\"d-flex gap-2 justify-content-center\">\n <button type=\"button\" class=\"btn btn-primary btn-sm\" (click)=\"addColumn()\">\n <i class=\"bi bi-plus me-1\"></i>Add First Column\n </button>\n @if (isEdit && template?.id) {\n <button type=\"button\" class=\"btn btn-success btn-sm\" (click)=\"addColumnWithAutoReorder()\" \n [disabled]=\"isLoading()\"\n title=\"Add column with automatic schema-based reordering\">\n <i class=\"bi bi-plus-circle me-1\"></i>Add with Auto-Reorder\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\" \n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ isEdit ? 'Update' : 'Create' }} Template\n </button>\n</div>\n", styles: [".column-row{transition:transform .2s cubic-bezier(0,0,.2,1)}.column-row.cdk-drag-disabled{opacity:.5}.column-row.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.column-row.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;background:var(--cupcake-card-bg)}.column-row.cdk-drag-preview td{border:none!important}.column-row.cdk-drag-placeholder{opacity:0}.drag-handle{cursor:grab;color:var(--cupcake-text-muted)}.drag-handle:hover{color:var(--cupcake-text)}.drag-handle:active{cursor:grabbing}.column-row[data-column-group]{position:relative}.column-row[data-column-group]:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--bs-primary)}.badge.badge-pill{border-radius:50px;font-size:.65em;padding:.25em .5em}.modal-lg{max-width:900px}.table-responsive{border-radius:.375rem;overflow:hidden}.table-sm{font-size:.875rem}.table-sm th,.table-sm td{padding:.5rem;vertical-align:middle}.text-center.py-4{background:var(--cupcake-bg-tertiary);border-radius:.375rem;margin:1rem 0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5$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: i5$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: i5$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] });
5286
5284
  }
5287
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplateEditModal, decorators: [{
5285
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplateEditModal, decorators: [{
5288
5286
  type: Component,
5289
5287
  args: [{ selector: 'ccv-metadata-table-template-edit-modal', imports: [CommonModule, ReactiveFormsModule, NgbModule, DragDropModule], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>{{ title }}\n </h4>\n <button type=\"button\" class=\"btn-close\" (click)=\"onCancel()\"></button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"editForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row\">\n <!-- Basic Information -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-info-circle me-2\"></i>Basic Information\n </h6>\n </div>\n \n <div class=\"col-12 mb-3\">\n <label for=\"templateName\" class=\"form-label\">Template Name *</label>\n <input \n type=\"text\" \n class=\"form-control\"\n id=\"templateName\"\n formControlName=\"name\"\n [class.is-invalid]=\"editForm.get('name')?.invalid && editForm.get('name')?.touched\"\n placeholder=\"Enter template name\">\n <div class=\"invalid-feedback\">\n {{ getFieldError('name') }}\n </div>\n </div>\n\n <div class=\"col-12 mb-3\">\n <label for=\"description\" class=\"form-label\">Description</label>\n <textarea \n class=\"form-control\" \n id=\"description\"\n formControlName=\"description\"\n rows=\"3\"\n placeholder=\"Optional description\"></textarea>\n </div>\n\n <!-- Configuration -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-gear me-2\"></i>Configuration\n </h6>\n </div>\n\n <div class=\"col-md-6 mb-3\">\n <label for=\"labGroup\" class=\"form-label\">Lab Group</label>\n <select \n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n </div>\n\n <div class=\"col-12 mb-3\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <label for=\"visibility\" class=\"form-label\">Visibility</label>\n <select \n class=\"form-select\"\n id=\"visibility\"\n formControlName=\"visibility\">\n @for (option of visibilityOptions; track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-check form-switch\">\n <input \n class=\"form-check-input\" \n type=\"checkbox\" \n id=\"isDefault\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"isDefault\">\n <i class=\"bi bi-star me-1\"></i>Default Template\n </label>\n <small class=\"form-text text-muted d-block\">Use as default for new projects</small>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Column Management -->\n <div class=\"col-12 mb-3\">\n <h6 class=\"text-muted border-bottom pb-2\">\n <i class=\"bi bi-columns-gap me-2\"></i>Template Columns\n <button type=\"button\" class=\"btn btn-sm btn-outline-primary ms-2\" (click)=\"addColumn()\">\n <i class=\"bi bi-plus me-1\"></i>Add Column\n </button>\n @if (isEdit && template?.id) {\n <button type=\"button\" class=\"btn btn-sm btn-success ms-2\" (click)=\"addColumnWithAutoReorder()\" \n [disabled]=\"isLoading()\"\n title=\"Add column with automatic schema-based reordering\">\n <i class=\"bi bi-plus-circle me-1\"></i>Add Column (Auto-Reorder)\n </button>\n }\n </h6>\n </div>\n\n <div class=\"col-12 mb-3\">\n @if (templateColumns().length > 0) {\n <div class=\"table-responsive\">\n <table class=\"table table-sm table-hover\">\n <thead class=\"table-light\">\n <tr>\n <th style=\"width: 40px;\"><i class=\"bi bi-arrows-move\"></i></th>\n <th>Position</th>\n <th>Column Name</th>\n <th>Type</th>\n <th>Default Value</th>\n <th>Options</th>\n <th style=\"width: 120px;\">Actions</th>\n </tr>\n </thead>\n <tbody cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\" [cdkDropListData]=\"_templateColumns()\">\n @for (column of _templateColumns(); track column.id; let i = $index) {\n <tr cdkDrag [cdkDragData]=\"column\" class=\"column-row\" [attr.data-column-id]=\"column.id\">\n <td class=\"text-center\">\n <i class=\"bi bi-grip-vertical drag-handle\" cdkDragHandle></i>\n </td>\n <td>\n <span class=\"badge bg-secondary\">{{ i + 1 }}</span>\n </td>\n <td>\n <strong>{{ column.name }}</strong>\n </td>\n <td>\n <span class=\"badge bg-info\">{{ column.type }}</span>\n </td>\n <td>\n <span class=\"text-muted\">{{ column.value || '-' }}</span>\n </td>\n <td>\n <div class=\"d-flex gap-1\">\n @if (column.mandatory) {\n <span class=\"badge badge-pill bg-warning\" title=\"Mandatory\">\n <i class=\"bi bi-exclamation-circle\"></i>\n </span>\n }\n @if (column.hidden) {\n <span class=\"badge badge-pill bg-secondary\" title=\"Hidden\">\n <i class=\"bi bi-eye-slash\"></i>\n </span>\n }\n @if (column.readonly) {\n <span class=\"badge badge-pill bg-dark\" title=\"Read Only\">\n <i class=\"bi bi-lock\"></i>\n </span>\n }\n @if (column.ontologyType) {\n <span class=\"badge badge-pill bg-success\" title=\"Has Ontology\">\n <i class=\"bi bi-tags\"></i>\n </span>\n }\n </div>\n </td>\n <td>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button \n type=\"button\"\n class=\"btn btn-outline-primary\" \n (click)=\"editColumn(column)\"\n title=\"Edit column\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n <button \n type=\"button\"\n class=\"btn btn-outline-info\" \n (click)=\"duplicateColumn(column)\"\n title=\"Duplicate column\">\n <i class=\"bi bi-copy\"></i>\n </button>\n <button \n type=\"button\"\n class=\"btn btn-outline-danger\" \n (click)=\"removeColumn(column)\"\n title=\"Remove column\">\n <i class=\"bi bi-trash\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"text-center py-4 text-muted\">\n <i class=\"bi bi-columns-gap fs-1 mb-2\"></i>\n <p>No columns in this template yet.</p>\n <div class=\"d-flex gap-2 justify-content-center\">\n <button type=\"button\" class=\"btn btn-primary btn-sm\" (click)=\"addColumn()\">\n <i class=\"bi bi-plus me-1\"></i>Add First Column\n </button>\n @if (isEdit && template?.id) {\n <button type=\"button\" class=\"btn btn-success btn-sm\" (click)=\"addColumnWithAutoReorder()\" \n [disabled]=\"isLoading()\"\n title=\"Add column with automatic schema-based reordering\">\n <i class=\"bi bi-plus-circle me-1\"></i>Add with Auto-Reorder\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-secondary\" (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button \n type=\"button\" \n class=\"btn btn-primary\" \n (click)=\"onSubmit()\"\n [disabled]=\"editForm.invalid || isLoading()\">\n @if (isLoading()) {\n <span class=\"spinner-border spinner-border-sm me-2\"></span>\n }\n <i class=\"bi bi-check-circle me-1\"></i>\n {{ isEdit ? 'Update' : 'Create' }} Template\n </button>\n</div>\n", styles: [".column-row{transition:transform .2s cubic-bezier(0,0,.2,1)}.column-row.cdk-drag-disabled{opacity:.5}.column-row.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.column-row.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;background:var(--cupcake-card-bg)}.column-row.cdk-drag-preview td{border:none!important}.column-row.cdk-drag-placeholder{opacity:0}.drag-handle{cursor:grab;color:var(--cupcake-text-muted)}.drag-handle:hover{color:var(--cupcake-text)}.drag-handle:active{cursor:grabbing}.column-row[data-column-group]{position:relative}.column-row[data-column-group]:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--bs-primary)}.badge.badge-pill{border-radius:50px;font-size:.65em;padding:.25em .5em}.modal-lg{max-width:900px}.table-responsive{border-radius:.375rem;overflow:hidden}.table-sm{font-size:.875rem}.table-sm th,.table-sm td{padding:.5rem;vertical-align:middle}.text-center.py-4{background:var(--cupcake-bg-tertiary);border-radius:.375rem;margin:1rem 0}\n"] }]
5290
5288
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }, { type: i2.NgbModal }, { type: MetadataTableTemplateService }, { type: i3.ToastService }], propDecorators: { template: [{
@@ -5399,10 +5397,10 @@ class TemplateSchemaSelectionModal {
5399
5397
  onCancel() {
5400
5398
  this.activeModal.dismiss();
5401
5399
  }
5402
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: TemplateSchemaSelectionModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }], target: i0.ɵɵFactoryTarget.Component });
5403
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: TemplateSchemaSelectionModal, isStandalone: true, selector: "ccv-template-schema-selection-modal", inputs: { availableSchemas: "availableSchemas", labGroups: "labGroups" }, outputs: { templateCreated: "templateCreated" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-file-earmark-plus me-2\"></i>Create Template from Schemas\n </h4>\n <button\n type=\"button\"\n class=\"btn-close\"\n aria-label=\"Close\"\n (click)=\"onCancel()\">\n </button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"templateForm\" class=\"needs-validation\" novalidate>\n <!-- Template Basic Information -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-info-circle me-1\"></i>Template Information\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-12 mb-3\">\n <div class=\"form-floating\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.is-invalid]=\"templateForm.get('name')?.invalid && templateForm.get('name')?.touched\"\n id=\"templateName\"\n formControlName=\"name\"\n placeholder=\"Enter template name\">\n <label for=\"templateName\">Template Name *</label>\n @if (templateForm.get('name')?.invalid && templateForm.get('name')?.touched) {\n <div class=\"invalid-feedback\">\n Template name is required.\n </div>\n }\n </div>\n </div>\n <div class=\"col-12 mb-3\">\n <div class=\"form-floating\">\n <textarea\n class=\"form-control\"\n id=\"templateDescription\"\n formControlName=\"description\"\n style=\"height: 80px\"\n placeholder=\"Enter template description\">\n </textarea>\n <label for=\"templateDescription\">Description</label>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Schema Selection -->\n <div class=\"card mb-4\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-check2-square me-1\"></i>Select Schemas\n </h6>\n <small class=\"text-muted\">{{ selectedSchemasCount() }} selected</small>\n </div>\n <div class=\"card-body\">\n @if (availableSchemas.length === 0) {\n <div class=\"text-center text-muted py-3\">\n <i class=\"bi bi-exclamation-triangle fs-4 d-block mb-2\"></i>\n No schemas available\n </div>\n } @else {\n <div class=\"row\">\n @for (schema of availableSchemas; track schema.id) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"card schema-card\"\n [class.selected]=\"isSchemaSelected(schema.id)\"\n (click)=\"toggleSchema(schema.id)\">\n <div class=\"card-body p-3\">\n <div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n [id]=\"'schema-' + schema.id\"\n [checked]=\"isSchemaSelected(schema.id)\"\n (change)=\"onSchemaCheckChange($event, schema.id)\">\n <label class=\"form-check-label w-100\" [for]=\"'schema-' + schema.id\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"flex-grow-1\">\n <strong class=\"schema-title\">{{ schema.displayName }}</strong>\n @if (schema.description) {\n <p class=\"schema-description text-muted small mb-1\">\n {{ schema.description }}\n </p>\n }\n <div class=\"schema-metadata\">\n @if (schema.tags && schema.tags.length > 0) {\n @for (tag of schema.tags; track tag) {\n <span class=\"badge bg-secondary me-1\">{{ tag }}</span>\n }\n }\n @if (schema.usage_count > 0) {\n <small class=\"text-muted\">\n <i class=\"bi bi-graph-up me-1\"></i>{{ schema.usage_count }} uses\n </small>\n }\n </div>\n </div>\n </div>\n </label>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n @if (selectedSchemasCount() === 0) {\n <div class=\"alert alert-warning mt-3\" role=\"alert\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n Please select at least one schema to create a template.\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Template Settings -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-gear me-1\"></i>Template Settings\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6 mb-3\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n <label for=\"labGroup\">Lab Group</label>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-check form-switch mb-3\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"isDefault\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"isDefault\">\n <i class=\"bi bi-star me-1\"></i>Default Template\n </label>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Preview Section -->\n @if (selectedSchemasCount() > 0) {\n <div class=\"card\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-eye me-1\"></i>Selected Schemas Preview\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"d-flex flex-wrap gap-2\">\n @for (schemaId of getSelectedSchemas(); track schemaId) {\n <span class=\"badge bg-primary fs-6 py-2 px-3\">\n {{ getSchemaDisplayName(schemaId) }}\n <button\n type=\"button\"\n class=\"btn-close btn-close-white ms-2\"\n style=\"font-size: 0.6em;\"\n (click)=\"removeSchema(schemaId)\"\n [attr.aria-label]=\"'Remove ' + getSchemaDisplayName(schemaId)\">\n </button>\n </span>\n }\n </div>\n <p class=\"text-muted small mt-2 mb-0\">\n This template will combine columns from {{ selectedSchemasCount() }} schema{{ selectedSchemasCount() === 1 ? '' : 's' }}.\n </p>\n </div>\n </div>\n }\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n [disabled]=\"!canCreateTemplate()\"\n (click)=\"onCreateTemplate()\">\n <i class=\"bi bi-plus-circle me-1\"></i>Create Template\n </button>\n</div>\n", styles: [".schema-card{cursor:pointer;transition:all .2s ease;border:2px solid transparent}.schema-card:hover{border-color:var(--bs-primary);box-shadow:0 2px 8px #0000001a}.schema-card.selected{border-color:var(--bs-primary);background-color:var(--bs-primary-bg-subtle)}.schema-title{font-size:.95em;color:var(--bs-body-color)}.schema-description{font-size:.8em;line-height:1.3}.schema-metadata{font-size:.75em}.schema-metadata .badge{font-size:.65em}.form-check-input{pointer-events:none}.modal-body{max-height:80vh;overflow-y:auto}.badge.fs-6{font-size:.875rem!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }] });
5400
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: TemplateSchemaSelectionModal, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }], target: i0.ɵɵFactoryTarget.Component });
5401
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: TemplateSchemaSelectionModal, isStandalone: true, selector: "ccv-template-schema-selection-modal", inputs: { availableSchemas: "availableSchemas", labGroups: "labGroups" }, outputs: { templateCreated: "templateCreated" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-file-earmark-plus me-2\"></i>Create Template from Schemas\n </h4>\n <button\n type=\"button\"\n class=\"btn-close\"\n aria-label=\"Close\"\n (click)=\"onCancel()\">\n </button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"templateForm\" class=\"needs-validation\" novalidate>\n <!-- Template Basic Information -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-info-circle me-1\"></i>Template Information\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-12 mb-3\">\n <div class=\"form-floating\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.is-invalid]=\"templateForm.get('name')?.invalid && templateForm.get('name')?.touched\"\n id=\"templateName\"\n formControlName=\"name\"\n placeholder=\"Enter template name\">\n <label for=\"templateName\">Template Name *</label>\n @if (templateForm.get('name')?.invalid && templateForm.get('name')?.touched) {\n <div class=\"invalid-feedback\">\n Template name is required.\n </div>\n }\n </div>\n </div>\n <div class=\"col-12 mb-3\">\n <div class=\"form-floating\">\n <textarea\n class=\"form-control\"\n id=\"templateDescription\"\n formControlName=\"description\"\n style=\"height: 80px\"\n placeholder=\"Enter template description\">\n </textarea>\n <label for=\"templateDescription\">Description</label>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Schema Selection -->\n <div class=\"card mb-4\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-check2-square me-1\"></i>Select Schemas\n </h6>\n <small class=\"text-muted\">{{ selectedSchemasCount() }} selected</small>\n </div>\n <div class=\"card-body\">\n @if (availableSchemas.length === 0) {\n <div class=\"text-center text-muted py-3\">\n <i class=\"bi bi-exclamation-triangle fs-4 d-block mb-2\"></i>\n No schemas available\n </div>\n } @else {\n <div class=\"row\">\n @for (schema of availableSchemas; track schema.id) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"card schema-card\"\n [class.selected]=\"isSchemaSelected(schema.id)\"\n (click)=\"toggleSchema(schema.id)\">\n <div class=\"card-body p-3\">\n <div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n [id]=\"'schema-' + schema.id\"\n [checked]=\"isSchemaSelected(schema.id)\"\n (change)=\"onSchemaCheckChange($event, schema.id)\">\n <label class=\"form-check-label w-100\" [for]=\"'schema-' + schema.id\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"flex-grow-1\">\n <strong class=\"schema-title\">{{ schema.displayName }}</strong>\n @if (schema.description) {\n <p class=\"schema-description text-muted small mb-1\">\n {{ schema.description }}\n </p>\n }\n <div class=\"schema-metadata\">\n @if (schema.tags && schema.tags.length > 0) {\n @for (tag of schema.tags; track tag) {\n <span class=\"badge bg-secondary me-1\">{{ tag }}</span>\n }\n }\n @if (schema.usage_count > 0) {\n <small class=\"text-muted\">\n <i class=\"bi bi-graph-up me-1\"></i>{{ schema.usage_count }} uses\n </small>\n }\n </div>\n </div>\n </div>\n </label>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n @if (selectedSchemasCount() === 0) {\n <div class=\"alert alert-warning mt-3\" role=\"alert\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n Please select at least one schema to create a template.\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Template Settings -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-gear me-1\"></i>Template Settings\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6 mb-3\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n <label for=\"labGroup\">Lab Group</label>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-check form-switch mb-3\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"isDefault\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"isDefault\">\n <i class=\"bi bi-star me-1\"></i>Default Template\n </label>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Preview Section -->\n @if (selectedSchemasCount() > 0) {\n <div class=\"card\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-eye me-1\"></i>Selected Schemas Preview\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"d-flex flex-wrap gap-2\">\n @for (schemaId of getSelectedSchemas(); track schemaId) {\n <span class=\"badge bg-primary fs-6 py-2 px-3\">\n {{ getSchemaDisplayName(schemaId) }}\n <button\n type=\"button\"\n class=\"btn-close btn-close-white ms-2\"\n style=\"font-size: 0.6em;\"\n (click)=\"removeSchema(schemaId)\"\n [attr.aria-label]=\"'Remove ' + getSchemaDisplayName(schemaId)\">\n </button>\n </span>\n }\n </div>\n <p class=\"text-muted small mt-2 mb-0\">\n This template will combine columns from {{ selectedSchemasCount() }} schema{{ selectedSchemasCount() === 1 ? '' : 's' }}.\n </p>\n </div>\n </div>\n }\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n [disabled]=\"!canCreateTemplate()\"\n (click)=\"onCreateTemplate()\">\n <i class=\"bi bi-plus-circle me-1\"></i>Create Template\n </button>\n</div>\n", styles: [".schema-card{cursor:pointer;transition:all .2s ease;border:2px solid transparent}.schema-card:hover{border-color:var(--bs-primary);box-shadow:0 2px 8px #0000001a}.schema-card.selected{border-color:var(--bs-primary);background-color:var(--bs-primary-bg-subtle)}.schema-title{font-size:.95em;color:var(--bs-body-color)}.schema-description{font-size:.8em;line-height:1.3}.schema-metadata{font-size:.75em}.schema-metadata .badge{font-size:.65em}.form-check-input{pointer-events:none}.modal-body{max-height:80vh;overflow-y:auto}.badge.fs-6{font-size:.875rem!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }] });
5404
5402
  }
5405
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: TemplateSchemaSelectionModal, decorators: [{
5403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: TemplateSchemaSelectionModal, decorators: [{
5406
5404
  type: Component,
5407
5405
  args: [{ selector: 'ccv-template-schema-selection-modal', standalone: true, imports: [CommonModule, ReactiveFormsModule, NgbModule], template: "<div class=\"modal-header\">\n <h4 class=\"modal-title\">\n <i class=\"bi bi-file-earmark-plus me-2\"></i>Create Template from Schemas\n </h4>\n <button\n type=\"button\"\n class=\"btn-close\"\n aria-label=\"Close\"\n (click)=\"onCancel()\">\n </button>\n</div>\n\n<div class=\"modal-body\">\n <form [formGroup]=\"templateForm\" class=\"needs-validation\" novalidate>\n <!-- Template Basic Information -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-info-circle me-1\"></i>Template Information\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-12 mb-3\">\n <div class=\"form-floating\">\n <input\n type=\"text\"\n class=\"form-control\"\n [class.is-invalid]=\"templateForm.get('name')?.invalid && templateForm.get('name')?.touched\"\n id=\"templateName\"\n formControlName=\"name\"\n placeholder=\"Enter template name\">\n <label for=\"templateName\">Template Name *</label>\n @if (templateForm.get('name')?.invalid && templateForm.get('name')?.touched) {\n <div class=\"invalid-feedback\">\n Template name is required.\n </div>\n }\n </div>\n </div>\n <div class=\"col-12 mb-3\">\n <div class=\"form-floating\">\n <textarea\n class=\"form-control\"\n id=\"templateDescription\"\n formControlName=\"description\"\n style=\"height: 80px\"\n placeholder=\"Enter template description\">\n </textarea>\n <label for=\"templateDescription\">Description</label>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Schema Selection -->\n <div class=\"card mb-4\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-check2-square me-1\"></i>Select Schemas\n </h6>\n <small class=\"text-muted\">{{ selectedSchemasCount() }} selected</small>\n </div>\n <div class=\"card-body\">\n @if (availableSchemas.length === 0) {\n <div class=\"text-center text-muted py-3\">\n <i class=\"bi bi-exclamation-triangle fs-4 d-block mb-2\"></i>\n No schemas available\n </div>\n } @else {\n <div class=\"row\">\n @for (schema of availableSchemas; track schema.id) {\n <div class=\"col-md-6 mb-2\">\n <div class=\"card schema-card\"\n [class.selected]=\"isSchemaSelected(schema.id)\"\n (click)=\"toggleSchema(schema.id)\">\n <div class=\"card-body p-3\">\n <div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n [id]=\"'schema-' + schema.id\"\n [checked]=\"isSchemaSelected(schema.id)\"\n (change)=\"onSchemaCheckChange($event, schema.id)\">\n <label class=\"form-check-label w-100\" [for]=\"'schema-' + schema.id\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"flex-grow-1\">\n <strong class=\"schema-title\">{{ schema.displayName }}</strong>\n @if (schema.description) {\n <p class=\"schema-description text-muted small mb-1\">\n {{ schema.description }}\n </p>\n }\n <div class=\"schema-metadata\">\n @if (schema.tags && schema.tags.length > 0) {\n @for (tag of schema.tags; track tag) {\n <span class=\"badge bg-secondary me-1\">{{ tag }}</span>\n }\n }\n @if (schema.usage_count > 0) {\n <small class=\"text-muted\">\n <i class=\"bi bi-graph-up me-1\"></i>{{ schema.usage_count }} uses\n </small>\n }\n </div>\n </div>\n </div>\n </label>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n\n @if (selectedSchemasCount() === 0) {\n <div class=\"alert alert-warning mt-3\" role=\"alert\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n Please select at least one schema to create a template.\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Template Settings -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-gear me-1\"></i>Template Settings\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6 mb-3\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroup\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n <label for=\"labGroup\">Lab Group</label>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"form-check form-switch mb-3\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"isDefault\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"isDefault\">\n <i class=\"bi bi-star me-1\"></i>Default Template\n </label>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Preview Section -->\n @if (selectedSchemasCount() > 0) {\n <div class=\"card\">\n <div class=\"card-header\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-eye me-1\"></i>Selected Schemas Preview\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"d-flex flex-wrap gap-2\">\n @for (schemaId of getSelectedSchemas(); track schemaId) {\n <span class=\"badge bg-primary fs-6 py-2 px-3\">\n {{ getSchemaDisplayName(schemaId) }}\n <button\n type=\"button\"\n class=\"btn-close btn-close-white ms-2\"\n style=\"font-size: 0.6em;\"\n (click)=\"removeSchema(schemaId)\"\n [attr.aria-label]=\"'Remove ' + getSchemaDisplayName(schemaId)\">\n </button>\n </span>\n }\n </div>\n <p class=\"text-muted small mt-2 mb-0\">\n This template will combine columns from {{ selectedSchemasCount() }} schema{{ selectedSchemasCount() === 1 ? '' : 's' }}.\n </p>\n </div>\n </div>\n }\n </form>\n</div>\n\n<div class=\"modal-footer\">\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"onCancel()\">\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n [disabled]=\"!canCreateTemplate()\"\n (click)=\"onCreateTemplate()\">\n <i class=\"bi bi-plus-circle me-1\"></i>Create Template\n </button>\n</div>\n", styles: [".schema-card{cursor:pointer;transition:all .2s ease;border:2px solid transparent}.schema-card:hover{border-color:var(--bs-primary);box-shadow:0 2px 8px #0000001a}.schema-card.selected{border-color:var(--bs-primary);background-color:var(--bs-primary-bg-subtle)}.schema-title{font-size:.95em;color:var(--bs-body-color)}.schema-description{font-size:.8em;line-height:1.3}.schema-metadata{font-size:.75em}.schema-metadata .badge{font-size:.65em}.form-check-input{pointer-events:none}.modal-body{max-height:80vh;overflow-y:auto}.badge.fs-6{font-size:.875rem!important}\n"] }]
5408
5406
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }], propDecorators: { availableSchemas: [{
@@ -5508,10 +5506,10 @@ class SchemaSelectionModal {
5508
5506
  };
5509
5507
  return categoryMap[category.toLowerCase()] || 'bg-secondary';
5510
5508
  }
5511
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SchemaSelectionModal, deps: [{ token: i2.NgbActiveModal }, { token: SchemaService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Component });
5512
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: SchemaSelectionModal, isStandalone: true, selector: "ccv-schema-selection-modal", ngImport: i0, template: "<!-- Schema Selection Modal -->\r\n<div class=\"modal-header\">\r\n <h4 class=\"modal-title\">\r\n <i class=\"bi bi-arrows-move me-2\"></i>{{ title }}\r\n </h4>\r\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\" (click)=\"onCancel()\"></button>\r\n</div>\r\n\r\n<div class=\"modal-body\">\r\n <!-- Description -->\r\n <div class=\"alert alert-info mb-4\">\r\n <div class=\"d-flex align-items-start\">\r\n <i class=\"bi bi-info-circle-fill me-2 mt-1\"></i>\r\n <div>\r\n <small>{{ description }}</small>\r\n <br>\r\n <small class=\"text-muted\">\r\n Leave empty to use all available schemas or select specific ones for custom ordering.\r\n </small>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Search and Actions -->\r\n <div class=\"row mb-3\">\r\n <div class=\"col-md-8\">\r\n <div class=\"input-group\">\r\n <span class=\"input-group-text\">\r\n <i class=\"bi bi-search\"></i>\r\n </span>\r\n <input\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"Search schemas...\"\r\n [(ngModel)]=\"searchTerm\"\r\n [ngModelOptions]=\"{updateOn: 'change'}\"\r\n >\r\n </div>\r\n </div>\r\n <div class=\"col-md-4\">\r\n <div class=\"btn-group w-100\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-outline-primary btn-sm\"\r\n (click)=\"selectAll()\"\r\n [disabled]=\"filteredSchemas.length === 0\"\r\n title=\"Select All Visible\">\r\n <i class=\"bi bi-check-all\"></i> All\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-outline-secondary btn-sm\"\r\n (click)=\"clearSelection()\"\r\n [disabled]=\"selectedSchemaIds().size === 0\"\r\n title=\"Clear Selection\">\r\n <i class=\"bi bi-x-circle\"></i> Clear\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Selection Summary -->\r\n @if (selectedSchemaIds().size > 0) {\r\n <div class=\"alert alert-success mb-3\">\r\n <i class=\"bi bi-check-circle me-2\"></i>\r\n <strong>{{ selectedSchemaIds().size }}</strong> schema{{ selectedSchemaIds().size !== 1 ? 's' : '' }} selected\r\n </div>\r\n }\r\n\r\n <!-- Loading State -->\r\n @if (isLoading()) {\r\n <div class=\"text-center py-4\">\r\n <div class=\"spinner-border text-primary\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading schemas...</span>\r\n </div>\r\n <p class=\"text-muted mt-2\">Loading available schemas...</p>\r\n </div>\r\n } @else {\r\n <!-- Schema List -->\r\n <div class=\"schema-list\" style=\"max-height: 400px; overflow-y: auto;\">\r\n @if (filteredSchemas.length === 0) {\r\n <div class=\"text-center py-4 text-muted\">\r\n <i class=\"bi bi-search fs-1 mb-3 opacity-50\"></i>\r\n <h6>No schemas found</h6>\r\n <p class=\"small mb-0\">\r\n @if (searchTerm()) {\r\n Try adjusting your search terms\r\n } @else {\r\n No schemas are currently available\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n @for (schema of filteredSchemas; track schema.id) {\r\n <div\r\n class=\"schema-item border rounded p-3 mb-2\"\r\n [class.selected]=\"isSchemaSelected(schema.id)\"\r\n (click)=\"toggleSchemaSelection(schema.id)\"\r\n role=\"button\">\r\n\r\n <div class=\"d-flex align-items-start\">\r\n <!-- Checkbox -->\r\n <div class=\"form-check me-3\">\r\n <input\r\n class=\"form-check-input\"\r\n type=\"checkbox\"\r\n [checked]=\"isSchemaSelected(schema.id)\"\r\n [id]=\"'schema-' + schema.id\"\r\n (click)=\"$event.stopPropagation()\">\r\n <label class=\"form-check-label visually-hidden\" [for]=\"'schema-' + schema.id\">\r\n Select {{ schema.name }}\r\n </label>\r\n </div>\r\n\r\n <!-- Schema Info -->\r\n <div class=\"flex-grow-1\">\r\n <div class=\"d-flex justify-content-between align-items-start mb-2\">\r\n <h6 class=\"mb-0 text-truncate me-2\">\r\n {{ schema.name }}\r\n @if (schema.version) {\r\n <small class=\"text-muted\">v{{ schema.version }}</small>\r\n }\r\n </h6>\r\n\r\n <div class=\"flex-shrink-0\">\r\n @if (schema.category) {\r\n <span class=\"badge\" [class]=\"getBadgeClass(schema.category)\">\r\n {{ schema.category }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (schema.description) {\r\n <p class=\"text-muted small mb-1\">{{ schema.description }}</p>\r\n }\r\n\r\n <div class=\"d-flex align-items-center justify-content-between\">\r\n @if (schema.columnsCount !== undefined) {\r\n <small class=\"text-muted\">\r\n <i class=\"bi bi-columns me-1\"></i>{{ schema.columnsCount }} columns\r\n </small>\r\n }\r\n\r\n @if (schema.url) {\r\n <a\r\n [href]=\"schema.url\"\r\n target=\"_blank\"\r\n class=\"btn btn-sm btn-outline-secondary\"\r\n title=\"View Schema Documentation\"\r\n (click)=\"$event.stopPropagation()\">\r\n <i class=\"bi bi-box-arrow-up-right\"></i>\r\n </a>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\r\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-primary\"\r\n (click)=\"onConfirm()\"\r\n [disabled]=\"isLoading()\">\r\n <i class=\"bi bi-arrows-move me-1\"></i>\r\n @if (selectedSchemaIds().size > 0) {\r\n Reorder with {{ selectedSchemaIds().size }} Schema{{ selectedSchemaIds().size !== 1 ? 's' : '' }}\r\n } @else {\r\n Reorder with All Schemas\r\n }\r\n </button>\r\n</div>", styles: [".schema-list{scrollbar-width:thin;scrollbar-color:rgba(var(--bs-secondary-rgb),.5) transparent}.schema-list .schema-item{cursor:pointer;transition:all .2s ease}.schema-list .schema-item:hover{background-color:rgba(var(--bs-primary-rgb),.05);border-color:var(--bs-primary)!important}.schema-list .schema-item.selected{background-color:rgba(var(--bs-success-rgb),.1);border-color:var(--bs-success)!important}.schema-list .schema-item.selected .form-check-input{background-color:var(--bs-success);border-color:var(--bs-success)}.schema-list .schema-item .form-check-input{cursor:pointer}.schema-list::-webkit-scrollbar{width:4px}.schema-list::-webkit-scrollbar-track{background:transparent}.schema-list::-webkit-scrollbar-thumb{background-color:rgba(var(--bs-secondary-rgb),.5);border-radius:2px}.schema-list::-webkit-scrollbar-thumb:hover{background-color:rgba(var(--bs-secondary-rgb),.7)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NgbModule }] });
5509
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SchemaSelectionModal, deps: [{ token: i2.NgbActiveModal }, { token: SchemaService }, { token: i3.ToastService }], target: i0.ɵɵFactoryTarget.Component });
5510
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: SchemaSelectionModal, isStandalone: true, selector: "ccv-schema-selection-modal", ngImport: i0, template: "<!-- Schema Selection Modal -->\r\n<div class=\"modal-header\">\r\n <h4 class=\"modal-title\">\r\n <i class=\"bi bi-arrows-move me-2\"></i>{{ title }}\r\n </h4>\r\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\" (click)=\"onCancel()\"></button>\r\n</div>\r\n\r\n<div class=\"modal-body\">\r\n <!-- Description -->\r\n <div class=\"alert alert-info mb-4\">\r\n <div class=\"d-flex align-items-start\">\r\n <i class=\"bi bi-info-circle-fill me-2 mt-1\"></i>\r\n <div>\r\n <small>{{ description }}</small>\r\n <br>\r\n <small class=\"text-muted\">\r\n Leave empty to use all available schemas or select specific ones for custom ordering.\r\n </small>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Search and Actions -->\r\n <div class=\"row mb-3\">\r\n <div class=\"col-md-8\">\r\n <div class=\"input-group\">\r\n <span class=\"input-group-text\">\r\n <i class=\"bi bi-search\"></i>\r\n </span>\r\n <input\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"Search schemas...\"\r\n [(ngModel)]=\"searchTerm\"\r\n [ngModelOptions]=\"{updateOn: 'change'}\"\r\n >\r\n </div>\r\n </div>\r\n <div class=\"col-md-4\">\r\n <div class=\"btn-group w-100\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-outline-primary btn-sm\"\r\n (click)=\"selectAll()\"\r\n [disabled]=\"filteredSchemas.length === 0\"\r\n title=\"Select All Visible\">\r\n <i class=\"bi bi-check-all\"></i> All\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-outline-secondary btn-sm\"\r\n (click)=\"clearSelection()\"\r\n [disabled]=\"selectedSchemaIds().size === 0\"\r\n title=\"Clear Selection\">\r\n <i class=\"bi bi-x-circle\"></i> Clear\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Selection Summary -->\r\n @if (selectedSchemaIds().size > 0) {\r\n <div class=\"alert alert-success mb-3\">\r\n <i class=\"bi bi-check-circle me-2\"></i>\r\n <strong>{{ selectedSchemaIds().size }}</strong> schema{{ selectedSchemaIds().size !== 1 ? 's' : '' }} selected\r\n </div>\r\n }\r\n\r\n <!-- Loading State -->\r\n @if (isLoading()) {\r\n <div class=\"text-center py-4\">\r\n <div class=\"spinner-border text-primary\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading schemas...</span>\r\n </div>\r\n <p class=\"text-muted mt-2\">Loading available schemas...</p>\r\n </div>\r\n } @else {\r\n <!-- Schema List -->\r\n <div class=\"schema-list\" style=\"max-height: 400px; overflow-y: auto;\">\r\n @if (filteredSchemas.length === 0) {\r\n <div class=\"text-center py-4 text-muted\">\r\n <i class=\"bi bi-search fs-1 mb-3 opacity-50\"></i>\r\n <h6>No schemas found</h6>\r\n <p class=\"small mb-0\">\r\n @if (searchTerm()) {\r\n Try adjusting your search terms\r\n } @else {\r\n No schemas are currently available\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n @for (schema of filteredSchemas; track schema.id) {\r\n <div\r\n class=\"schema-item border rounded p-3 mb-2\"\r\n [class.selected]=\"isSchemaSelected(schema.id)\"\r\n (click)=\"toggleSchemaSelection(schema.id)\"\r\n role=\"button\">\r\n\r\n <div class=\"d-flex align-items-start\">\r\n <!-- Checkbox -->\r\n <div class=\"form-check me-3\">\r\n <input\r\n class=\"form-check-input\"\r\n type=\"checkbox\"\r\n [checked]=\"isSchemaSelected(schema.id)\"\r\n [id]=\"'schema-' + schema.id\"\r\n (click)=\"$event.stopPropagation()\">\r\n <label class=\"form-check-label visually-hidden\" [for]=\"'schema-' + schema.id\">\r\n Select {{ schema.name }}\r\n </label>\r\n </div>\r\n\r\n <!-- Schema Info -->\r\n <div class=\"flex-grow-1\">\r\n <div class=\"d-flex justify-content-between align-items-start mb-2\">\r\n <h6 class=\"mb-0 text-truncate me-2\">\r\n {{ schema.name }}\r\n @if (schema.version) {\r\n <small class=\"text-muted\">v{{ schema.version }}</small>\r\n }\r\n </h6>\r\n\r\n <div class=\"flex-shrink-0\">\r\n @if (schema.category) {\r\n <span class=\"badge\" [class]=\"getBadgeClass(schema.category)\">\r\n {{ schema.category }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (schema.description) {\r\n <p class=\"text-muted small mb-1\">{{ schema.description }}</p>\r\n }\r\n\r\n <div class=\"d-flex align-items-center justify-content-between\">\r\n @if (schema.columnsCount !== undefined) {\r\n <small class=\"text-muted\">\r\n <i class=\"bi bi-columns me-1\"></i>{{ schema.columnsCount }} columns\r\n </small>\r\n }\r\n\r\n @if (schema.url) {\r\n <a\r\n [href]=\"schema.url\"\r\n target=\"_blank\"\r\n class=\"btn btn-sm btn-outline-secondary\"\r\n title=\"View Schema Documentation\"\r\n (click)=\"$event.stopPropagation()\">\r\n <i class=\"bi bi-box-arrow-up-right\"></i>\r\n </a>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\r\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-primary\"\r\n (click)=\"onConfirm()\"\r\n [disabled]=\"isLoading()\">\r\n <i class=\"bi bi-arrows-move me-1\"></i>\r\n @if (selectedSchemaIds().size > 0) {\r\n Reorder with {{ selectedSchemaIds().size }} Schema{{ selectedSchemaIds().size !== 1 ? 's' : '' }}\r\n } @else {\r\n Reorder with All Schemas\r\n }\r\n </button>\r\n</div>", styles: [".schema-list{scrollbar-width:thin;scrollbar-color:rgba(var(--bs-secondary-rgb),.5) transparent}.schema-list .schema-item{cursor:pointer;transition:all .2s ease}.schema-list .schema-item:hover{background-color:rgba(var(--bs-primary-rgb),.05);border-color:var(--bs-primary)!important}.schema-list .schema-item.selected{background-color:rgba(var(--bs-success-rgb),.1);border-color:var(--bs-success)!important}.schema-list .schema-item.selected .form-check-input{background-color:var(--bs-success);border-color:var(--bs-success)}.schema-list .schema-item .form-check-input{cursor:pointer}.schema-list::-webkit-scrollbar{width:4px}.schema-list::-webkit-scrollbar-track{background:transparent}.schema-list::-webkit-scrollbar-thumb{background-color:rgba(var(--bs-secondary-rgb),.5);border-radius:2px}.schema-list::-webkit-scrollbar-thumb:hover{background-color:rgba(var(--bs-secondary-rgb),.7)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NgbModule }] });
5513
5511
  }
5514
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: SchemaSelectionModal, decorators: [{
5512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: SchemaSelectionModal, decorators: [{
5515
5513
  type: Component,
5516
5514
  args: [{ selector: 'ccv-schema-selection-modal', standalone: true, imports: [CommonModule, FormsModule, NgbModule], template: "<!-- Schema Selection Modal -->\r\n<div class=\"modal-header\">\r\n <h4 class=\"modal-title\">\r\n <i class=\"bi bi-arrows-move me-2\"></i>{{ title }}\r\n </h4>\r\n <button type=\"button\" class=\"btn-close\" aria-label=\"Close\" (click)=\"onCancel()\"></button>\r\n</div>\r\n\r\n<div class=\"modal-body\">\r\n <!-- Description -->\r\n <div class=\"alert alert-info mb-4\">\r\n <div class=\"d-flex align-items-start\">\r\n <i class=\"bi bi-info-circle-fill me-2 mt-1\"></i>\r\n <div>\r\n <small>{{ description }}</small>\r\n <br>\r\n <small class=\"text-muted\">\r\n Leave empty to use all available schemas or select specific ones for custom ordering.\r\n </small>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Search and Actions -->\r\n <div class=\"row mb-3\">\r\n <div class=\"col-md-8\">\r\n <div class=\"input-group\">\r\n <span class=\"input-group-text\">\r\n <i class=\"bi bi-search\"></i>\r\n </span>\r\n <input\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"Search schemas...\"\r\n [(ngModel)]=\"searchTerm\"\r\n [ngModelOptions]=\"{updateOn: 'change'}\"\r\n >\r\n </div>\r\n </div>\r\n <div class=\"col-md-4\">\r\n <div class=\"btn-group w-100\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-outline-primary btn-sm\"\r\n (click)=\"selectAll()\"\r\n [disabled]=\"filteredSchemas.length === 0\"\r\n title=\"Select All Visible\">\r\n <i class=\"bi bi-check-all\"></i> All\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-outline-secondary btn-sm\"\r\n (click)=\"clearSelection()\"\r\n [disabled]=\"selectedSchemaIds().size === 0\"\r\n title=\"Clear Selection\">\r\n <i class=\"bi bi-x-circle\"></i> Clear\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Selection Summary -->\r\n @if (selectedSchemaIds().size > 0) {\r\n <div class=\"alert alert-success mb-3\">\r\n <i class=\"bi bi-check-circle me-2\"></i>\r\n <strong>{{ selectedSchemaIds().size }}</strong> schema{{ selectedSchemaIds().size !== 1 ? 's' : '' }} selected\r\n </div>\r\n }\r\n\r\n <!-- Loading State -->\r\n @if (isLoading()) {\r\n <div class=\"text-center py-4\">\r\n <div class=\"spinner-border text-primary\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading schemas...</span>\r\n </div>\r\n <p class=\"text-muted mt-2\">Loading available schemas...</p>\r\n </div>\r\n } @else {\r\n <!-- Schema List -->\r\n <div class=\"schema-list\" style=\"max-height: 400px; overflow-y: auto;\">\r\n @if (filteredSchemas.length === 0) {\r\n <div class=\"text-center py-4 text-muted\">\r\n <i class=\"bi bi-search fs-1 mb-3 opacity-50\"></i>\r\n <h6>No schemas found</h6>\r\n <p class=\"small mb-0\">\r\n @if (searchTerm()) {\r\n Try adjusting your search terms\r\n } @else {\r\n No schemas are currently available\r\n }\r\n </p>\r\n </div>\r\n } @else {\r\n @for (schema of filteredSchemas; track schema.id) {\r\n <div\r\n class=\"schema-item border rounded p-3 mb-2\"\r\n [class.selected]=\"isSchemaSelected(schema.id)\"\r\n (click)=\"toggleSchemaSelection(schema.id)\"\r\n role=\"button\">\r\n\r\n <div class=\"d-flex align-items-start\">\r\n <!-- Checkbox -->\r\n <div class=\"form-check me-3\">\r\n <input\r\n class=\"form-check-input\"\r\n type=\"checkbox\"\r\n [checked]=\"isSchemaSelected(schema.id)\"\r\n [id]=\"'schema-' + schema.id\"\r\n (click)=\"$event.stopPropagation()\">\r\n <label class=\"form-check-label visually-hidden\" [for]=\"'schema-' + schema.id\">\r\n Select {{ schema.name }}\r\n </label>\r\n </div>\r\n\r\n <!-- Schema Info -->\r\n <div class=\"flex-grow-1\">\r\n <div class=\"d-flex justify-content-between align-items-start mb-2\">\r\n <h6 class=\"mb-0 text-truncate me-2\">\r\n {{ schema.name }}\r\n @if (schema.version) {\r\n <small class=\"text-muted\">v{{ schema.version }}</small>\r\n }\r\n </h6>\r\n\r\n <div class=\"flex-shrink-0\">\r\n @if (schema.category) {\r\n <span class=\"badge\" [class]=\"getBadgeClass(schema.category)\">\r\n {{ schema.category }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (schema.description) {\r\n <p class=\"text-muted small mb-1\">{{ schema.description }}</p>\r\n }\r\n\r\n <div class=\"d-flex align-items-center justify-content-between\">\r\n @if (schema.columnsCount !== undefined) {\r\n <small class=\"text-muted\">\r\n <i class=\"bi bi-columns me-1\"></i>{{ schema.columnsCount }} columns\r\n </small>\r\n }\r\n\r\n @if (schema.url) {\r\n <a\r\n [href]=\"schema.url\"\r\n target=\"_blank\"\r\n class=\"btn btn-sm btn-outline-secondary\"\r\n title=\"View Schema Documentation\"\r\n (click)=\"$event.stopPropagation()\">\r\n <i class=\"bi bi-box-arrow-up-right\"></i>\r\n </a>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">\r\n <i class=\"bi bi-x-circle me-1\"></i>Cancel\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-primary\"\r\n (click)=\"onConfirm()\"\r\n [disabled]=\"isLoading()\">\r\n <i class=\"bi bi-arrows-move me-1\"></i>\r\n @if (selectedSchemaIds().size > 0) {\r\n Reorder with {{ selectedSchemaIds().size }} Schema{{ selectedSchemaIds().size !== 1 ? 's' : '' }}\r\n } @else {\r\n Reorder with All Schemas\r\n }\r\n </button>\r\n</div>", styles: [".schema-list{scrollbar-width:thin;scrollbar-color:rgba(var(--bs-secondary-rgb),.5) transparent}.schema-list .schema-item{cursor:pointer;transition:all .2s ease}.schema-list .schema-item:hover{background-color:rgba(var(--bs-primary-rgb),.05);border-color:var(--bs-primary)!important}.schema-list .schema-item.selected{background-color:rgba(var(--bs-success-rgb),.1);border-color:var(--bs-success)!important}.schema-list .schema-item.selected .form-check-input{background-color:var(--bs-success);border-color:var(--bs-success)}.schema-list .schema-item .form-check-input{cursor:pointer}.schema-list::-webkit-scrollbar{width:4px}.schema-list::-webkit-scrollbar-track{background:transparent}.schema-list::-webkit-scrollbar-thumb{background-color:rgba(var(--bs-secondary-rgb),.5);border-radius:2px}.schema-list::-webkit-scrollbar-thumb:hover{background-color:rgba(var(--bs-secondary-rgb),.7)}\n"] }]
5517
5515
  }], ctorParameters: () => [{ type: i2.NgbActiveModal }, { type: SchemaService }, { type: i3.ToastService }] });
@@ -5596,10 +5594,10 @@ class TableCreationModalComponent {
5596
5594
  };
5597
5595
  return labels[fieldName] || fieldName;
5598
5596
  }
5599
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: TableCreationModalComponent, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }], target: i0.ɵɵFactoryTarget.Component });
5600
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: TableCreationModalComponent, isStandalone: true, selector: "ccv-table-creation-modal", inputs: { template: "template", labGroups: "labGroups", selectedLabGroupId: "selectedLabGroupId" }, outputs: { tableCreated: "tableCreated" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"bi bi-table me-2\"></i>Create Table from Template\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"onClose()\" aria-label=\"Close\"></button>\n</div>\n\n<div class=\"modal-body\">\n @if (template) {\n <div class=\"alert alert-info d-flex align-items-center mb-4\">\n <i class=\"bi bi-info-circle me-2\"></i>\n <div>\n <strong>Template:</strong> {{ template.name }}\n @if (template.description) {\n <br><small>{{ template.description }}</small>\n }\n </div>\n </div>\n\n @if (errorMessage) {\n <div class=\"alert alert-danger d-flex align-items-center\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n {{ errorMessage }}\n </div>\n }\n\n <form [formGroup]=\"tableForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row g-3\">\n <!-- Table Name -->\n <div class=\"col-12\">\n <div class=\"form-floating\">\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"tableName\"\n formControlName=\"name\"\n placeholder=\"Table name\"\n [class.is-invalid]=\"getFieldError('name')\">\n <label for=\"tableName\">Table Name *</label>\n </div>\n @if (getFieldError('name')) {\n <div class=\"invalid-feedback d-block\">\n {{ getFieldError('name') }}\n </div>\n }\n </div>\n\n <!-- Sample Count -->\n <div class=\"col-md-6\">\n <div class=\"form-floating\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"sampleCount\"\n formControlName=\"sampleCount\"\n placeholder=\"Sample count\"\n min=\"1\"\n max=\"10000\"\n [class.is-invalid]=\"getFieldError('sampleCount')\">\n <label for=\"sampleCount\">Number of Samples *</label>\n </div>\n @if (getFieldError('sampleCount')) {\n <div class=\"invalid-feedback d-block\">\n {{ getFieldError('sampleCount') }}\n </div>\n }\n <div class=\"form-text\">\n Number of sample rows to create in the table (1-10,000)\n </div>\n </div>\n\n <!-- Lab Group -->\n <div class=\"col-md-6\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroupSelect\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n <label for=\"labGroupSelect\">Lab Group</label>\n </div>\n <div class=\"form-text\">\n Optional: Assign table to a specific lab group\n </div>\n </div>\n\n <!-- Description -->\n <div class=\"col-12\">\n <div class=\"form-floating\">\n <textarea\n class=\"form-control\"\n id=\"tableDescription\"\n formControlName=\"description\"\n placeholder=\"Description\"\n style=\"height: 100px\"></textarea>\n <label for=\"tableDescription\">Description (optional)</label>\n </div>\n <div class=\"form-text\">\n Optional description for the metadata table\n </div>\n </div>\n </div>\n </form>\n }\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onClose()\" [disabled]=\"isCreating\">\n Cancel\n </button>\n <button\n type=\"submit\"\n class=\"btn btn-success\"\n (click)=\"onSubmit()\"\n [disabled]=\"tableForm.invalid || isCreating\">\n @if (isCreating) {\n <span class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></span>\n }\n <i class=\"bi bi-table me-1\"></i>Create Table\n </button>\n</div>\n", styles: [".form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px)}.form-text{font-size:.8rem;margin-top:.25rem}.alert{border-radius:.5rem}.modal-header{border-bottom:1px solid var(--bs-border-color)}.modal-footer{border-top:1px solid var(--bs-border-color)}.spinner-border-sm{width:1rem;height:1rem}.invalid-feedback{font-size:.875rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
5597
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: TableCreationModalComponent, deps: [{ token: i1.FormBuilder }, { token: i2.NgbActiveModal }], target: i0.ɵɵFactoryTarget.Component });
5598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: TableCreationModalComponent, isStandalone: true, selector: "ccv-table-creation-modal", inputs: { template: "template", labGroups: "labGroups", selectedLabGroupId: "selectedLabGroupId" }, outputs: { tableCreated: "tableCreated" }, ngImport: i0, template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"bi bi-table me-2\"></i>Create Table from Template\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"onClose()\" aria-label=\"Close\"></button>\n</div>\n\n<div class=\"modal-body\">\n @if (template) {\n <div class=\"alert alert-info d-flex align-items-center mb-4\">\n <i class=\"bi bi-info-circle me-2\"></i>\n <div>\n <strong>Template:</strong> {{ template.name }}\n @if (template.description) {\n <br><small>{{ template.description }}</small>\n }\n </div>\n </div>\n\n @if (errorMessage) {\n <div class=\"alert alert-danger d-flex align-items-center\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n {{ errorMessage }}\n </div>\n }\n\n <form [formGroup]=\"tableForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row g-3\">\n <!-- Table Name -->\n <div class=\"col-12\">\n <div class=\"form-floating\">\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"tableName\"\n formControlName=\"name\"\n placeholder=\"Table name\"\n [class.is-invalid]=\"getFieldError('name')\">\n <label for=\"tableName\">Table Name *</label>\n </div>\n @if (getFieldError('name')) {\n <div class=\"invalid-feedback d-block\">\n {{ getFieldError('name') }}\n </div>\n }\n </div>\n\n <!-- Sample Count -->\n <div class=\"col-md-6\">\n <div class=\"form-floating\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"sampleCount\"\n formControlName=\"sampleCount\"\n placeholder=\"Sample count\"\n min=\"1\"\n max=\"10000\"\n [class.is-invalid]=\"getFieldError('sampleCount')\">\n <label for=\"sampleCount\">Number of Samples *</label>\n </div>\n @if (getFieldError('sampleCount')) {\n <div class=\"invalid-feedback d-block\">\n {{ getFieldError('sampleCount') }}\n </div>\n }\n <div class=\"form-text\">\n Number of sample rows to create in the table (1-10,000)\n </div>\n </div>\n\n <!-- Lab Group -->\n <div class=\"col-md-6\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroupSelect\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n <label for=\"labGroupSelect\">Lab Group</label>\n </div>\n <div class=\"form-text\">\n Optional: Assign table to a specific lab group\n </div>\n </div>\n\n <!-- Description -->\n <div class=\"col-12\">\n <div class=\"form-floating\">\n <textarea\n class=\"form-control\"\n id=\"tableDescription\"\n formControlName=\"description\"\n placeholder=\"Description\"\n style=\"height: 100px\"></textarea>\n <label for=\"tableDescription\">Description (optional)</label>\n </div>\n <div class=\"form-text\">\n Optional description for the metadata table\n </div>\n </div>\n </div>\n </form>\n }\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onClose()\" [disabled]=\"isCreating\">\n Cancel\n </button>\n <button\n type=\"submit\"\n class=\"btn btn-success\"\n (click)=\"onSubmit()\"\n [disabled]=\"tableForm.invalid || isCreating\">\n @if (isCreating) {\n <span class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></span>\n }\n <i class=\"bi bi-table me-1\"></i>Create Table\n </button>\n</div>\n", styles: [".form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px)}.form-text{font-size:.8rem;margin-top:.25rem}.alert{border-radius:.5rem}.modal-header{border-bottom:1px solid var(--bs-border-color)}.modal-footer{border-top:1px solid var(--bs-border-color)}.spinner-border-sm{width:1rem;height:1rem}.invalid-feedback{font-size:.875rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
5601
5599
  }
5602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: TableCreationModalComponent, decorators: [{
5600
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: TableCreationModalComponent, decorators: [{
5603
5601
  type: Component,
5604
5602
  args: [{ selector: 'ccv-table-creation-modal', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\">\n <i class=\"bi bi-table me-2\"></i>Create Table from Template\n </h5>\n <button type=\"button\" class=\"btn-close\" (click)=\"onClose()\" aria-label=\"Close\"></button>\n</div>\n\n<div class=\"modal-body\">\n @if (template) {\n <div class=\"alert alert-info d-flex align-items-center mb-4\">\n <i class=\"bi bi-info-circle me-2\"></i>\n <div>\n <strong>Template:</strong> {{ template.name }}\n @if (template.description) {\n <br><small>{{ template.description }}</small>\n }\n </div>\n </div>\n\n @if (errorMessage) {\n <div class=\"alert alert-danger d-flex align-items-center\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n {{ errorMessage }}\n </div>\n }\n\n <form [formGroup]=\"tableForm\" (ngSubmit)=\"onSubmit()\">\n <div class=\"row g-3\">\n <!-- Table Name -->\n <div class=\"col-12\">\n <div class=\"form-floating\">\n <input\n type=\"text\"\n class=\"form-control\"\n id=\"tableName\"\n formControlName=\"name\"\n placeholder=\"Table name\"\n [class.is-invalid]=\"getFieldError('name')\">\n <label for=\"tableName\">Table Name *</label>\n </div>\n @if (getFieldError('name')) {\n <div class=\"invalid-feedback d-block\">\n {{ getFieldError('name') }}\n </div>\n }\n </div>\n\n <!-- Sample Count -->\n <div class=\"col-md-6\">\n <div class=\"form-floating\">\n <input\n type=\"number\"\n class=\"form-control\"\n id=\"sampleCount\"\n formControlName=\"sampleCount\"\n placeholder=\"Sample count\"\n min=\"1\"\n max=\"10000\"\n [class.is-invalid]=\"getFieldError('sampleCount')\">\n <label for=\"sampleCount\">Number of Samples *</label>\n </div>\n @if (getFieldError('sampleCount')) {\n <div class=\"invalid-feedback d-block\">\n {{ getFieldError('sampleCount') }}\n </div>\n }\n <div class=\"form-text\">\n Number of sample rows to create in the table (1-10,000)\n </div>\n </div>\n\n <!-- Lab Group -->\n <div class=\"col-md-6\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroupSelect\"\n formControlName=\"labGroupId\">\n <option [ngValue]=\"null\">No Lab Group</option>\n @for (group of labGroups; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n </select>\n <label for=\"labGroupSelect\">Lab Group</label>\n </div>\n <div class=\"form-text\">\n Optional: Assign table to a specific lab group\n </div>\n </div>\n\n <!-- Description -->\n <div class=\"col-12\">\n <div class=\"form-floating\">\n <textarea\n class=\"form-control\"\n id=\"tableDescription\"\n formControlName=\"description\"\n placeholder=\"Description\"\n style=\"height: 100px\"></textarea>\n <label for=\"tableDescription\">Description (optional)</label>\n </div>\n <div class=\"form-text\">\n Optional description for the metadata table\n </div>\n </div>\n </div>\n </form>\n }\n</div>\n\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onClose()\" [disabled]=\"isCreating\">\n Cancel\n </button>\n <button\n type=\"submit\"\n class=\"btn btn-success\"\n (click)=\"onSubmit()\"\n [disabled]=\"tableForm.invalid || isCreating\">\n @if (isCreating) {\n <span class=\"spinner-border spinner-border-sm me-2\" role=\"status\"></span>\n }\n <i class=\"bi bi-table me-1\"></i>Create Table\n </button>\n</div>\n", styles: [".form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px)}.form-text{font-size:.8rem;margin-top:.25rem}.alert{border-radius:.5rem}.modal-header{border-bottom:1px solid var(--bs-border-color)}.modal-footer{border-top:1px solid var(--bs-border-color)}.spinner-border-sm{width:1rem;height:1rem}.invalid-feedback{font-size:.875rem}\n"] }]
5605
5603
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.NgbActiveModal }], propDecorators: { template: [{
@@ -6024,23 +6022,23 @@ class MetadataTableTemplates {
6024
6022
  }
6025
6023
  });
6026
6024
  }
6027
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplates, deps: [{ token: i1.FormBuilder }, { token: MetadataTableTemplateService }, { token: i3.LabGroupService }, { token: i2.NgbModal }, { token: i3.ToastService }, { token: AsyncTaskUIService }], target: i0.ɵɵFactoryTarget.Component });
6028
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.0", type: MetadataTableTemplates, isStandalone: true, selector: "ccv-metadata-table-templates", ngImport: i0, template: "<div class=\"metadata-table-templates-container\">\n <!-- Header Section -->\n <nav class=\"navbar navbar-expand-lg bg-body-tertiary shadow-sm\">\n <div class=\"container-fluid\">\n <div class=\"d-flex align-items-center justify-content-between w-100\">\n <div class=\"d-flex align-items-center\">\n <h4 class=\"navbar-brand m-0 fw-bold\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Table Templates\n </h4>\n @if (selectedLabGroup()) {\n <span class=\"badge bg-primary ms-3\">{{ selectedLabGroup()!.name }}</span>\n }\n </div>\n <div class=\"d-flex gap-2\">\n <div class=\"btn-group\" ngbDropdown>\n <button type=\"button\" class=\"btn btn-primary btn-sm dropdown-toggle\" ngbDropdownToggle>\n <i class=\"bi bi-plus me-1\"></i>New Template\n </button>\n <div class=\"dropdown-menu dropdown-menu-end\" ngbDropdownMenu>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"createTemplateWithCustomSchemas(); $event.preventDefault()\"\n title=\"Select one or multiple schemas to create template\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>\n <strong>Create from Schemas</strong>\n </a>\n <div class=\"dropdown-divider\"></div>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"createTemplate(); $event.preventDefault()\"\n title=\"Create from scratch\">\n <i class=\"bi bi-wrench me-2\"></i>\n <strong>Manual Configuration</strong>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </nav>\n\n <div class=\"templates-content p-4\">\n <!-- Search and Filters Section -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h5 class=\"mb-0\">\n <i class=\"bi bi-search me-2\"></i>Search & Filters\n </h5>\n </div>\n <div class=\"card-body\">\n <form [formGroup]=\"searchForm\" class=\"row g-3\">\n <div class=\"col-md-3\">\n <div class=\"form-floating\">\n <input\n type=\"search\"\n class=\"form-control\"\n id=\"templateSearch\"\n formControlName=\"search\"\n placeholder=\"Search templates...\">\n <label for=\"templateSearch\">\n <i class=\"bi bi-search me-1\"></i>Search Templates\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroupSelect\"\n formControlName=\"labGroupId\"\n (change)=\"onLabGroupChange($event)\">\n <option [ngValue]=\"null\">All Lab Groups</option>\n @if (hasLabGroups()) {\n @for (group of labGroupsData().results; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n }\n </select>\n <label for=\"labGroupSelect\">Lab Group</label>\n </div>\n\n <!-- Lab Groups Pagination -->\n @if (showLabGroupsPagination()) {\n <div class=\"d-flex justify-content-center mt-2\">\n <ngb-pagination\n [collectionSize]=\"labGroupsData().count\"\n [page]=\"labGroupsCurrentPage()\"\n [pageSize]=\"labGroupsPageSize()\"\n [maxSize]=\"3\"\n [boundaryLinks]=\"false\"\n [rotate]=\"true\"\n (pageChange)=\"onLabGroupsPageChange($event)\"\n class=\"pagination-sm\">\n </ngb-pagination>\n </div>\n }\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch mt-3\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"defaultTemplates\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"defaultTemplates\">\n <i class=\"bi bi-star me-1\"></i>Default Templates Only\n </label>\n </div>\n </div>\n </form>\n </div>\n </div>\n\n <!-- Templates Table -->\n <div class=\"card\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Table Templates\n <span class=\"badge bg-primary ms-2\">{{ templatesData().count }}</span>\n </h5>\n @if (isLoading()) {\n <div class=\"spinner-border spinner-border-sm text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n }\n </div>\n <div class=\"card-body p-0\">\n @if (hasTemplates()) {\n <div class=\"table-responsive\">\n <table class=\"table table-hover mb-0\">\n <thead>\n <tr>\n <th><i class=\"bi bi-tag me-1\"></i>Template Name</th>\n <th><i class=\"bi bi-person me-1\"></i>Creator</th>\n <th><i class=\"bi bi-folder me-1\"></i>Lab Group</th>\n <th><i class=\"bi bi-list me-1\"></i>Columns</th>\n <th><i class=\"bi bi-eye me-1\"></i>Visibility</th>\n <th><i class=\"bi bi-star me-1\"></i>Default</th>\n <th><i class=\"bi bi-gear me-1\"></i>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (template of templatesData().results; track template.id) {\n <tr>\n <td>\n <strong>{{ template.name }}</strong>\n @if (template.description) {\n <br><small class=\"text-muted\">{{ template.description }}</small>\n }\n </td>\n <td>{{ template.ownerUsername || 'System' }}</td>\n <td>\n @if (template.labGroup) {\n <span class=\"badge bg-info\">{{ getLabGroupName(template.labGroup) }}</span>\n } @else {\n <span class=\"text-muted\">-</span>\n }\n </td>\n <td>\n <span class=\"badge bg-secondary\">{{ template.columnCount || 0 }}</span>\n </td>\n <td>\n @switch (template.visibility) {\n @case ('public') {\n <span class=\"badge bg-success\">Public</span>\n }\n @case ('group') {\n <span class=\"badge bg-info\">Lab Group</span>\n }\n @case ('private') {\n <span class=\"badge bg-warning\">Private</span>\n }\n @default {\n <span class=\"badge bg-secondary\">{{ template.visibility }}</span>\n }\n }\n </td>\n <td>\n @if (template.isDefault) {\n <i class=\"bi bi-star-fill text-warning\"></i>\n } @else {\n <span class=\"text-muted\">-</span>\n }\n </td>\n <td>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n class=\"btn btn-outline-success\"\n (click)=\"createTableFromTemplate(template)\"\n title=\"Create metadata table from this template\">\n <i class=\"bi bi-table\"></i>\n </button>\n <button\n class=\"btn btn-outline-primary\"\n (click)=\"editTemplate(template)\"\n title=\"Edit template\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n <button\n class=\"btn btn-outline-info\"\n (click)=\"duplicateTemplate(template)\"\n title=\"Duplicate template\">\n <i class=\"bi bi-copy\"></i>\n </button>\n <button\n class=\"btn btn-outline-warning\"\n (click)=\"reorderTemplateColumns(template)\"\n title=\"Reorder columns by schema\"\n [disabled]=\"!template.columnCount || template.columnCount === 0\">\n <i class=\"bi bi-arrows-move\"></i>\n </button>\n <button\n class=\"btn btn-outline-danger\"\n (click)=\"deleteTemplate(template)\"\n title=\"Delete template\">\n <i class=\"bi bi-trash\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n @if (showTemplatesPagination()) {\n <div class=\"d-flex justify-content-center mt-3 p-3\">\n <ngb-pagination\n [collectionSize]=\"templatesData().count\"\n [page]=\"currentPage()\"\n [pageSize]=\"pageSize()\"\n [maxSize]=\"5\"\n [boundaryLinks]=\"true\"\n [rotate]=\"true\"\n (pageChange)=\"onPageChange($event)\"\n class=\"pagination-sm\">\n </ngb-pagination>\n </div>\n }\n } @else {\n <div class=\"empty-state text-center py-5\">\n <i class=\"bi bi-file-earmark-text fs-1 text-muted mb-3\"></i>\n <h5 class=\"text-muted\">No Table Templates Found</h5>\n <p class=\"text-muted\">\n @if (searchForm.get('search')?.value || searchForm.get('labGroupId')?.value) {\n Try adjusting your search criteria or create a new template.\n } @else {\n Get started by creating your first table template.\n }\n </p>\n <button class=\"btn btn-primary\" (click)=\"createTemplate()\">\n <i class=\"bi bi-plus me-1\"></i>Create First Template\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }, { kind: "directive", type: i2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "component", type: i2.NgbPagination, selector: "ngb-pagination", inputs: ["disabled", "boundaryLinks", "directionLinks", "ellipses", "rotate", "collectionSize", "maxSize", "page", "pageSize", "size"], outputs: ["pageChange"] }] });
6025
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplates, deps: [{ token: i1.FormBuilder }, { token: MetadataTableTemplateService }, { token: i3.LabGroupService }, { token: i2.NgbModal }, { token: i3.ToastService }, { token: AsyncTaskUIService }], target: i0.ɵɵFactoryTarget.Component });
6026
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: MetadataTableTemplates, isStandalone: true, selector: "ccv-metadata-table-templates", ngImport: i0, template: "<div class=\"metadata-table-templates-container\">\n <!-- Header Section -->\n <nav class=\"navbar navbar-expand-lg bg-body-tertiary shadow-sm\">\n <div class=\"container-fluid\">\n <div class=\"d-flex align-items-center justify-content-between w-100\">\n <div class=\"d-flex align-items-center\">\n <h4 class=\"navbar-brand m-0 fw-bold\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Table Templates\n </h4>\n @if (selectedLabGroup()) {\n <span class=\"badge bg-primary ms-3\">{{ selectedLabGroup()!.name }}</span>\n }\n </div>\n <div class=\"d-flex gap-2\">\n <div class=\"btn-group\" ngbDropdown>\n <button type=\"button\" class=\"btn btn-primary btn-sm dropdown-toggle\" ngbDropdownToggle>\n <i class=\"bi bi-plus me-1\"></i>New Template\n </button>\n <div class=\"dropdown-menu dropdown-menu-end\" ngbDropdownMenu>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"createTemplateWithCustomSchemas(); $event.preventDefault()\"\n title=\"Select one or multiple schemas to create template\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>\n <strong>Create from Schemas</strong>\n </a>\n <div class=\"dropdown-divider\"></div>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"createTemplate(); $event.preventDefault()\"\n title=\"Create from scratch\">\n <i class=\"bi bi-wrench me-2\"></i>\n <strong>Manual Configuration</strong>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </nav>\n\n <div class=\"templates-content p-4\">\n <!-- Search and Filters Section -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h5 class=\"mb-0\">\n <i class=\"bi bi-search me-2\"></i>Search & Filters\n </h5>\n </div>\n <div class=\"card-body\">\n <form [formGroup]=\"searchForm\" class=\"row g-3\">\n <div class=\"col-md-3\">\n <div class=\"form-floating\">\n <input\n type=\"search\"\n class=\"form-control\"\n id=\"templateSearch\"\n formControlName=\"search\"\n placeholder=\"Search templates...\">\n <label for=\"templateSearch\">\n <i class=\"bi bi-search me-1\"></i>Search Templates\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroupSelect\"\n formControlName=\"labGroupId\"\n (change)=\"onLabGroupChange($event)\">\n <option [ngValue]=\"null\">All Lab Groups</option>\n @if (hasLabGroups()) {\n @for (group of labGroupsData().results; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n }\n </select>\n <label for=\"labGroupSelect\">Lab Group</label>\n </div>\n\n <!-- Lab Groups Pagination -->\n @if (showLabGroupsPagination()) {\n <div class=\"d-flex justify-content-center mt-2\">\n <ngb-pagination\n [collectionSize]=\"labGroupsData().count\"\n [page]=\"labGroupsCurrentPage()\"\n [pageSize]=\"labGroupsPageSize()\"\n [maxSize]=\"3\"\n [boundaryLinks]=\"false\"\n [rotate]=\"true\"\n (pageChange)=\"onLabGroupsPageChange($event)\"\n class=\"pagination-sm\">\n </ngb-pagination>\n </div>\n }\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch mt-3\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"defaultTemplates\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"defaultTemplates\">\n <i class=\"bi bi-star me-1\"></i>Default Templates Only\n </label>\n </div>\n </div>\n </form>\n </div>\n </div>\n\n <!-- Templates Table -->\n <div class=\"card\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Table Templates\n <span class=\"badge bg-primary ms-2\">{{ templatesData().count }}</span>\n </h5>\n @if (isLoading()) {\n <div class=\"spinner-border spinner-border-sm text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n }\n </div>\n <div class=\"card-body p-0\">\n @if (hasTemplates()) {\n <div class=\"table-responsive\">\n <table class=\"table table-hover mb-0\">\n <thead>\n <tr>\n <th><i class=\"bi bi-tag me-1\"></i>Template Name</th>\n <th><i class=\"bi bi-person me-1\"></i>Creator</th>\n <th><i class=\"bi bi-folder me-1\"></i>Lab Group</th>\n <th><i class=\"bi bi-list me-1\"></i>Columns</th>\n <th><i class=\"bi bi-eye me-1\"></i>Visibility</th>\n <th><i class=\"bi bi-star me-1\"></i>Default</th>\n <th><i class=\"bi bi-gear me-1\"></i>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (template of templatesData().results; track template.id) {\n <tr>\n <td>\n <strong>{{ template.name }}</strong>\n @if (template.description) {\n <br><small class=\"text-muted\">{{ template.description }}</small>\n }\n </td>\n <td>{{ template.ownerUsername || 'System' }}</td>\n <td>\n @if (template.labGroup) {\n <span class=\"badge bg-info\">{{ getLabGroupName(template.labGroup) }}</span>\n } @else {\n <span class=\"text-muted\">-</span>\n }\n </td>\n <td>\n <span class=\"badge bg-secondary\">{{ template.columnCount || 0 }}</span>\n </td>\n <td>\n @switch (template.visibility) {\n @case ('public') {\n <span class=\"badge bg-success\">Public</span>\n }\n @case ('group') {\n <span class=\"badge bg-info\">Lab Group</span>\n }\n @case ('private') {\n <span class=\"badge bg-warning\">Private</span>\n }\n @default {\n <span class=\"badge bg-secondary\">{{ template.visibility }}</span>\n }\n }\n </td>\n <td>\n @if (template.isDefault) {\n <i class=\"bi bi-star-fill text-warning\"></i>\n } @else {\n <span class=\"text-muted\">-</span>\n }\n </td>\n <td>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n class=\"btn btn-outline-success\"\n (click)=\"createTableFromTemplate(template)\"\n title=\"Create metadata table from this template\">\n <i class=\"bi bi-table\"></i>\n </button>\n <button\n class=\"btn btn-outline-primary\"\n (click)=\"editTemplate(template)\"\n title=\"Edit template\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n <button\n class=\"btn btn-outline-info\"\n (click)=\"duplicateTemplate(template)\"\n title=\"Duplicate template\">\n <i class=\"bi bi-copy\"></i>\n </button>\n <button\n class=\"btn btn-outline-warning\"\n (click)=\"reorderTemplateColumns(template)\"\n title=\"Reorder columns by schema\"\n [disabled]=\"!template.columnCount || template.columnCount === 0\">\n <i class=\"bi bi-arrows-move\"></i>\n </button>\n <button\n class=\"btn btn-outline-danger\"\n (click)=\"deleteTemplate(template)\"\n title=\"Delete template\">\n <i class=\"bi bi-trash\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n @if (showTemplatesPagination()) {\n <div class=\"d-flex justify-content-center mt-3 p-3\">\n <ngb-pagination\n [collectionSize]=\"templatesData().count\"\n [page]=\"currentPage()\"\n [pageSize]=\"pageSize()\"\n [maxSize]=\"5\"\n [boundaryLinks]=\"true\"\n [rotate]=\"true\"\n (pageChange)=\"onPageChange($event)\"\n class=\"pagination-sm\">\n </ngb-pagination>\n </div>\n }\n } @else {\n <div class=\"empty-state text-center py-5\">\n <i class=\"bi bi-file-earmark-text fs-1 text-muted mb-3\"></i>\n <h5 class=\"text-muted\">No Table Templates Found</h5>\n <p class=\"text-muted\">\n @if (searchForm.get('search')?.value || searchForm.get('labGroupId')?.value) {\n Try adjusting your search criteria or create a new template.\n } @else {\n Get started by creating your first table template.\n }\n </p>\n <button class=\"btn btn-primary\" (click)=\"createTemplate()\">\n <i class=\"bi bi-plus me-1\"></i>Create First Template\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.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: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: NgbModule }, { kind: "directive", type: i2.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "popperOptions", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { kind: "directive", type: i2.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { kind: "directive", type: i2.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { kind: "component", type: i2.NgbPagination, selector: "ngb-pagination", inputs: ["disabled", "boundaryLinks", "directionLinks", "ellipses", "rotate", "collectionSize", "maxSize", "page", "pageSize", "size"], outputs: ["pageChange"] }] });
6029
6027
  }
6030
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: MetadataTableTemplates, decorators: [{
6028
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: MetadataTableTemplates, decorators: [{
6031
6029
  type: Component,
6032
6030
  args: [{ selector: 'ccv-metadata-table-templates', imports: [CommonModule, ReactiveFormsModule, NgbModule], template: "<div class=\"metadata-table-templates-container\">\n <!-- Header Section -->\n <nav class=\"navbar navbar-expand-lg bg-body-tertiary shadow-sm\">\n <div class=\"container-fluid\">\n <div class=\"d-flex align-items-center justify-content-between w-100\">\n <div class=\"d-flex align-items-center\">\n <h4 class=\"navbar-brand m-0 fw-bold\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Table Templates\n </h4>\n @if (selectedLabGroup()) {\n <span class=\"badge bg-primary ms-3\">{{ selectedLabGroup()!.name }}</span>\n }\n </div>\n <div class=\"d-flex gap-2\">\n <div class=\"btn-group\" ngbDropdown>\n <button type=\"button\" class=\"btn btn-primary btn-sm dropdown-toggle\" ngbDropdownToggle>\n <i class=\"bi bi-plus me-1\"></i>New Template\n </button>\n <div class=\"dropdown-menu dropdown-menu-end\" ngbDropdownMenu>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"createTemplateWithCustomSchemas(); $event.preventDefault()\"\n title=\"Select one or multiple schemas to create template\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>\n <strong>Create from Schemas</strong>\n </a>\n <div class=\"dropdown-divider\"></div>\n <a class=\"dropdown-item\" href=\"#\" (click)=\"createTemplate(); $event.preventDefault()\"\n title=\"Create from scratch\">\n <i class=\"bi bi-wrench me-2\"></i>\n <strong>Manual Configuration</strong>\n </a>\n </div>\n </div>\n </div>\n </div>\n </div>\n </nav>\n\n <div class=\"templates-content p-4\">\n <!-- Search and Filters Section -->\n <div class=\"card mb-4\">\n <div class=\"card-header\">\n <h5 class=\"mb-0\">\n <i class=\"bi bi-search me-2\"></i>Search & Filters\n </h5>\n </div>\n <div class=\"card-body\">\n <form [formGroup]=\"searchForm\" class=\"row g-3\">\n <div class=\"col-md-3\">\n <div class=\"form-floating\">\n <input\n type=\"search\"\n class=\"form-control\"\n id=\"templateSearch\"\n formControlName=\"search\"\n placeholder=\"Search templates...\">\n <label for=\"templateSearch\">\n <i class=\"bi bi-search me-1\"></i>Search Templates\n </label>\n </div>\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-floating\">\n <select\n class=\"form-select\"\n id=\"labGroupSelect\"\n formControlName=\"labGroupId\"\n (change)=\"onLabGroupChange($event)\">\n <option [ngValue]=\"null\">All Lab Groups</option>\n @if (hasLabGroups()) {\n @for (group of labGroupsData().results; track group.id) {\n <option [ngValue]=\"group.id\">{{ group.name }}</option>\n }\n }\n </select>\n <label for=\"labGroupSelect\">Lab Group</label>\n </div>\n\n <!-- Lab Groups Pagination -->\n @if (showLabGroupsPagination()) {\n <div class=\"d-flex justify-content-center mt-2\">\n <ngb-pagination\n [collectionSize]=\"labGroupsData().count\"\n [page]=\"labGroupsCurrentPage()\"\n [pageSize]=\"labGroupsPageSize()\"\n [maxSize]=\"3\"\n [boundaryLinks]=\"false\"\n [rotate]=\"true\"\n (pageChange)=\"onLabGroupsPageChange($event)\"\n class=\"pagination-sm\">\n </ngb-pagination>\n </div>\n }\n </div>\n <div class=\"col-md-3\">\n <div class=\"form-check form-switch mt-3\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n id=\"defaultTemplates\"\n formControlName=\"isDefault\">\n <label class=\"form-check-label\" for=\"defaultTemplates\">\n <i class=\"bi bi-star me-1\"></i>Default Templates Only\n </label>\n </div>\n </div>\n </form>\n </div>\n </div>\n\n <!-- Templates Table -->\n <div class=\"card\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\">\n <i class=\"bi bi-file-earmark-text me-2\"></i>Table Templates\n <span class=\"badge bg-primary ms-2\">{{ templatesData().count }}</span>\n </h5>\n @if (isLoading()) {\n <div class=\"spinner-border spinner-border-sm text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n }\n </div>\n <div class=\"card-body p-0\">\n @if (hasTemplates()) {\n <div class=\"table-responsive\">\n <table class=\"table table-hover mb-0\">\n <thead>\n <tr>\n <th><i class=\"bi bi-tag me-1\"></i>Template Name</th>\n <th><i class=\"bi bi-person me-1\"></i>Creator</th>\n <th><i class=\"bi bi-folder me-1\"></i>Lab Group</th>\n <th><i class=\"bi bi-list me-1\"></i>Columns</th>\n <th><i class=\"bi bi-eye me-1\"></i>Visibility</th>\n <th><i class=\"bi bi-star me-1\"></i>Default</th>\n <th><i class=\"bi bi-gear me-1\"></i>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (template of templatesData().results; track template.id) {\n <tr>\n <td>\n <strong>{{ template.name }}</strong>\n @if (template.description) {\n <br><small class=\"text-muted\">{{ template.description }}</small>\n }\n </td>\n <td>{{ template.ownerUsername || 'System' }}</td>\n <td>\n @if (template.labGroup) {\n <span class=\"badge bg-info\">{{ getLabGroupName(template.labGroup) }}</span>\n } @else {\n <span class=\"text-muted\">-</span>\n }\n </td>\n <td>\n <span class=\"badge bg-secondary\">{{ template.columnCount || 0 }}</span>\n </td>\n <td>\n @switch (template.visibility) {\n @case ('public') {\n <span class=\"badge bg-success\">Public</span>\n }\n @case ('group') {\n <span class=\"badge bg-info\">Lab Group</span>\n }\n @case ('private') {\n <span class=\"badge bg-warning\">Private</span>\n }\n @default {\n <span class=\"badge bg-secondary\">{{ template.visibility }}</span>\n }\n }\n </td>\n <td>\n @if (template.isDefault) {\n <i class=\"bi bi-star-fill text-warning\"></i>\n } @else {\n <span class=\"text-muted\">-</span>\n }\n </td>\n <td>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button\n class=\"btn btn-outline-success\"\n (click)=\"createTableFromTemplate(template)\"\n title=\"Create metadata table from this template\">\n <i class=\"bi bi-table\"></i>\n </button>\n <button\n class=\"btn btn-outline-primary\"\n (click)=\"editTemplate(template)\"\n title=\"Edit template\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n <button\n class=\"btn btn-outline-info\"\n (click)=\"duplicateTemplate(template)\"\n title=\"Duplicate template\">\n <i class=\"bi bi-copy\"></i>\n </button>\n <button\n class=\"btn btn-outline-warning\"\n (click)=\"reorderTemplateColumns(template)\"\n title=\"Reorder columns by schema\"\n [disabled]=\"!template.columnCount || template.columnCount === 0\">\n <i class=\"bi bi-arrows-move\"></i>\n </button>\n <button\n class=\"btn btn-outline-danger\"\n (click)=\"deleteTemplate(template)\"\n title=\"Delete template\">\n <i class=\"bi bi-trash\"></i>\n </button>\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n @if (showTemplatesPagination()) {\n <div class=\"d-flex justify-content-center mt-3 p-3\">\n <ngb-pagination\n [collectionSize]=\"templatesData().count\"\n [page]=\"currentPage()\"\n [pageSize]=\"pageSize()\"\n [maxSize]=\"5\"\n [boundaryLinks]=\"true\"\n [rotate]=\"true\"\n (pageChange)=\"onPageChange($event)\"\n class=\"pagination-sm\">\n </ngb-pagination>\n </div>\n }\n } @else {\n <div class=\"empty-state text-center py-5\">\n <i class=\"bi bi-file-earmark-text fs-1 text-muted mb-3\"></i>\n <h5 class=\"text-muted\">No Table Templates Found</h5>\n <p class=\"text-muted\">\n @if (searchForm.get('search')?.value || searchForm.get('labGroupId')?.value) {\n Try adjusting your search criteria or create a new template.\n } @else {\n Get started by creating your first table template.\n }\n </p>\n <button class=\"btn btn-primary\" (click)=\"createTemplate()\">\n <i class=\"bi bi-plus me-1\"></i>Create First Template\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n" }]
6033
6031
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: MetadataTableTemplateService }, { type: i3.LabGroupService }, { type: i2.NgbModal }, { type: i3.ToastService }, { type: AsyncTaskUIService }] });
6034
6032
 
6035
6033
  class CupcakeVanilla {
6036
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: CupcakeVanilla, deps: [], target: i0.ɵɵFactoryTarget.Component });
6037
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.0", type: CupcakeVanilla, isStandalone: true, selector: "ccv-cupcake-vanilla", ngImport: i0, template: `
6034
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: CupcakeVanilla, deps: [], target: i0.ɵɵFactoryTarget.Component });
6035
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: CupcakeVanilla, isStandalone: true, selector: "ccv-cupcake-vanilla", ngImport: i0, template: `
6038
6036
  <p>
6039
6037
  cupcake-vanilla works!
6040
6038
  </p>
6041
6039
  `, isInline: true, styles: [""] });
6042
6040
  }
6043
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: CupcakeVanilla, decorators: [{
6041
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: CupcakeVanilla, decorators: [{
6044
6042
  type: Component,
6045
6043
  args: [{ selector: 'ccv-cupcake-vanilla', imports: [], template: `
6046
6044
  <p>