0 &&
+ const base = this.data.dataField.name && this.data.dataField.name.length > 0 &&
this.data.dataField.displayOrder > 0;
+ if (this.isCompetency()) {
+ return base && this.data.dataField.dataOptions && this.data.dataField.dataOptions.length > 0;
+ }
+ return base;
}
changeDataFieldDataType(selectedDataType: string) {
@@ -62,6 +69,12 @@ export class DataFieldEditDialogComponent {
if (this.data.dataField.dataType !== selectedDataType) {
// set the new value
this.data.dataField.dataType = selectedDataType;
+ // Competency fields are always option-list + multi-select
+ if (selectedDataType === DataFieldType.Competency) {
+ this.data.dataField.isChosenFromList = true;
+ this.data.dataField.isMultiSelect = true;
+ this.data.dataField.isFacilitationField = true;
+ }
}
}
@@ -110,16 +123,97 @@ export class DataFieldEditDialogComponent {
this.data.dataField.dataOptions.splice(index, 1);
}
+ importDataOptions(dataField: DataField) {
+ // If field is not saved yet, save it first
+ if (!dataField.id) {
+ if (!this.errorFree()) {
+ return; // Can't save if there are validation errors
+ }
+ // Save via callback if provided, otherwise emit event
+ if (this.data.onSave) {
+ const savedId = this.data.onSave(this.data.dataField);
+ if (savedId) {
+ this.data.dataField.id = savedId;
+ // Now open import dialog with the new ID
+ this.openImportDialog(this.data.dataField);
+ }
+ }
+ return;
+ }
+
+ this.openImportDialog(dataField);
+ }
+
+ private openImportDialog(dataField: DataField) {
+ const dialogRef = this.dialog.open(DataOptionImportDialogComponent, {
+ width: '800px',
+ maxWidth: '90vw',
+ data: {
+ dataFieldId: dataField.id,
+ existingOptions: this.data.dataField.dataOptions
+ },
+ });
+ dialogRef.afterClosed().subscribe((result) => {
+ if (result && Array.isArray(result)) {
+ // Add imported options to the displayed list
+ this.data.dataField.dataOptions.push(...result);
+ }
+ });
+ }
+
+ viewAllOptions() {
+ const canEdit = this.data.canEdit || this.data.isOwner;
+ const canAddOptions = canEdit && !this.optionListNotAllowed();
+
+ if (this.data.dataField.dataType === DataFieldType.Competency) {
+ const compDialogRef = this.dialog.open(CompetencyOptionsDialogComponent, {
+ width: '900px',
+ maxWidth: '95vw',
+ maxHeight: '90vh',
+ data: {
+ dataFieldId: this.data.dataField.id,
+ dataOptions: this.data.dataField.dataOptions,
+ canEdit: canAddOptions
+ }
+ });
+ compDialogRef.afterClosed().subscribe((updatedOptions) => {
+ if (updatedOptions) {
+ this.data.dataField = { ...this.data.dataField, dataOptions: updatedOptions };
+ }
+ });
+ } else {
+ this.dialog.open(DataOptionListDialogComponent, {
+ width: '900px',
+ maxWidth: '95vw',
+ maxHeight: '90vh',
+ data: {
+ dataOptions: this.data.dataField.dataOptions,
+ canEdit: canAddOptions,
+ canImport: canAddOptions,
+ onEdit: (option: DataOption) => this.editDataOption(option),
+ onDelete: (option: DataOption) => this.deleteDataOption(option),
+ onAdd: () => this.addDataOption(this.data.dataField),
+ onImport: () => this.importDataOptions(this.data.dataField)
+ }
+ });
+ }
+ }
+
sortedDataFieldOptions() {
return this.data.dataField.dataOptions
.sort((a, b) => +a.displayOrder < +b.displayOrder ? -1 : 1);
}
+ isCompetency(): boolean {
+ return this.data.dataField.dataType === DataFieldType.Competency;
+ }
+
optionListNotAllowed(): boolean {
return !(
this.data.dataField.dataType === DataFieldType.Double ||
this.data.dataField.dataType === DataFieldType.Integer ||
- this.data.dataField.dataType === DataFieldType.String
+ this.data.dataField.dataType === DataFieldType.String ||
+ this.data.dataField.dataType === DataFieldType.Competency
);
}
diff --git a/src/app/components/data-field-list/data-field-list.component.html b/src/app/components/data-field-list/data-field-list.component.html
index fea08a34..b04947d7 100755
--- a/src/app/components/data-field-list/data-field-list.component.html
+++ b/src/app/components/data-field-list/data-field-list.component.html
@@ -76,9 +76,10 @@
+ {{ !showTemplates && element.displayOrder > 0 ? element.displayOrder : ' '}}
@if (element.displayOrder > 0) {
-
+
@if (canEdit()) {
- {{ !showTemplates && element.displayOrder > 0 ? element.displayOrder : ' '}}
-
List
+ title="Display on Scenario Events list">
-
View
+ (click)="$event.stopPropagation();" title="Display on the Exercise View">
+
+
+
+
+
+
+ Assess
+
+
+
-
Info
@@ -145,14 +158,14 @@
@if (element.displayOrder >= 0) {
+ title="Display for Information Events">
}
-
Facil
@@ -160,7 +173,7 @@
@if (element.displayOrder >= 0) {
+ title="Display for Facilitation Events">
}
@@ -182,7 +195,7 @@
-
Devs
@@ -190,7 +203,7 @@
@if (element.displayOrder >= 0) {
+ title="Hide this field from participants on all views - show only to content developers and MSEL owners">
}
@@ -213,14 +226,19 @@
- Options
+ Options
- {{ getDataOptionsString(element) }}
+ 0)"
+ [title]="element.dataType === 'Competency' || (element.dataOptions && element.dataOptions.length > 0) ? 'Click to manage options' : ''">
+ {{ getDataOptionsString(element) }}
+
- Integration
+ Integration
@if (element.displayOrder > 0) {
this.saveDataField(df)
},
});
dialogRef.componentInstance.editComplete.subscribe((result) => {
@@ -392,6 +404,9 @@ export class DataFieldListComponent implements OnDestroy, OnInit, AfterViewInit
break;
case 'onScenarioEventList':
case 'onExerciseView':
+ case 'isInformationField':
+ case 'isFacilitationField':
+ case 'isAssessorVisible':
case 'isShownOnDefaultTab':
case 'isOnlyShownToOwners':
const aChecked = a[sortFieldName];
@@ -400,8 +415,18 @@ export class DataFieldListComponent implements OnDestroy, OnInit, AfterViewInit
sortResult = aChecked ? -1 : 1;
}
break;
+ case 'options':
+ const aOptionsCount = a.dataOptions?.length || 0;
+ const bOptionsCount = b.dataOptions?.length || 0;
+ if (aOptionsCount < bOptionsCount) {
+ sortResult = -1;
+ } else if (aOptionsCount > bOptionsCount) {
+ sortResult = 1;
+ }
+ break;
case 'name':
case 'dataType':
+ case 'galleryArticleParameter':
const aStr = a[sortFieldName];
const bStr = b[sortFieldName];
sortResult = Intl.Collator().compare(
@@ -459,19 +484,123 @@ export class DataFieldListComponent implements OnDestroy, OnInit, AfterViewInit
}
getDataOptionsString(dataField: DataField): string {
- if (dataField.dataOptions) {
- const dataOptions = dataField.dataOptions
- .slice()
- .sort((a, b) => (+a.displayOrder < +b.displayOrder ? -1 : 1))
- .map(function (elem) {
- return elem.optionName;
- });
- return dataOptions.join(', ');
+ if (dataField.dataOptions && dataField.dataOptions.length > 0) {
+ const count = dataField.dataOptions.length;
+ if (dataField.dataType === DataFieldType.Competency) {
+ return `${count} competenc${count !== 1 ? 'ies' : 'y'}`;
+ }
+ return `${count} option${count !== 1 ? 's' : ''}`;
+ } else if (dataField.dataType === DataFieldType.Competency) {
+ return 'Manage';
} else {
return ' ';
}
}
+ viewDataOptions(dataField: DataField, event: Event) {
+ event.stopPropagation();
+ if (dataField.dataType === DataFieldType.Competency || (dataField.dataOptions && dataField.dataOptions.length > 0)) {
+ const hasEditPermission = this.canEdit() || this.msel.hasRole(this.loggedInUserId, null).owner;
+ const supportsOptionList =
+ dataField.dataType === DataFieldType.Double ||
+ dataField.dataType === DataFieldType.Integer ||
+ dataField.dataType === DataFieldType.String ||
+ dataField.dataType === DataFieldType.Competency;
+ const canAddOptions = hasEditPermission && supportsOptionList;
+
+ if (dataField.dataType === DataFieldType.Competency) {
+ const compDialogRef = this.dialog.open(CompetencyOptionsDialogComponent, {
+ width: '900px',
+ maxWidth: '95vw',
+ maxHeight: '90vh',
+ data: {
+ dataFieldId: dataField.id,
+ dataOptions: dataField.dataOptions,
+ canEdit: canAddOptions
+ }
+ });
+ compDialogRef.afterClosed().subscribe((updatedOptions) => {
+ const updated = { ...dataField, dataOptions: updatedOptions || dataField.dataOptions };
+ this.dataFieldDataService.updateDataField(updated);
+ });
+ } else {
+ this.dialog.open(DataOptionListDialogComponent, {
+ width: '900px',
+ maxWidth: '95vw',
+ maxHeight: '90vh',
+ data: {
+ dataOptions: dataField.dataOptions,
+ canEdit: canAddOptions,
+ onEdit: (option: DataOption) => this.editDataOption(option, dataField),
+ onDelete: (option: DataOption) => this.deleteDataOption(option, dataField),
+ onAdd: () => this.addDataOption(dataField),
+ onImport: () => this.importDataOptions(dataField)
+ }
+ });
+ }
+ }
+ }
+
+ addDataOption(dataField: DataField) {
+ const dataOption: DataOption = {
+ displayOrder: dataField.dataOptions.length + 1,
+ dataFieldId: dataField.id
+ };
+ this.openDataOptionEditDialog(dataOption, dataField);
+ }
+
+ editDataOption(option: DataOption, dataField: DataField) {
+ const selected = { ...option };
+ this.openDataOptionEditDialog(selected, dataField);
+ }
+
+ deleteDataOption(option: DataOption, dataField: DataField) {
+ const index = dataField.dataOptions.findIndex(x => x.id === option.id);
+ if (index >= 0) {
+ dataField.dataOptions.splice(index, 1);
+ this.dataFieldDataService.updateDataField(dataField);
+ }
+ }
+
+ private openDataOptionEditDialog(dataOption: DataOption, dataField: DataField) {
+ const dialogRef = this.dialog.open(DataOptionEditDialogComponent, {
+ minWidth: '400px',
+ maxWidth: '90vw',
+ width: 'auto',
+ data: { dataOption }
+ });
+ dialogRef.componentInstance.editComplete.subscribe((result) => {
+ if (result.saveChanges && result.dataOption) {
+ if (dataOption.id) {
+ const index = dataField.dataOptions.findIndex(x => x.id === dataOption.id);
+ dataField.dataOptions[index] = dataOption;
+ } else {
+ dataOption.id = uuidv4();
+ dataField.dataOptions.push(dataOption);
+ }
+ this.dataFieldDataService.updateDataField(dataField);
+ }
+ dialogRef.close();
+ });
+ }
+
+ importDataOptions(dataField: DataField) {
+ const dialogRef = this.dialog.open(DataOptionImportDialogComponent, {
+ width: '800px',
+ maxWidth: '90vw',
+ data: {
+ dataFieldId: dataField.id,
+ existingOptions: dataField.dataOptions
+ },
+ });
+ dialogRef.afterClosed().subscribe((result) => {
+ if (result && Array.isArray(result)) {
+ dataField.dataOptions.push(...result);
+ this.dataFieldDataService.updateDataField(dataField);
+ }
+ });
+ }
+
saveChange(dataField: DataField) {
if (dataField.displayOrder > 0) {
this.dataFieldDataService.updateDataField(dataField);
@@ -480,19 +609,23 @@ export class DataFieldListComponent implements OnDestroy, OnInit, AfterViewInit
case 'Move':
this.msel.showMoveOnExerciseView = dataField.onExerciseView;
this.msel.showMoveOnScenarioEventList = dataField.onScenarioEventList;
+ this.msel.showMoveOnAssessorView = dataField.isAssessorVisible;
break;
case 'Group':
this.msel.showGroupOnExerciseView = dataField.onExerciseView;
this.msel.showGroupOnScenarioEventList =
dataField.onScenarioEventList;
+ this.msel.showGroupOnAssessorView = dataField.isAssessorVisible;
break;
case 'Execution Time':
this.msel.showTimeOnExerciseView = dataField.onExerciseView;
this.msel.showTimeOnScenarioEventList = dataField.onScenarioEventList;
+ this.msel.showTimeOnAssessorView = dataField.isAssessorVisible;
break;
case 'Integration Target':
this.msel.showIntegrationTargetOnExerciseView = dataField.onExerciseView;
this.msel.showIntegrationTargetOnScenarioEventList = dataField.onScenarioEventList;
+ this.msel.showIntegrationTargetOnAssessorView = dataField.isAssessorVisible;
break;
}
this.saveMsel();
diff --git a/src/app/components/data-option-edit-dialog/data-option-edit-dialog.component.html b/src/app/components/data-option-edit-dialog/data-option-edit-dialog.component.html
index 26ce3aed..092adbcc 100644
--- a/src/app/components/data-option-edit-dialog/data-option-edit-dialog.component.html
+++ b/src/app/components/data-option-edit-dialog/data-option-edit-dialog.component.html
@@ -5,7 +5,7 @@
-->
-
+ Edit Option
- Name
+ ID
@@ -37,11 +37,21 @@
+ @if (data.showDescription) {
+
+
+
+ Description
+
+
+
+
+ }
}
+
+@if (dataField.dataType === dataType.Competency) {
+
+ @if (!showValueOnly) {
+
+
{{ dataField.name }}
+
+ @for (id of selectedCompetencies; track id) {
+
+ {{ displayCompetency(id) }}
+ @if (canEdit) {
+
+
+
+ }
+
+ }
+
+ @if (canEdit) {
+
+ Select competencies
+
+
+
+
+ @if (competencyFilter) {
+
+
+
+ }
+
+
+ @for (option of filteredCompetencyOptions; track option.optionName) {
+ {{ option.optionName }} - {{ option.optionValue }}
+ }
+ @if (filteredCompetencyOptions.length === 0) {
+ No matching competencies
+ }
+
+
+ }
+
+ }
+ @if (showValueOnly) {
+
+ @for (id of selectedCompetencies; track id; let isLast = $last) {
+ {{ displayCompetency(id) }} @if (!isLast) {, }
+ }
+
+ }
+
+}
-@if (dataField.isChosenFromList) {
+@if (dataField.isChosenFromList && dataField.dataType !== dataType.Competency) {
@if (!showValueOnly) {
@@ -428,27 +485,12 @@
}
@if (showValueOnly) {
-
@if (!dataField.isMultiSelect) {
-
- @for (dataOption of dataField.dataOptions; track $index) {
-
- {{ dataOption.optionName }}
-
- }
-
+ {{ value }}
+ } @else {
+ @for (val of getValueArray(); track val) {
+ {{ val }}
}
-
- @if (dataField.isMultiSelect) {
-
- @for (dataOption of dataField.dataOptions; track $index) {
-
- {{ dataOption.optionName }}
-
- }
-
}
}
diff --git a/src/app/components/data-value/data-value.component.scss b/src/app/components/data-value/data-value.component.scss
index eb5c2fa5..9212a999 100644
--- a/src/app/components/data-value/data-value.component.scss
+++ b/src/app/components/data-value/data-value.component.scss
@@ -19,6 +19,26 @@
width: 300px;
}
+.competency-field {
+ width: 100%;
+}
+
+.competency-label {
+ font-size: 13px;
+ font-weight: 400;
+ color: var(--mat-sys-on-surface-variant);
+}
+
+
+.competency-chip-view {
+ display: inline-block;
+ padding: 2px 8px;
+ margin: 2px 4px 2px 0;
+ border-radius: 16px;
+ font-size: 13px;
+ background: var(--mat-sys-surface-variant);
+}
+
// overrides.scss or styles.scss
// This fixes https://github.com//issues/4609
/* TODO(mdc-migration): The following rule targets internal classes of dialog that may no longer apply for the MDC version.*/
diff --git a/src/app/components/data-value/data-value.component.ts b/src/app/components/data-value/data-value.component.ts
index 68a25be1..cc2f99b6 100644
--- a/src/app/components/data-value/data-value.component.ts
+++ b/src/app/components/data-value/data-value.component.ts
@@ -85,6 +85,58 @@ export class DataValueComponent {
MselItemStatus.Archived,
];
+ // --- Competency chips + search list ---
+ competencyFilter = '';
+
+ get selectedCompetencies(): string[] {
+ return this.value ? this.value.split(', ').filter(v => v) : [];
+ }
+
+ get filteredCompetencyOptions() {
+ const options = this.dataField?.dataOptions || [];
+ if (!this.competencyFilter) return options;
+ const s = this.competencyFilter.toLowerCase();
+ return options.filter(o =>
+ o.optionName?.toLowerCase().includes(s) ||
+ o.optionValue?.toLowerCase().includes(s) ||
+ o.optionDescription?.toLowerCase().includes(s)
+ );
+ }
+
+ displayCompetency = (val: string): string => {
+ if (!val || !this.dataField?.dataOptions) return val || '';
+ const opt = this.dataField.dataOptions.find(o => o.optionName === val);
+ return opt ? opt.optionName : val;
+ }
+
+ getCompetencyTooltip = (val: string): string => {
+ if (!val || !this.dataField?.dataOptions) return '';
+ const opt = this.dataField.dataOptions.find(o => o.optionName === val);
+ return opt?.optionValue || '';
+ }
+
+ isCompetencySelected(optionName: string): boolean {
+ return this.selectedCompetencies.includes(optionName);
+ }
+
+ toggleCompetency(optionName: string) {
+ const current = this.selectedCompetencies;
+ const idx = current.indexOf(optionName);
+ if (idx >= 0) {
+ current.splice(idx, 1);
+ } else {
+ current.push(optionName);
+ }
+ this.value = current.join(', ');
+ this.valueChangeHandler();
+ }
+
+ removeCompetency(id: string) {
+ const current = this.selectedCompetencies.filter(v => v !== id);
+ this.value = current.join(', ');
+ this.valueChangeHandler();
+ }
+
valueChangeHandler() {
this.valueChange.emit(this.value);
}
diff --git a/src/app/components/home-app/home-app.component.html b/src/app/components/home-app/home-app.component.html
index 920e4e48..3ccb2d9f 100755
--- a/src/app/components/home-app/home-app.component.html
+++ b/src/app/components/home-app/home-app.component.html
@@ -36,7 +36,8 @@ If the problem persists, please contact the site administrator.
@if (!apiIsSick && selectedMselId) {
}
diff --git a/src/app/components/home-app/home-app.component.ts b/src/app/components/home-app/home-app.component.ts
index bc3924f9..d86f4571 100755
--- a/src/app/components/home-app/home-app.component.ts
+++ b/src/app/components/home-app/home-app.component.ts
@@ -52,6 +52,7 @@ export class HomeAppComponent implements OnDestroy, OnInit {
username = '';
canAccessAdminSection = false;
canEditMsels = false;
+ canEditCheckboxes = false;
isAuthorizedUser = false;
isSidebarOpen = true;
private unsubscribe$ = new Subject();
@@ -147,6 +148,8 @@ export class HomeAppComponent implements OnDestroy, OnInit {
this.permissions = this.permissionDataService.permissions;
this.canAccessAdminSection = this.permissions.filter(p => !p.endsWith('Msels')).length > 0;
this.canEditMsels = this.permissionDataService.hasPermission(SystemPermission.EditMsels);
+ // Admins (ContentDevelopers/SystemAdmins) can edit checkboxes
+ this.canEditCheckboxes = this.permissionDataService.hasPermission(SystemPermission.CreateMsels);
}
);
// Start SignalR connection
diff --git a/src/app/components/landing/join/join.component.ts b/src/app/components/landing/join/join.component.ts
index 52cef043..2eec56f7 100644
--- a/src/app/components/landing/join/join.component.ts
+++ b/src/app/components/landing/join/join.component.ts
@@ -19,6 +19,7 @@ import { TopbarView } from '../../shared/top-bar/topbar.models';
import { Title } from '@angular/platform-browser';
import { ErrorService } from 'src/app/services/error/error.service';
import { UIDataService } from 'src/app/data/ui/ui-data.service';
+import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-join',
@@ -52,7 +53,8 @@ export class JoinComponent implements OnDestroy, OnInit {
private router: Router,
private titleService: Title,
private errorService: ErrorService,
- private uiDataService: UIDataService
+ private uiDataService: UIDataService,
+ private http: HttpClient
) {
this.hideTopbar = this.uiDataService.inIframe();
// set image
@@ -79,6 +81,12 @@ export class JoinComponent implements OnDestroy, OnInit {
}
ngOnInit() {
+ // Call xAPI for join page viewed
+ const baseUrl = this.settingsService.settings.ApiUrl.endsWith('/')
+ ? this.settingsService.settings.ApiUrl
+ : this.settingsService.settings.ApiUrl + '/';
+ this.http.post(`${baseUrl}api/xapi/viewed/joinpage`, {}).subscribe();
+
// subscribe to users
this.userQuery.selectAll()
.pipe(takeUntil(this.unsubscribe$))
diff --git a/src/app/components/msel-competencies/msel-competencies.component.html b/src/app/components/msel-competencies/msel-competencies.component.html
new file mode 100644
index 00000000..313bd049
--- /dev/null
+++ b/src/app/components/msel-competencies/msel-competencies.component.html
@@ -0,0 +1,362 @@
+
+
+@if (msel) {
+
+
+
+
+
+
+
+ Add Competencies
+
+
+
+ @if (selectedFramework && frameworkWorkRoles.length > 0) {
+
+ } @else if (!selectedFramework) {
+ Select a framework to browse work roles and competencies
+ }
+
+
+
+
+
+
+
+ MSEL Competencies ({{ mselCompetencyList.length }})
+
+
+
+
+
+
+}
diff --git a/src/app/components/msel-competencies/msel-competencies.component.scss b/src/app/components/msel-competencies/msel-competencies.component.scss
new file mode 100644
index 00000000..ad1516f5
--- /dev/null
+++ b/src/app/components/msel-competencies/msel-competencies.component.scss
@@ -0,0 +1,344 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the
+// project root for license information.
+
+@use "@angular/material" as mat;
+
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.top-toolbar {
+ display: flex;
+ align-items: center;
+ min-height: 76px;
+ flex-shrink: 0;
+ padding: 0 4px;
+}
+
+.sp-icon {
+ margin-left: 4px;
+ margin-right: 4px;
+}
+
+.framework-select {
+ width: 400px;
+ margin-left: 10px;
+}
+
+.content-area {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ padding: 0;
+ overflow-y: auto;
+}
+
+.section-panel {
+ margin: 0 16px 12px;
+ background-color: transparent;
+ flex-shrink: 0;
+}
+
+.panel-icon {
+ margin-right: 8px;
+ color: var(--mat-sys-primary);
+}
+
+.toolbar-row {
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ min-height: 48px;
+}
+
+.button-end {
+ margin-left: auto;
+}
+
+table {
+ width: 100%;
+}
+
+// =============================================
+// Panel 1: Add Competencies — work role browser
+// =============================================
+
+.browser-table-scroll {
+ max-height: 580px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+// Work role browser columns
+.wr-col-check {
+ flex: 0 0 60px;
+ width: 60px;
+ padding-left: 8px !important;
+ padding-right: 8px !important;
+}
+
+.wr-col-id {
+ flex: 0 0 120px;
+ white-space: nowrap;
+}
+
+.wr-col-name {
+ flex: 1 1 40%;
+ min-width: 0;
+}
+
+.wr-col-category {
+ flex: 1 1 30%;
+ min-width: 0;
+}
+
+// Browser row expand
+.browser-row {
+ cursor: pointer;
+}
+
+.browser-row:hover {
+ background-color: var(--mat-sys-surface-variant);
+}
+
+.browser-row-expanded {
+ background-color: var(--mat-sys-surface-variant);
+}
+
+// Browser children (expanded detail)
+.browser-children {
+ width: 100%;
+ padding: 8px 16px 12px 60px;
+}
+
+.browser-children-toolbar {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+
+ @include mat.form-field-density(-5);
+}
+
+.browser-children-count {
+ font-size: 13px;
+ color: var(--mat-sys-on-surface-variant);
+ margin-left: auto;
+}
+
+.browser-children-list {
+ max-height: 300px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ border: 1px solid var(--mat-sys-outline-variant);
+ border-radius: 4px;
+}
+
+.browser-child-row {
+ display: flex;
+ align-items: center;
+ padding: 4px 8px;
+ border-bottom: 1px solid var(--mat-sys-outline-variant);
+ font-size: 13px;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &:hover {
+ background-color: var(--mat-sys-surface-variant);
+ }
+}
+
+.browser-child-check {
+ flex: 0 0 40px;
+ margin-right: 8px;
+
+ @include mat.checkbox-density(-2);
+}
+
+.browser-child-id {
+ flex: 0 0 100px;
+ font-weight: 500;
+ white-space: nowrap;
+}
+
+.browser-child-type {
+ flex: 0 0 80px;
+ color: var(--mat-sys-on-surface-variant);
+ font-size: 12px;
+}
+
+.browser-child-name {
+ flex: 1 1 auto;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+// =============================================
+// Panel 2: MSEL Competencies — pool
+// =============================================
+
+.pool-table-scroll {
+ max-height: calc(100vh - 400px);
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding-bottom: 20px;
+}
+
+// Pool table columns
+.pool-col-select {
+ flex: 0 0 56px;
+ width: 56px;
+}
+
+.pool-col-action {
+ flex: 0 0 72px;
+ width: 72px;
+}
+
+.pool-col-id {
+ flex: 0 0 120px;
+ white-space: nowrap;
+}
+
+.pool-col-type {
+ flex: 0 0 10%;
+}
+
+.pool-col-framework {
+ flex: 1 1 15%;
+ min-width: 0;
+}
+
+.pool-col-name {
+ flex: 1 1 20%;
+ min-width: 0;
+}
+
+.pool-col-teams {
+ flex: 2 1 30%;
+ min-width: 0;
+}
+
+.pool-col-events {
+ flex: 0 0 60px;
+ text-align: center;
+}
+
+// Pool row expand
+.pool-row {
+ cursor: pointer;
+}
+
+.pool-row:hover {
+ background-color: var(--mat-sys-surface-variant);
+}
+
+.pool-row-expanded {
+ background-color: var(--mat-sys-surface-variant);
+}
+
+// =============================================
+// Shared: detail row expand
+// =============================================
+
+.detail-row {
+ height: 0;
+ min-height: 0;
+ overflow: hidden;
+ --mat-table-row-item-outline-width: 0px;
+}
+
+::ng-deep .detail-row .mat-mdc-cell.expanded-detail-cell {
+ padding: 0 !important;
+ min-height: 0;
+ border-bottom-width: 0;
+ flex: 0 0 100% !important;
+ width: 100% !important;
+ max-width: 100% !important;
+}
+
+.expanded-detail-cell {
+ display: flex;
+ flex-direction: column;
+ flex: 0 0 100%;
+ width: 100%;
+ max-width: 100%;
+ padding: 0;
+}
+
+.no-results {
+ margin-top: 20px;
+ margin-left: 150px;
+}
+
+.no-results-inline {
+ padding: 8px 0;
+ color: var(--mat-sys-on-surface-variant);
+ font-style: italic;
+}
+
+// =============================================
+// Team assignment enchilada (Panel 2 expand)
+// =============================================
+
+// TODO: standardize team enchilada widths across all pages
+// (card-teams, player-application-teams, competencies, etc.)
+.related-enchilada {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ width: 100%;
+ padding: 8px 0;
+ gap: 4%;
+}
+
+.related-list-container {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 48%;
+ min-width: 250px;
+ border: 1px solid var(--mat-sys-outline-variant);
+ border-radius: 4px;
+
+ table {
+ width: 100%;
+ }
+}
+
+.related-toolbar {
+ background: transparent;
+ overflow: visible;
+
+ @include mat.form-field-density(-5);
+
+ p {
+ margin-left: 8px;
+ }
+}
+
+::ng-deep .related-toolbar .mat-toolbar-row {
+ overflow: visible;
+}
+
+.header-icon {
+ color: var(--mat-sys-primary);
+}
+
+.related-table-scroll {
+ max-height: 300px;
+ overflow: auto;
+}
+
+.related-col-btn {
+ flex: 0 0 48px;
+ width: 48px;
+ justify-content: center;
+}
diff --git a/src/app/components/msel-competencies/msel-competencies.component.ts b/src/app/components/msel-competencies/msel-competencies.component.ts
new file mode 100644
index 00000000..afdc31ab
--- /dev/null
+++ b/src/app/components/msel-competencies/msel-competencies.component.ts
@@ -0,0 +1,872 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the
+// project root for license information.
+import { Component, Input, OnDestroy, OnInit, ViewChild, AfterViewInit } from '@angular/core';
+import { UntypedFormControl } from '@angular/forms';
+import { SelectionModel } from '@angular/cdk/collections';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { Subject, Observable } from 'rxjs';
+import { take, takeUntil } from 'rxjs/operators';
+import {
+ Competency,
+ CompetencyFramework,
+ CompetencyFrameworkService,
+ DataField,
+ DataFieldType,
+ DataValue,
+ MselCompetency,
+ Team,
+ TeamCompetency,
+ SystemPermission,
+} from 'src/app/generated/blueprint.api';
+import { PermissionDataService } from 'src/app/data/permission/permission-data.service';
+import { MselDataService, MselPlus } from 'src/app/data/msel/msel-data.service';
+import { MselQuery } from 'src/app/data/msel/msel.query';
+import { MselCompetencyDataService } from 'src/app/data/msel-competency/msel-competency-data.service';
+import { MselCompetencyQuery } from 'src/app/data/msel-competency/msel-competency.query';
+import { TeamCompetencyDataService } from 'src/app/data/team-competency/team-competency-data.service';
+import { TeamCompetencyQuery } from 'src/app/data/team-competency/team-competency.query';
+import { CompetencyFrameworkDataService } from 'src/app/data/competency-framework/competency-framework-data.service';
+import { CompetencyFrameworkQuery } from 'src/app/data/competency-framework/competency-framework.query';
+import { MatDialog } from '@angular/material/dialog';
+import { DialogService } from 'src/app/services/dialog/dialog.service';
+import { TeamCompetencyPropagateDialogComponent, TeamCompetencyPropagateData } from '../team-competency-propagate-dialog/team-competency-propagate-dialog.component';
+import { DataFieldDataService } from 'src/app/data/data-field/data-field-data.service';
+import { DataFieldQuery } from 'src/app/data/data-field/data-field.query';
+import { DataValueDataService } from 'src/app/data/data-value/data-value-data.service';
+import { DataValueQuery } from 'src/app/data/data-value/data-value.query';
+import { MatTableDataSource } from '@angular/material/table';
+import { MatPaginator } from '@angular/material/paginator';
+import { MatSort, Sort } from '@angular/material/sort';
+
+@Component({
+ selector: 'app-msel-competencies',
+ templateUrl: './msel-competencies.component.html',
+ styleUrls: ['./msel-competencies.component.scss'],
+ animations: [
+ trigger('detailExpand', [
+ state('collapsed', style({ height: '0px', minHeight: '0', visibility: 'hidden' })),
+ state('expanded', style({ height: '*', visibility: 'visible' })),
+ transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
+ ]),
+ ],
+ standalone: false
+})
+export class MselCompetenciesComponent implements OnDestroy, OnInit, AfterViewInit {
+ @Input() loggedInUserId: string;
+ @ViewChild('competencyPaginator') competencyPaginator: MatPaginator;
+ @ViewChild('workRolePaginator') workRolePaginator: MatPaginator;
+ @ViewChild('workRoleSort') workRoleSort: MatSort;
+ @ViewChild('poolSort') poolSort: MatSort;
+ msel = new MselPlus();
+ mselCompetencyList: MselCompetency[] = [];
+ teamCompetencyList: TeamCompetency[] = [];
+ private allDataFields: DataField[] = [];
+ private allDataValues: DataValue[] = [];
+ // Frameworks
+ frameworks: CompetencyFramework[] = [];
+ selectedFrameworkId = '';
+ selectedFramework: CompetencyFramework = null;
+ frameworkWorkRoles: Competency[] = [];
+ private competencyTypeMap = new Map();
+ private competencyById = new Map();
+
+ // --- Panel 1: Add Competencies (browser) ---
+ workRoleFilterControl = new UntypedFormControl();
+ workRoleFilterString = '';
+ competencySearchControl = new UntypedFormControl();
+ competencySearchString = '';
+ workRoleDataSource = new MatTableDataSource([]);
+ workRoleDisplayedColumns: string[] = ['check', 'idNumber', 'shortName', 'category'];
+ workRoleCategoryFilter = '';
+ workRoleCategories: string[] = [];
+ addPanelExpanded = false;
+ browserExpandedId: string = null;
+ browserAutoExpandedIds = new Set();
+ browserChildTypeFilter = '';
+ browserChildTypes: string[] = [];
+ browserChildrenPageIndex = 0;
+ browserChildrenPageSize = 25;
+
+ // --- Panel 2: MSEL Competencies (pool) ---
+ filterControl = new UntypedFormControl();
+ filterString = '';
+ dataSource = new MatTableDataSource([]);
+ displayedColumns: string[] = ['select', 'action', 'idNumber', 'type', 'framework', 'shortName', 'teams', 'events'];
+ selection = new SelectionModel(true, []);
+ typeFilter = '';
+ competencyTypes: string[] = [];
+ frameworkFilter = '';
+ poolFrameworks: { id: string, name: string, version: string }[] = [];
+ // Teams
+ mselTeams: Team[] = [];
+ teamsByCompetency = new Map();
+ expandedCompetencyId: string = null;
+
+ private unsubscribe$ = new Subject();
+
+ constructor(
+ private mselQuery: MselQuery,
+ private mselCompetencyDataService: MselCompetencyDataService,
+ private mselCompetencyQuery: MselCompetencyQuery,
+ private teamCompetencyDataService: TeamCompetencyDataService,
+ private teamCompetencyQuery: TeamCompetencyQuery,
+ private competencyFrameworkDataService: CompetencyFrameworkDataService,
+ private competencyFrameworkQuery: CompetencyFrameworkQuery,
+ private competencyFrameworkService: CompetencyFrameworkService,
+ private permissionDataService: PermissionDataService,
+ public dialog: MatDialog,
+ public dialogService: DialogService,
+ private dataFieldDataService: DataFieldDataService,
+ private dataFieldQuery: DataFieldQuery,
+ private dataValueDataService: DataValueDataService,
+ private dataValueQuery: DataValueQuery
+ ) {
+ (this.mselQuery.selectActive() as Observable).pipe(takeUntil(this.unsubscribe$)).subscribe(msel => {
+ if (msel) {
+ const isNewMsel = this.msel.id !== msel.id;
+ Object.assign(this.msel, msel);
+ this.mselTeams = msel.teams || [];
+ if (isNewMsel) {
+ this.mselCompetencyDataService.loadByMsel(msel.id);
+ this.teamCompetencyDataService.loadByMsel(msel.id);
+ }
+ }
+ });
+ this.mselCompetencyQuery.selectAll().pipe(takeUntil(this.unsubscribe$)).subscribe(mselCompetencies => {
+ this.mselCompetencyList = mselCompetencies;
+ if (!this.selectedFrameworkId) {
+ this.addPanelExpanded = mselCompetencies.length === 0;
+ }
+ this.selection.clear();
+ this.buildPoolTypes();
+ this.applyFilter();
+ });
+ this.teamCompetencyQuery.selectAll().pipe(takeUntil(this.unsubscribe$)).subscribe(teamCompetencies => {
+ this.teamCompetencyList = teamCompetencies;
+ this.buildTeamsByCompetency();
+ });
+ this.competencyFrameworkQuery.selectAll().pipe(takeUntil(this.unsubscribe$)).subscribe(frameworks => {
+ this.frameworks = frameworks;
+ });
+ this.dataFieldQuery.selectAll().pipe(takeUntil(this.unsubscribe$)).subscribe(fields => {
+ this.allDataFields = fields;
+ });
+ this.dataValueQuery.selectAll().pipe(takeUntil(this.unsubscribe$)).subscribe(values => {
+ this.allDataValues = values;
+ });
+ this.filterControl.valueChanges.pipe(takeUntil(this.unsubscribe$)).subscribe(term => {
+ this.filterString = term;
+ this.applyFilter();
+ });
+ this.workRoleFilterControl.valueChanges.pipe(takeUntil(this.unsubscribe$)).subscribe(term => {
+ this.workRoleFilterString = term;
+ this.applyWorkRoleFilter();
+ });
+ this.competencySearchControl.valueChanges.pipe(takeUntil(this.unsubscribe$)).subscribe(term => {
+ this.competencySearchString = term;
+ this.applyWorkRoleFilter();
+ });
+ }
+
+ ngOnInit() {
+ this.permissionDataService.load().pipe(takeUntil(this.unsubscribe$)).subscribe();
+ this.competencyFrameworkDataService.load();
+ }
+
+ ngAfterViewInit() {
+ if (this.poolSort) {
+ this.dataSource.sort = this.poolSort;
+ }
+ }
+
+ canEditMsel(): boolean {
+ return this.permissionDataService.hasPermission(SystemPermission.ManageMsels) ||
+ this.msel.hasRole(this.loggedInUserId, '').owner;
+ }
+
+ // =============================================
+ // Framework selection
+ // =============================================
+
+ onFrameworkChange(frameworkId: string) {
+ this.selectedFrameworkId = frameworkId;
+ this.browserExpandedId = null;
+ this.browserChildTypeFilter = '';
+ if (frameworkId) {
+ this.addPanelExpanded = true;
+ }
+ if (!frameworkId) {
+ this.selectedFramework = null;
+ this.frameworkWorkRoles = [];
+ this.workRoleDataSource.data = [];
+ return;
+ }
+ this.competencyFrameworkService.getCompetencyFramework(frameworkId)
+ .pipe(take(1))
+ .subscribe(fw => {
+ this.selectedFramework = fw;
+ const allComps = fw.competencies || [];
+ this.buildTypeMapFromComps(fw, allComps);
+ this.frameworkWorkRoles = allComps.filter(c => this.competencyTypeMap.get(c.id) === 'Work Role');
+ this.workRoleCategories = [...new Set(this.frameworkWorkRoles.map(wr => this.getWorkRoleCategory(wr)).filter(c => c))].sort();
+ this.workRoleCategoryFilter = '';
+ this.workRoleFilterControl.setValue('');
+ this.applyWorkRoleFilter();
+ setTimeout(() => {
+ if (this.workRolePaginator) {
+ this.workRoleDataSource.paginator = this.workRolePaginator;
+ }
+ });
+ });
+ }
+
+ private buildTypeMapFromComps(fw: CompetencyFramework, comps: Competency[]) {
+ this.competencyTypeMap.clear();
+ this.competencyById.clear();
+ const byId = new Map();
+ comps.forEach(c => {
+ byId.set(c.id, c);
+ this.competencyById.set(c.id, c);
+ });
+ const hasHierarchy = comps.some(c => c.parentId && byId.has(c.parentId));
+ const taxonomyLevels = (fw.taxonomies || '')
+ .split(',')
+ .map(t => t.trim())
+ .filter(t => t.length > 0);
+
+ for (const c of comps) {
+ const idType = this.deriveTypeFromId(c.idNumber);
+ if (idType !== 'Other') {
+ this.competencyTypeMap.set(c.id, idType);
+ } else if (hasHierarchy && taxonomyLevels.length > 0) {
+ const depth = this.getDepth(c, byId);
+ this.competencyTypeMap.set(c.id, taxonomyLevels[Math.min(depth, taxonomyLevels.length - 1)]);
+ } else if (hasHierarchy) {
+ const isRoot = !c.parentId || !byId.has(c.parentId);
+ this.competencyTypeMap.set(c.id, isRoot ? 'Category' : 'Other');
+ } else {
+ this.competencyTypeMap.set(c.id, 'Other');
+ }
+ }
+ }
+
+ private deriveTypeFromId(idNumber: string): string {
+ if (!idNumber) return 'Other';
+ if (idNumber.includes('WRL')) return 'Work Role';
+ if (/^[TKSA][\d-]/.test(idNumber)) {
+ const prefixMap: Record = { 'T': 'Task', 'K': 'Knowledge', 'S': 'Skill', 'A': 'Ability' };
+ return prefixMap[idNumber.charAt(0)] || 'Other';
+ }
+ if (/^[A-Z]{2}-[A-Z]{3}-\d+$/.test(idNumber)) return 'Work Role';
+ if (/^[A-Z]{3}$/.test(idNumber)) return 'Specialty Area';
+ if (/^[A-Z]{2}$/.test(idNumber)) return 'Category';
+ return 'Other';
+ }
+
+ private getDepth(comp: Competency, byId: Map): number {
+ let depth = 0;
+ let current = comp;
+ while (current.parentId && byId.has(current.parentId)) {
+ depth++;
+ current = byId.get(current.parentId);
+ }
+ return depth;
+ }
+
+ getWorkRoleCategory(workRole: Competency): string {
+ let current = workRole;
+ while (current.parentId && this.competencyById.has(current.parentId)) {
+ current = this.competencyById.get(current.parentId);
+ }
+ if (current.id === workRole.id) return '';
+ return current.shortName || current.idNumber || '';
+ }
+
+ getCompetencyType(comp: Competency): string {
+ return this.competencyTypeMap.get(comp.id) || '';
+ }
+
+ // =============================================
+ // Panel 1: Add Competencies — work role browser
+ // =============================================
+
+ isInPool(competencyId: string): boolean {
+ return this.mselCompetencyList.some(mc => mc.competencyId === competencyId);
+ }
+
+ togglePoolItem(comp: Competency) {
+ if (this.isInPool(comp.id)) {
+ const mc = this.mselCompetencyList.find(m => m.competencyId === comp.id);
+ if (mc) {
+ const msg = this.buildRemoveMessage(comp.idNumber, comp.shortName, this.getEventCount(mc), this.getDataFieldOptionCount(comp.idNumber));
+ this.dialogService.confirm('Remove Competency', msg).subscribe(result => {
+ if (result['confirm']) {
+ this.removeCompetencyReferences(comp.idNumber);
+ this.mselCompetencyDataService.delete(mc.id);
+ }
+ });
+ }
+ } else {
+ this.mselCompetencyDataService.add({
+ mselId: this.msel.id,
+ competencyId: comp.id,
+ } as MselCompetency);
+ }
+ }
+
+ onWorkRoleCategoryChange(category: string) {
+ this.workRoleCategoryFilter = category;
+ this.applyWorkRoleFilter();
+ }
+
+ applyWorkRoleFilter() {
+ let filtered = [...this.frameworkWorkRoles];
+ if (this.workRoleCategoryFilter) {
+ filtered = filtered.filter(wr => this.getWorkRoleCategory(wr) === this.workRoleCategoryFilter);
+ }
+ if (this.workRoleFilterString) {
+ const fs = this.workRoleFilterString.toLowerCase();
+ filtered = filtered.filter(c =>
+ c.idNumber?.toLowerCase().includes(fs) ||
+ c.shortName?.toLowerCase().includes(fs) ||
+ c.description?.toLowerCase().includes(fs));
+ }
+ // Competency search: filter work roles to those with matching children, auto-expand
+ this.browserAutoExpandedIds.clear();
+ if (this.competencySearchString) {
+ const cs = this.competencySearchString.toLowerCase();
+ filtered = filtered.filter(wr => {
+ const children = this.getRelatedCompetencies(wr);
+ return children.some(c =>
+ c.idNumber?.toLowerCase().includes(cs) ||
+ c.shortName?.toLowerCase().includes(cs) ||
+ c.description?.toLowerCase().includes(cs));
+ });
+ for (const wr of filtered) {
+ this.browserAutoExpandedIds.add(wr.id);
+ }
+ }
+ filtered.sort((a, b) => (a.idNumber || '').localeCompare(b.idNumber || ''));
+ this.workRoleDataSource.data = filtered;
+ this.workRoleDataSource.sortingDataAccessor = (item: Competency, property: string) => {
+ switch (property) {
+ case 'category': return this.getWorkRoleCategory(item)?.toLowerCase() || '';
+ default: return (item[property] || '').toString().toLowerCase();
+ }
+ };
+ setTimeout(() => {
+ if (this.workRoleSort) {
+ this.workRoleDataSource.sort = this.workRoleSort;
+ }
+ });
+ }
+
+ // --- Browser expand: child TKSAs ---
+
+ isBrowserExpanded(workRole: Competency): boolean {
+ return this.browserExpandedId === workRole.id || this.browserAutoExpandedIds.has(workRole.id);
+ }
+
+ toggleBrowserExpand(workRole: Competency) {
+ if (this.browserAutoExpandedIds.size > 0) {
+ // In search mode: toggle individual within auto-expanded set
+ if (this.browserAutoExpandedIds.has(workRole.id)) {
+ this.browserAutoExpandedIds.delete(workRole.id);
+ } else {
+ this.browserAutoExpandedIds.add(workRole.id);
+ }
+ this.browserExpandedId = null;
+ } else if (this.browserExpandedId === workRole.id) {
+ this.browserExpandedId = null;
+ } else {
+ this.browserExpandedId = workRole.id;
+ this.browserChildTypeFilter = '';
+ this.browserChildrenPageIndex = 0;
+ this.buildBrowserChildTypes(workRole);
+ }
+ }
+
+ private buildBrowserChildTypes(workRole: Competency) {
+ const children = this.getRelatedCompetencies(workRole);
+ const types = new Set();
+ for (const c of children) {
+ const t = this.competencyTypeMap.get(c.id) || this.deriveTypeFromId(c.idNumber);
+ if (t && t !== 'Other' && t !== 'Work Role') types.add(t);
+ }
+ this.browserChildTypes = [...types].sort();
+ }
+
+ private getRelatedCompetencies(comp: Competency): Competency[] {
+ const resultMap = new Map();
+ for (const c of this.competencyById.values()) {
+ if (c.parentId === comp.id) {
+ resultMap.set(c.id, c);
+ }
+ }
+ if (comp.relatedIdNumbers?.length > 0) {
+ const relatedSet = new Set(comp.relatedIdNumbers);
+ for (const c of this.competencyById.values()) {
+ if (c.idNumber && relatedSet.has(c.idNumber) && c.id !== comp.id) {
+ resultMap.set(c.id, c);
+ }
+ }
+ }
+ return [...resultMap.values()].sort((a, b) => (a.idNumber || '').localeCompare(b.idNumber || ''));
+ }
+
+ getFilteredChildren(workRole: Competency): Competency[] {
+ let children = this.getRelatedCompetencies(workRole);
+ if (this.browserChildTypeFilter) {
+ children = children.filter(c => {
+ const t = this.competencyTypeMap.get(c.id) || this.deriveTypeFromId(c.idNumber);
+ return t === this.browserChildTypeFilter;
+ });
+ }
+ if (this.competencySearchString) {
+ const cs = this.competencySearchString.toLowerCase();
+ children = children.filter(c =>
+ c.idNumber?.toLowerCase().includes(cs) ||
+ c.shortName?.toLowerCase().includes(cs) ||
+ c.description?.toLowerCase().includes(cs));
+ }
+ return children;
+ }
+
+ getPaginatedChildren(workRole: Competency): Competency[] {
+ const all = this.getFilteredChildren(workRole);
+ const start = this.browserChildrenPageIndex * this.browserChildrenPageSize;
+ return all.slice(start, start + this.browserChildrenPageSize);
+ }
+
+ onBrowserChildrenPage(event: any) {
+ this.browserChildrenPageIndex = event.pageIndex;
+ this.browserChildrenPageSize = event.pageSize;
+ }
+
+ onBrowserChildTypeChange(type: string) {
+ this.browserChildTypeFilter = type;
+ this.browserChildrenPageIndex = 0;
+ }
+
+ selectAllChildren(workRole: Competency) {
+ const children = this.getFilteredChildren(workRole);
+ for (const c of children) {
+ if (!this.isInPool(c.id)) {
+ this.mselCompetencyDataService.add({
+ mselId: this.msel.id,
+ competencyId: c.id,
+ } as MselCompetency);
+ }
+ }
+ }
+
+ deselectAllChildren(workRole: Competency) {
+ const children = this.getFilteredChildren(workRole);
+ const toRemove = children
+ .map(c => this.mselCompetencyList.find(m => m.competencyId === c.id))
+ .filter(mc => mc);
+ if (toRemove.length === 0) return;
+ const totalEvents = toRemove.reduce((sum, mc) => sum + this.getEventCount(mc), 0);
+ const totalOptions = toRemove.reduce((sum, mc) => sum + this.getDataFieldOptionCount(mc.competency?.idNumber), 0);
+ let msg = `Remove ${toRemove.length} competenc${toRemove.length === 1 ? 'y' : 'ies'} from this MSEL?`;
+ const parts: string[] = [];
+ if (totalEvents > 0) parts.push(`${totalEvents} scenario event reference${totalEvents === 1 ? '' : 's'}`);
+ if (totalOptions > 0) parts.push(`${totalOptions} data field option${totalOptions === 1 ? '' : 's'}`);
+ if (parts.length > 0) {
+ msg += ` ${parts.join(' and ')} will also be removed.`;
+ }
+ this.dialogService.confirm('Remove Competencies', msg).subscribe(result => {
+ if (result['confirm']) {
+ for (const mc of toRemove) {
+ if (mc.competency?.idNumber) {
+ this.removeCompetencyReferences(mc.competency.idNumber);
+ }
+ this.mselCompetencyDataService.delete(mc.id);
+ }
+ }
+ });
+ }
+
+ allChildrenSelected(workRole: Competency): boolean {
+ const children = this.getFilteredChildren(workRole);
+ return children.length > 0 && children.every(c => this.isInPool(c.id));
+ }
+
+ someChildrenSelected(workRole: Competency): boolean {
+ const children = this.getFilteredChildren(workRole);
+ const selectedCount = children.filter(c => this.isInPool(c.id)).length;
+ return selectedCount > 0 && selectedCount < children.length;
+ }
+
+ // --- Select all / deselect all (work role browser) ---
+
+ isAllWorkRolesSelected(): boolean {
+ return this.workRoleDataSource.data.length > 0 && this.workRoleDataSource.data.every(wr => this.isInPool(wr.id));
+ }
+
+ hasWorkRoleSelection(): boolean {
+ return this.workRoleDataSource.data.some(wr => this.isInPool(wr.id));
+ }
+
+ toggleAllWorkRoles() {
+ const allSelected = this.isAllWorkRolesSelected();
+ if (allSelected) {
+ // Remove all visible work roles
+ const toRemove = this.workRoleDataSource.data
+ .map(wr => this.mselCompetencyList.find(mc => mc.competencyId === wr.id))
+ .filter(mc => mc);
+ if (toRemove.length === 0) return;
+ const totalEvents = toRemove.reduce((sum, mc) => sum + this.getEventCount(mc), 0);
+ const totalOptions = toRemove.reduce((sum, mc) => sum + this.getDataFieldOptionCount(mc.competency?.idNumber), 0);
+ let msg = `Remove ${toRemove.length} work role${toRemove.length === 1 ? '' : 's'} from this MSEL?`;
+ const parts: string[] = [];
+ if (totalEvents > 0) parts.push(`${totalEvents} scenario event reference${totalEvents === 1 ? '' : 's'}`);
+ if (totalOptions > 0) parts.push(`${totalOptions} data field option${totalOptions === 1 ? '' : 's'}`);
+ if (parts.length > 0) {
+ msg += ` ${parts.join(' and ')} will also be removed.`;
+ }
+ this.dialogService.confirm('Remove Work Roles', msg).subscribe(result => {
+ if (result['confirm']) {
+ for (const mc of toRemove) {
+ if (mc.competency?.idNumber) {
+ this.removeCompetencyReferences(mc.competency.idNumber);
+ }
+ this.mselCompetencyDataService.delete(mc.id);
+ }
+ }
+ });
+ } else {
+ // Add all visible work roles
+ for (const wr of this.workRoleDataSource.data) {
+ if (!this.isInPool(wr.id)) {
+ this.mselCompetencyDataService.add({
+ mselId: this.msel.id,
+ competencyId: wr.id,
+ } as MselCompetency);
+ }
+ }
+ }
+ }
+
+ // =============================================
+ // Panel 2: MSEL Competencies — pool
+ // =============================================
+
+ private buildPoolTypes() {
+ const types = new Set();
+ const fwMap = new Map();
+ for (const mc of this.mselCompetencyList) {
+ const t = this.competencyTypeMap.get(mc.competencyId) || this.deriveTypeFromId(mc.competency?.idNumber);
+ if (t && t !== 'Other') types.add(t);
+ const fwId = mc.competency?.competencyFrameworkId;
+ if (fwId && !fwMap.has(fwId)) {
+ const fw = this.frameworks.find(f => f.id === fwId);
+ if (fw) fwMap.set(fwId, { name: fw.name, version: fw.version || '' });
+ }
+ }
+ this.competencyTypes = [...types].sort();
+ this.poolFrameworks = [...fwMap.entries()].map(([id, fw]) => ({ id, name: fw.name, version: fw.version })).sort((a, b) => a.name.localeCompare(b.name));
+ }
+
+ onTypeFilterChange(type: string) {
+ this.typeFilter = type;
+ this.applyFilter();
+ }
+
+ onFrameworkFilterChange(frameworkId: string) {
+ this.frameworkFilter = frameworkId;
+ this.applyFilter();
+ }
+
+ applyFilter() {
+ let filtered = [...this.mselCompetencyList];
+ if (this.frameworkFilter) {
+ filtered = filtered.filter(mc => mc.competency?.competencyFrameworkId === this.frameworkFilter);
+ }
+ if (this.typeFilter) {
+ filtered = filtered.filter(mc => {
+ const t = this.competencyTypeMap.get(mc.competencyId) || this.deriveTypeFromId(mc.competency?.idNumber);
+ return t === this.typeFilter;
+ });
+ }
+ if (this.filterString) {
+ const term = this.filterString.toLowerCase();
+ filtered = filtered.filter(mc =>
+ mc.competency?.idNumber?.toLowerCase().includes(term) ||
+ mc.competency?.shortName?.toLowerCase().includes(term) ||
+ mc.competency?.description?.toLowerCase().includes(term));
+ }
+ filtered.sort((a, b) => (a.competency?.idNumber || '').localeCompare(b.competency?.idNumber || ''));
+ this.dataSource.data = filtered;
+ this.dataSource.sortingDataAccessor = (item: MselCompetency, property: string) => {
+ switch (property) {
+ case 'idNumber': return item.competency?.idNumber?.toLowerCase() || '';
+ case 'type': return this.getMselCompetencyType(item)?.toLowerCase() || '';
+ case 'framework': return this.getFrameworkName(item)?.toLowerCase() || '';
+ case 'shortName': return item.competency?.shortName?.toLowerCase() || '';
+ case 'teams': return this.getTeamNames(item)?.toLowerCase() || '';
+ case 'events': return this.getEventCount(item);
+ default: return '';
+ }
+ };
+ setTimeout(() => {
+ if (this.competencyPaginator) {
+ this.dataSource.paginator = this.competencyPaginator;
+ }
+ if (this.poolSort) {
+ this.dataSource.sort = this.poolSort;
+ }
+ });
+ }
+
+ removeMselCompetency(mc: MselCompetency) {
+ const idNumber = mc.competency?.idNumber || '';
+ const name = mc.competency?.shortName || '';
+ const msg = this.buildRemoveMessage(idNumber, name, this.getEventCount(mc), this.getDataFieldOptionCount(idNumber));
+ this.dialogService.confirm('Remove Competency', msg).subscribe(result => {
+ if (result['confirm']) {
+ this.removeCompetencyReferences(idNumber);
+ this.mselCompetencyDataService.delete(mc.id);
+ }
+ });
+ }
+
+ getMselCompetencyType(mc: MselCompetency): string {
+ return this.competencyTypeMap.get(mc.competencyId) || this.deriveTypeFromId(mc.competency?.idNumber);
+ }
+
+ getFrameworkName(mc: MselCompetency): string {
+ const fwId = mc.competency?.competencyFrameworkId;
+ if (!fwId) return '';
+ const fw = this.frameworks.find(f => f.id === fwId);
+ return fw ? fw.name + ' (' + fw.version + ')' : '';
+ }
+
+ private get competencyFieldIds(): Set {
+ return new Set(
+ this.allDataFields
+ .filter(df => df.mselId === this.msel.id && df.dataType === DataFieldType.Competency)
+ .map(df => df.id)
+ );
+ }
+
+ getEventCount(mc: MselCompetency): number {
+ const idNumber = mc.competency?.idNumber;
+ if (!idNumber) return 0;
+ const fieldIds = this.competencyFieldIds;
+ if (fieldIds.size === 0) return 0;
+ const seen = new Set();
+ let count = 0;
+ for (const dv of this.allDataValues) {
+ if (fieldIds.has(dv.dataFieldId) && dv.value && dv.scenarioEventId && !seen.has(dv.scenarioEventId)) {
+ const ids = dv.value.split(',').map(s => s.trim());
+ if (ids.includes(idNumber)) {
+ count++;
+ seen.add(dv.scenarioEventId);
+ }
+ }
+ }
+ return count;
+ }
+
+ private getDataFieldOptionCount(idNumber: string): number {
+ if (!idNumber) return 0;
+ return this.allDataFields
+ .filter(df => df.mselId === this.msel.id && df.dataType === DataFieldType.Competency)
+ .filter(df => (df.dataOptions || []).some(opt => opt.optionName === idNumber))
+ .length;
+ }
+
+ private buildRemoveMessage(idNumber: string, name: string, eventCount: number, optionCount: number): string {
+ const parts: string[] = [];
+ if (eventCount > 0) {
+ parts.push(`${eventCount} scenario event${eventCount === 1 ? '' : 's'}`);
+ }
+ if (optionCount > 0) {
+ parts.push(`${optionCount} data field${optionCount === 1 ? '' : 's'}`);
+ }
+ if (parts.length > 0) {
+ return `The following competency is referenced by ${parts.join(' and ')}. Those references will be removed. Continue?\n\n${idNumber} — ${name}`;
+ }
+ return `Remove the following competency from this MSEL?\n\n${idNumber} — ${name}`;
+ }
+
+ private removeCompetencyReferences(idNumber: string) {
+ if (!idNumber) return;
+ const competencyFields = this.allDataFields
+ .filter(df => df.mselId === this.msel.id && df.dataType === DataFieldType.Competency);
+ // Remove from DataOptions on competency-type data fields
+ for (const df of competencyFields) {
+ const filtered = (df.dataOptions || []).filter(opt => opt.optionName !== idNumber);
+ if (filtered.length !== (df.dataOptions || []).length) {
+ this.dataFieldDataService.updateDataField({ ...df, dataOptions: filtered });
+ }
+ }
+ // Remove from DataValues on scenario events
+ const fieldIds = new Set(competencyFields.map(df => df.id));
+ for (const dv of this.allDataValues) {
+ if (fieldIds.has(dv.dataFieldId) && dv.value) {
+ const ids = dv.value.split(',').map(s => s.trim()).filter(id => id && id !== idNumber);
+ const newValue = ids.join(', ');
+ if (newValue !== dv.value) {
+ this.dataValueDataService.updateDataValue({ ...dv, value: newValue });
+ }
+ }
+ }
+ }
+
+ // --- Team mapping ---
+
+ private buildTeamsByCompetency() {
+ this.teamsByCompetency.clear();
+ for (const tc of this.teamCompetencyList) {
+ const arr = this.teamsByCompetency.get(tc.competencyId) || [];
+ arr.push(tc.teamId);
+ this.teamsByCompetency.set(tc.competencyId, arr);
+ }
+ }
+
+ getTeamNames(mc: MselCompetency): string {
+ const teamIds = this.teamsByCompetency.get(mc.competencyId) || [];
+ if (teamIds.length === 0) return '';
+ return teamIds
+ .map(tid => this.mselTeams.find(t => t.id === tid)?.shortName || '')
+ .filter(n => n)
+ .join(', ');
+ }
+
+ // --- Pool row expand: team assignment ---
+
+ toggleExpand(mc: MselCompetency) {
+ this.expandedCompetencyId = this.expandedCompetencyId === mc.competencyId ? null : mc.competencyId;
+ }
+
+ getAvailableTeams(mc: MselCompetency): Team[] {
+ const assignedIds = this.teamsByCompetency.get(mc.competencyId) || [];
+ return this.mselTeams.filter(t => !assignedIds.includes(t.id));
+ }
+
+ getAssignedTeams(mc: MselCompetency): Team[] {
+ const assignedIds = this.teamsByCompetency.get(mc.competencyId) || [];
+ return this.mselTeams.filter(t => assignedIds.includes(t.id));
+ }
+
+ addTeamToCompetency(mc: MselCompetency, team: Team) {
+ this.teamCompetencyDataService.add({
+ teamId: team.id,
+ competencyId: mc.competencyId,
+ } as TeamCompetency);
+ // Offer to add to related competencies on the MSEL
+ const children = this.getPoolChildren(mc);
+ const unassigned = children.filter(child => {
+ const assignedIds = this.teamsByCompetency.get(child.competencyId) || [];
+ return !assignedIds.includes(team.id);
+ });
+ if (unassigned.length > 0) {
+ this.openPropagateDialog(team, 'add', unassigned);
+ }
+ }
+
+ removeTeamFromCompetency(mc: MselCompetency, team: Team) {
+ const tc = this.teamCompetencyList.find(t => t.teamId === team.id && t.competencyId === mc.competencyId);
+ if (tc) {
+ this.teamCompetencyDataService.delete(tc.id);
+ // Offer to remove from related competencies on the MSEL
+ const children = this.getPoolChildren(mc);
+ const assigned = children.filter(child => {
+ const assignedIds = this.teamsByCompetency.get(child.competencyId) || [];
+ return assignedIds.includes(team.id);
+ });
+ if (assigned.length > 0) {
+ this.openPropagateDialog(team, 'remove', assigned);
+ }
+ }
+ }
+
+ private openPropagateDialog(team: Team, action: 'add' | 'remove', competencies: MselCompetency[]) {
+ const dialogRef = this.dialog.open(TeamCompetencyPropagateDialogComponent, {
+ width: '600px',
+ maxWidth: '95vw',
+ maxHeight: '80vh',
+ data: {
+ teamName: team.shortName || team.name,
+ action,
+ competencies,
+ } as TeamCompetencyPropagateData,
+ });
+ dialogRef.afterClosed().subscribe((selected: MselCompetency[] | null) => {
+ if (!selected || selected.length === 0) return;
+ if (action === 'add') {
+ for (const child of selected) {
+ this.teamCompetencyDataService.add({
+ teamId: team.id,
+ competencyId: child.competencyId,
+ } as TeamCompetency);
+ }
+ } else {
+ for (const child of selected) {
+ const childTc = this.teamCompetencyList.find(t => t.teamId === team.id && t.competencyId === child.competencyId);
+ if (childTc) {
+ this.teamCompetencyDataService.delete(childTc.id);
+ }
+ }
+ }
+ });
+ }
+
+ private getPoolChildren(mc: MselCompetency): MselCompetency[] {
+ const compId = mc.competencyId;
+ const relatedIds = new Set(mc.competency?.relatedIdNumbers || []);
+ return this.mselCompetencyList.filter(other => {
+ if (other.competencyId === compId) return false;
+ if (other.competency?.parentId === compId) return true;
+ if (other.competency?.idNumber && relatedIds.has(other.competency.idNumber)) return true;
+ return false;
+ });
+ }
+
+ // --- Select all / deselect all (pool) ---
+
+ isAllSelected(): boolean {
+ return this.selection.selected.length === this.dataSource.data.length && this.dataSource.data.length > 0;
+ }
+
+ toggleAllRows() {
+ if (this.isAllSelected()) {
+ this.selection.clear();
+ } else {
+ this.dataSource.data.forEach(row => this.selection.select(row));
+ }
+ }
+
+ removeSelected() {
+ const selected = this.selection.selected;
+ if (selected.length === 0) return;
+ const totalEvents = selected.reduce((sum, mc) => sum + this.getEventCount(mc), 0);
+ const totalOptions = selected.reduce((sum, mc) => sum + this.getDataFieldOptionCount(mc.competency?.idNumber), 0);
+ let msg = `Remove ${selected.length} competenc${selected.length === 1 ? 'y' : 'ies'} from this MSEL?`;
+ const parts: string[] = [];
+ if (totalEvents > 0) parts.push(`${totalEvents} scenario event reference${totalEvents === 1 ? '' : 's'}`);
+ if (totalOptions > 0) parts.push(`${totalOptions} data field option${totalOptions === 1 ? '' : 's'}`);
+ if (parts.length > 0) {
+ msg += ` ${parts.join(' and ')} will also be removed.`;
+ }
+ this.dialogService.confirm('Remove Selected', msg).subscribe(result => {
+ if (result['confirm']) {
+ for (const mc of selected) {
+ if (mc.competency?.idNumber) {
+ this.removeCompetencyReferences(mc.competency.idNumber);
+ }
+ this.mselCompetencyDataService.delete(mc.id);
+ }
+ this.selection.clear();
+ }
+ });
+ }
+
+ ngOnDestroy() {
+ this.unsubscribe$.next(null);
+ this.unsubscribe$.complete();
+ }
+}
diff --git a/src/app/components/msel-info/msel-info.component.html b/src/app/components/msel-info/msel-info.component.html
index 8fc548ac..bd592ecc 100755
--- a/src/app/components/msel-info/msel-info.component.html
+++ b/src/app/components/msel-info/msel-info.component.html
@@ -333,28 +333,144 @@
{{ starterUrl }}
+
+
+
+
+
+
+ @if (workRoleCompetencies.length > 0) {
+
+
+
+
+ Work Roles ({{ workRoleCompetencies.length }})
+
+
+
+ @for (c of workRoleCompetencies; track c.id) {
+
+ {{ c.idNumber }}
+
+ }
+
+
+ }
+ @if (nonWorkRoleCompetencies.length > 0) {
+
+
+
+
+ Competencies ({{ nonWorkRoleCompetencies.length }})
+
+
+
+ @for (c of nonWorkRoleCompetencies; track c.id) {
+
+ {{ c.idNumber }}
+
+ }
+
+
+ }
+
+
Manage on the Competencies tab
+
+
+
+
+
+ Difficulty Rating
+
+
+ @for (level of educationalLevels; track level) {
+ {{ level }}
+ }
+
+
+
+ Exercise Purpose
+
+
+ @for (use of educationalUses; track use) {
+ {{ use }}
+ }
+
+
+
+ Exercise Mode
+
+
+ @for (mode of courseModes; track mode) {
+ {{ mode }}
+ }
+
+
+
+
+
+ Subject
+
+
+
+
+
+ Keywords
+
+
+
@if (canManageMsel()) {
-
- Header Row Metadata (Height) when exported as an Excel file
+ Header Row Metadata (Height)
}
-
diff --git a/src/app/components/msel-info/msel-info.component.scss b/src/app/components/msel-info/msel-info.component.scss
index 44aed21a..d2d6f6fe 100755
--- a/src/app/components/msel-info/msel-info.component.scss
+++ b/src/app/components/msel-info/msel-info.component.scss
@@ -81,6 +81,75 @@ a {
align-self: center;
}
+.competency-section {
+ width: 100%;
+ margin-bottom: 16px;
+ text-align: left;
+
+ .competency-header {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-weight: bold;
+ margin-bottom: 8px;
+ }
+
+ .competency-title {
+ margin-right: 8px;
+ }
+
+ .competency-count {
+ font-weight: normal;
+ font-size: 13px;
+ color: var(--mat-sys-on-surface-variant);
+ }
+
+ .competency-panels-row {
+ display: flex;
+ gap: 16px;
+ align-items: flex-start;
+ }
+
+ .competency-expansion {
+ flex: 1 1 50%;
+ background: transparent;
+ }
+
+ .competency-list {
+ max-height: 300px;
+ overflow: auto;
+ }
+
+ .competency-list-item {
+ display: flex;
+ padding: 4px 8px;
+ font-size: 13px;
+ border-bottom: 1px solid var(--mat-sys-outline-variant);
+
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+
+ .competency-list-id {
+ flex: 0 0 100px;
+ font-weight: 500;
+ white-space: nowrap;
+ }
+
+ .competency-list-name {
+ flex: 1 1 auto;
+ min-width: 0;
+ }
+
+ .competency-hint {
+ font-size: 12px;
+ color: var(--mat-sys-on-surface-variant);
+ margin-top: 4px;
+ }
+}
+
+
.url-form-field {
display: flex;
flex-direction: column;
@@ -139,6 +208,30 @@ a {
width: 60%;
}
+.section-header {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-weight: bold;
+ margin-bottom: 8px;
+ text-align: left;
+ width: 100%;
+}
+
+.lmt-dropdown-row {
+ display: flex;
+ gap: 16px;
+ width: 100%;
+}
+
+.lmt-dropdown-width {
+ width: 250px;
+}
+
+.meta-field-width {
+ width: 300px;
+}
+
.half-button {
margin-top: 18px;
margin-left: 40px;
diff --git a/src/app/components/msel-info/msel-info.component.ts b/src/app/components/msel-info/msel-info.component.ts
index c29e3e7f..d62e5d14 100755
--- a/src/app/components/msel-info/msel-info.component.ts
+++ b/src/app/components/msel-info/msel-info.component.ts
@@ -6,8 +6,11 @@ import { Subject, Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { TeamQuery } from 'src/app/data/team/team.query';
import { UserQuery } from 'src/app/data/user/user.query';
+import { UserDataService } from 'src/app/data/user/user-data.service';
import {
+ Competency,
DataField,
+ MselCompetency,
MselItemStatus,
MselPage,
MselUnit,
@@ -28,6 +31,12 @@ import { DataFieldQuery } from 'src/app/data/data-field/data-field.query';
import { MselPageDataService } from 'src/app/data/msel-page/msel-page-data.service';
import { MselPageQuery } from 'src/app/data/msel-page/msel-page.query';
import { MselUnitQuery } from 'src/app/data/msel-unit/msel-unit.query';
+import { MselCompetencyDataService } from 'src/app/data/msel-competency/msel-competency-data.service';
+import { MselCompetencyQuery } from 'src/app/data/msel-competency/msel-competency.query';
+import { CompetencyFrameworkQuery } from 'src/app/data/competency-framework/competency-framework.query';
+import { CompetencyFrameworkDataService } from 'src/app/data/competency-framework/competency-framework-data.service';
+import { MatDialog } from '@angular/material/dialog';
+import { CompetencyOptionsDialogComponent } from '../competency-options-dialog/competency-options-dialog.component';
import { AngularEditorConfig } from '@kolkov/angular-editor';
import { ComnSettingsService } from '@cmusei/crucible-common';
import { HttpClient } from '@angular/common/http';
@@ -54,6 +63,13 @@ export class MselInfoComponent implements OnDestroy, OnInit {
userList: User[] = [];
teamList: Team[] = [];
mselUnitList: MselUnit[] = [];
+ mselCompetencyList: MselCompetency[] = [];
+ workRoleCount = 0;
+ workRoleCompetencies: Competency[] = [];
+ nonWorkRoleCompetencies: Competency[] = [];
+ competencyFrameworkNames: string[] = [];
+ private competencyTypeCache = new Map();
+ creatorName = 'unknown';
scoringModelList: ScoringModel[] = [];
itemStatus: MselItemStatus[] = [
MselItemStatus.Pending,
@@ -63,8 +79,12 @@ export class MselInfoComponent implements OnDestroy, OnInit {
MselItemStatus.Complete,
MselItemStatus.Archived,
];
+ educationalLevels: string[] = ['Beginner', 'Intermediate', 'Advanced'];
+ educationalUses: string[] = ['Assessment', 'Instruction', 'Professional Support'];
+ courseModes: string[] = ['Online', 'Onsite', 'Blended'];
viewUrl: string;
starterUrl: string;
+ assessorUrl: string;
mselPages: MselPage[] = [];
newMselPage = {} as MselPage;
changedMselPage = {} as MselPage;
@@ -142,6 +162,7 @@ export class MselInfoComponent implements OnDestroy, OnInit {
public dialogService: DialogService,
private teamQuery: TeamQuery,
private userQuery: UserQuery,
+ private userDataService: UserDataService,
private dataFieldQuery: DataFieldQuery,
private mselDataService: MselDataService,
private mselQuery: MselQuery,
@@ -150,6 +171,11 @@ export class MselInfoComponent implements OnDestroy, OnInit {
private mselPageDataService: MselPageDataService,
private mselPageQuery: MselPageQuery,
private mselUnitQuery: MselUnitQuery,
+ private mselCompetencyDataService: MselCompetencyDataService,
+ private mselCompetencyQuery: MselCompetencyQuery,
+ private competencyFrameworkQuery: CompetencyFrameworkQuery,
+ private competencyFrameworkDataService: CompetencyFrameworkDataService,
+ private dialog: MatDialog,
private permissionDataService: PermissionDataService,
private changeDetectorRef: ChangeDetectorRef,
private settingsService: ComnSettingsService,
@@ -168,7 +194,9 @@ export class MselInfoComponent implements OnDestroy, OnInit {
this.viewUrl = document.baseURI + 'msel/' + this.msel.id + '/view';
this.starterUrl =
document.baseURI + 'starter/?msel=' + this.msel.id;
+ this.assessorUrl = document.baseURI + 'assess/?msel=' + this.msel.id;
this.mselPageDataService.loadByMsel(msel.id);
+ this.mselCompetencyDataService.loadByMsel(msel.id);
this.newMselPage.mselId = msel.id;
}
this.savedStartTime = new Date(msel.startTime);
@@ -181,6 +209,7 @@ export class MselInfoComponent implements OnDestroy, OnInit {
}
// Fetch integration names for deployed integrations
this.fetchIntegrationNames();
+ this.resolveCreatorName();
}
});
// subscribe to MSEL loading flag
@@ -195,6 +224,7 @@ export class MselInfoComponent implements OnDestroy, OnInit {
.pipe(takeUntil(this.unsubscribe$))
.subscribe((users) => {
this.userList = users;
+ this.resolveCreatorName();
});
// subscribe to teams
this.teamQuery
@@ -210,6 +240,27 @@ export class MselInfoComponent implements OnDestroy, OnInit {
.subscribe((mselUnits) => {
this.mselUnitList = mselUnits;
});
+ // subscribe to mselCompetencies
+ this.mselCompetencyQuery
+ .selectAll()
+ .pipe(takeUntil(this.unsubscribe$))
+ .subscribe((mselCompetencies) => {
+ this.mselCompetencyList = mselCompetencies;
+ const isWorkRole = (mc: MselCompetency) => {
+ const id = mc.competency?.idNumber || '';
+ return id.includes('WRL') || /^[A-Z]{2}-[A-Z]{3}-\d+$/.test(id);
+ };
+ this.workRoleCompetencies = mselCompetencies
+ .filter(mc => isWorkRole(mc) && mc.competency)
+ .map(mc => mc.competency)
+ .sort((a, b) => (a.idNumber || '').localeCompare(b.idNumber || ''));
+ this.nonWorkRoleCompetencies = mselCompetencies
+ .filter(mc => !isWorkRole(mc) && mc.competency)
+ .map(mc => mc.competency)
+ .sort((a, b) => (a.idNumber || '').localeCompare(b.idNumber || ''));
+ this.workRoleCount = this.workRoleCompetencies.length;
+ this.updateFrameworkNames();
+ });
// subscribe to MselPages
this.mselPageQuery
.selectAll()
@@ -276,6 +327,59 @@ export class MselInfoComponent implements OnDestroy, OnInit {
.subscribe(() => {
this.changeDetectorRef.markForCheck();
});
+ this.competencyFrameworkDataService.load();
+ this.competencyFrameworkQuery.selectAll()
+ .pipe(takeUntil(this.unsubscribe$))
+ .subscribe(() => this.updateFrameworkNames());
+ }
+
+ private updateFrameworkNames() {
+ const fwIds = new Set();
+ for (const mc of this.mselCompetencyList) {
+ if (mc.competency?.competencyFrameworkId) {
+ fwIds.add(mc.competency.competencyFrameworkId);
+ }
+ }
+ const frameworks = this.competencyFrameworkQuery.getAll();
+ this.competencyFrameworkNames = [...fwIds]
+ .map(id => frameworks.find(f => f.id === id)?.name || '')
+ .filter(n => n)
+ .sort();
+ }
+
+ getCompetencyTooltip(comp: Competency): string {
+ return comp?.shortName || '';
+ }
+
+ private getCompetencyType(comp: Competency): string {
+ if (!comp || !comp.id) return '';
+
+ // Check cache first
+ if (this.competencyTypeCache.has(comp.id)) {
+ return this.competencyTypeCache.get(comp.id) || '';
+ }
+
+ // Derive from ID pattern
+ const idNumber = comp.idNumber || '';
+ let type = '';
+
+ if (idNumber.includes('WRL')) {
+ type = 'Work Role';
+ } else if (/^[TKSA][\d-]/.test(idNumber)) {
+ const prefixMap: Record = {
+ 'T': 'Task', 'K': 'Knowledge', 'S': 'Skill', 'A': 'Ability',
+ };
+ type = prefixMap[idNumber.charAt(0)] || '';
+ } else if (/^[A-Z]{2}-[A-Z]{3}-\d+$/.test(idNumber)) {
+ type = 'Work Role';
+ } else if (/^[A-Z]{3}$/.test(idNumber)) {
+ type = 'Specialty Area';
+ } else if (/^[A-Z]{2}$/.test(idNumber)) {
+ type = 'Category';
+ }
+
+ this.competencyTypeCache.set(comp.id, type);
+ return type;
}
getUserName(userId: string) {
@@ -283,6 +387,24 @@ export class MselInfoComponent implements OnDestroy, OnInit {
return user ? user.name : 'unknown';
}
+ private resolveCreatorName(): void {
+ if (!this.msel?.createdBy) {
+ this.creatorName = 'unknown';
+ return;
+ }
+ const user = this.userList.find(u => u.id === this.msel.createdBy);
+ if (user) {
+ this.creatorName = user.name;
+ } else {
+ this.userDataService.loadById(this.msel.createdBy)
+ .pipe(takeUntil(this.unsubscribe$))
+ .subscribe({
+ next: (u) => { this.creatorName = u?.name || 'unknown'; },
+ error: () => { this.creatorName = 'unknown'; }
+ });
+ }
+ }
+
saveChanges() {
this.mselDataService.updateMsel(this.msel);
this.isChanged = false;
@@ -318,6 +440,47 @@ export class MselInfoComponent implements OnDestroy, OnInit {
this.msel.hasRole(this.loggedInUserId, '').owner;
}
+ openCompetencyPicker(): void {
+ const existingIdNumbers = this.mselCompetencyList.map(mc => mc.competency?.idNumber).filter(Boolean);
+ const dialogRef = this.dialog.open(CompetencyOptionsDialogComponent, {
+ width: '900px',
+ maxWidth: '95vw',
+ maxHeight: '90vh',
+ data: {
+ dataFieldId: null,
+ dataOptions: existingIdNumbers.map(idNumber => ({ optionName: idNumber })),
+ canEdit: this.canEditMsel()
+ }
+ });
+ dialogRef.afterClosed().subscribe((updatedOptions) => {
+ if (!updatedOptions) return;
+ const newIdNumbers = new Set(updatedOptions.map((o: any) => o.optionName));
+ const existingMap = new Map();
+ for (const mc of this.mselCompetencyList) {
+ if (mc.competency?.idNumber) {
+ existingMap.set(mc.competency.idNumber, mc);
+ }
+ }
+ // Remove deselected
+ for (const [idNumber, mc] of existingMap) {
+ if (!newIdNumbers.has(idNumber)) {
+ this.mselCompetencyDataService.delete(mc.id);
+ }
+ }
+ // Add newly selected — need to resolve idNumber to competencyId
+ // The dialog returns DataOption-shaped objects with optionName = idNumber
+ // We need the competency ID, which we can get from the dialog's competencies
+ for (const opt of updatedOptions) {
+ if (!existingMap.has(opt.optionName) && opt.competencyId) {
+ this.mselCompetencyDataService.add({
+ mselId: this.msel.id,
+ competencyId: opt.competencyId
+ });
+ }
+ }
+ });
+ }
+
galleryWarningMessage() {
let warningMessage = '';
if (this.msel.useGallery && !this.msel.galleryExhibitId) {
diff --git a/src/app/components/msel-view/msel-view.component.html b/src/app/components/msel-view/msel-view.component.html
index 5ba47cd6..788ceacc 100755
--- a/src/app/components/msel-view/msel-view.component.html
+++ b/src/app/components/msel-view/msel-view.component.html
@@ -149,8 +149,8 @@
@if (df.dataType.toString() === 'Checkbox') {
-
+
}
@@ -162,12 +162,23 @@
}
+
+ @if (df.dataType.toString() === 'Competency') {
+
+
+ @for (compId of getDisplayValue(item, df.name).split(',').map(s => s.trim()).filter(s => s); track compId; let isLast = $last) {
+ {{ compId }} @if (!isLast) {, }
+ }
+
+
+ }
@if (df.dataType.toString() !== 'Html' &&
df.dataType.toString() !== 'Url' &&
df.dataType.toString() !== 'User' &&
df.dataType.toString() !== 'Checkbox' &&
- df.dataType.toString() !== 'DateTime') {
+ df.dataType.toString() !== 'DateTime' &&
+ df.dataType.toString() !== 'Competency') {
{{ getDisplayValue(item, df.name) }}
diff --git a/src/app/components/msel-view/msel-view.component.ts b/src/app/components/msel-view/msel-view.component.ts
index de1b14dd..9568ca67 100755
--- a/src/app/components/msel-view/msel-view.component.ts
+++ b/src/app/components/msel-view/msel-view.component.ts
@@ -5,6 +5,7 @@ import { Component, Input, OnDestroy, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Sort } from '@angular/material/sort';
import { Subject, Subscription, Observable, of } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
import {
takeUntil,
debounceTime,
@@ -15,10 +16,13 @@ import {
import { ComnSettingsService, Theme } from '@cmusei/crucible-common';
import {
Card,
+ Competency,
+ CompetencyFramework,
DataField,
DataValue,
Move,
Msel,
+ MselCompetency,
Organization,
ScenarioEvent,
Team,
@@ -43,6 +47,10 @@ import {
ScenarioEventViewIndexing,
} from 'src/app/data/scenario-event/scenario-event-data.service';
import { ScenarioEventQuery } from 'src/app/data/scenario-event/scenario-event.query';
+import { MselCompetencyQuery } from 'src/app/data/msel-competency/msel-competency.query';
+import { MselCompetencyDataService } from 'src/app/data/msel-competency/msel-competency-data.service';
+import { CompetencyFrameworkQuery } from 'src/app/data/competency-framework/competency-framework.query';
+import { CompetencyFrameworkDataService } from 'src/app/data/competency-framework/competency-framework-data.service';
import { UIDataService } from 'src/app/data/ui/ui-data.service';
import { AngularEditorConfig } from '@kolkov/angular-editor';
@@ -98,6 +106,8 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
keyUp = new Subject
();
private subscription: Subscription;
private unsubscribe$ = new Subject();
+ private competencyCache = new Map();
+ private competencyTypeCache = new Map();
viewConfig: AngularEditorConfig = {
editable: false,
height: 'auto',
@@ -116,7 +126,6 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
constructor(
private activatedRoute: ActivatedRoute,
- private settingsService: ComnSettingsService,
private mselDataService: MselDataService,
private mselQuery: MselQuery,
private organizationQuery: OrganizationQuery,
@@ -129,7 +138,13 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
private moveQuery: MoveQuery,
private scenarioEventDataService: ScenarioEventDataService,
private scenarioEventQuery: ScenarioEventQuery,
- private uiDataService: UIDataService
+ private mselCompetencyQuery: MselCompetencyQuery,
+ private mselCompetencyDataService: MselCompetencyDataService,
+ private competencyFrameworkQuery: CompetencyFrameworkQuery,
+ private competencyFrameworkDataService: CompetencyFrameworkDataService,
+ private uiDataService: UIDataService,
+ private http: HttpClient,
+ private settingsService: ComnSettingsService
) {
// subscribe to the route parameters. Used when viewing independently.
this.activatedRoute.params
@@ -140,6 +155,11 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
this.mselDataService.loadById(mselId);
this.loadInitialData(mselId);
this.mselDataService.setActive(mselId);
+ // Call xAPI for viewed event (only on view page, not build page)
+ const baseUrl = this.settingsService.settings.ApiUrl.endsWith('/')
+ ? this.settingsService.settings.ApiUrl
+ : this.settingsService.settings.ApiUrl + '/';
+ this.http.post(`${baseUrl}api/xapi/viewed/msel/${mselId}`, {}).subscribe();
}
});
// subscribe to the route query parameters. Used when editing the MSEL and checking the view.
@@ -171,6 +191,7 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
.subscribe((dataFields) => {
this.sortedDataFields = this.getSortedDataFields(dataFields);
this.scenarioEventDataService.updateScenarioEventViewDataFields(this);
+ this.scenarioEventDataService.updateScenarioEventViewDataValues(this);
this.scenarioEventDataService.updateScenarioEventViewDisplayedEvents(
this
);
@@ -248,6 +269,18 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
.subscribe((teams) => {
this.teamList = teams;
});
+ // observe the MselCompetencies for tooltip data
+ this.mselCompetencyQuery
+ .selectAll()
+ .pipe(takeUntil(this.unsubscribe$))
+ .subscribe((mselCompetencies) => {
+ this.competencyCache.clear();
+ for (const mc of mselCompetencies) {
+ if (mc.competency && mc.competency.idNumber) {
+ this.competencyCache.set(mc.competency.idNumber, mc.competency);
+ }
+ }
+ });
// subscribe to filter string changes for debounce
this.subscription = this.keyUp
.pipe(
@@ -285,6 +318,8 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
this.dataFieldDataService.loadByMsel(mselId);
this.dataValueDataService.loadByMsel(mselId);
this.scenarioEventDataService.loadByMsel(mselId);
+ this.mselCompetencyDataService.loadByMsel(mselId);
+ this.competencyFrameworkDataService.load();
}
applyFilter(filterValue: string) {
@@ -372,6 +407,42 @@ export class MselViewComponent implements OnDestroy, ScenarioEventView {
return users;
}
+ getCompetencyTooltip(idNumber: string): string {
+ const comp = this.competencyCache.get(idNumber);
+ return comp?.shortName || '';
+ }
+
+ private getCompetencyType(comp: Competency): string {
+ if (!comp || !comp.id) return '';
+
+ // Check cache first
+ if (this.competencyTypeCache.has(comp.id)) {
+ return this.competencyTypeCache.get(comp.id) || '';
+ }
+
+ // Derive from ID pattern
+ const idNumber = comp.idNumber || '';
+ let type = '';
+
+ if (idNumber.includes('WRL')) {
+ type = 'Work Role';
+ } else if (/^[TKSA][\d-]/.test(idNumber)) {
+ const prefixMap: Record = {
+ 'T': 'Task', 'K': 'Knowledge', 'S': 'Skill', 'A': 'Ability',
+ };
+ type = prefixMap[idNumber.charAt(0)] || '';
+ } else if (/^[A-Z]{2}-[A-Z]{3}-\d+$/.test(idNumber)) {
+ type = 'Work Role';
+ } else if (/^[A-Z]{3}$/.test(idNumber)) {
+ type = 'Specialty Area';
+ } else if (/^[A-Z]{2}$/.test(idNumber)) {
+ type = 'Category';
+ }
+
+ this.competencyTypeCache.set(comp.id, type);
+ return type;
+ }
+
trackByFn(index, item) {
return item.id;
}
diff --git a/src/app/components/msel/msel.component.html b/src/app/components/msel/msel.component.html
index 3a150a07..15ca3717 100755
--- a/src/app/components/msel/msel.component.html
+++ b/src/app/components/msel/msel.component.html
@@ -139,6 +139,9 @@
@if (selectedTab === 'CITE Duties') {
}
+ @if (selectedTab === 'Competencies') {
+
+ }
@if (selectedTab === 'Scenario Events') {
@@ -146,6 +149,10 @@
@if (selectedTab === 'Exercise View') {
}
+ @if (selectedTab === 'Assessor View') {
+
+ }
@if (selectedTab === 'Invitations') {
Invitations
diff --git a/src/app/components/msel/msel.component.ts b/src/app/components/msel/msel.component.ts
index 2918fb96..f0c4c68a 100755
--- a/src/app/components/msel/msel.component.ts
+++ b/src/app/components/msel/msel.component.ts
@@ -48,6 +48,7 @@ export class MselComponent implements OnDestroy {
@Input() loggedInUserId: string;
@Input() canEditMsel: boolean;
@Input() canAccessAdminSection: boolean;
+ @Input() canEditCheckboxes = false;
@Input() userTheme$: Observable;
@Output() deleteThisMsel = new EventEmitter();
@ViewChild('tabGroup0', { static: false }) tabGroup0: MatTabGroup;
@@ -56,8 +57,9 @@ export class MselComponent implements OnDestroy {
tabList: string[] = [
'Info',
'Contributors',
- 'Teams',
+ 'Competencies',
'Data Fields',
+ 'Teams',
'Organizations',
'Moves',
'Player Apps',
@@ -66,6 +68,7 @@ export class MselComponent implements OnDestroy {
'CITE Duties',
'Scenario Events',
'Exercise View',
+ 'Assessor View',
'MSEL Playbook',
'Invitations',
];
@@ -80,8 +83,10 @@ export class MselComponent implements OnDestroy {
['Gallery Cards', 'mdi-view-grid-outline'],
['CITE Actions', 'mdi-clipboard-check-outline'],
['CITE Duties', 'mdi-clipboard-account-outline'],
+ ['Competencies', 'mdi-certificate-outline'],
['Scenario Events', 'mdi-chart-timeline'],
['Exercise View', 'mdi-eye-outline'],
+ ['Assessor View', 'mdi-clipboard-check-multiple-outline'],
['MSEL Playbook', 'mdi-book'],
['Invitations', 'mdi-email-open-outline'],
]);
diff --git a/src/app/components/scenario-event-list/scenario-event-list.component.html b/src/app/components/scenario-event-list/scenario-event-list.component.html
index 4cbf1654..533311d3 100755
--- a/src/app/components/scenario-event-list/scenario-event-list.component.html
+++ b/src/app/components/scenario-event-list/scenario-event-list.component.html
@@ -33,6 +33,18 @@
+ @if (!showSearch) {
+
+ }
+ @if (showSearch) {
+
+ }
@if ((canEditMsel() || msel.hasRole(loggedInUserId, null).owner)) {
- @if (!showSearch) {
-
- }
- @if (showSearch) {
-
- }
diff --git a/src/app/components/scenario-event-list/scenario-event-list.component.scss b/src/app/components/scenario-event-list/scenario-event-list.component.scss
index 059a3f9b..e33c002e 100755
--- a/src/app/components/scenario-event-list/scenario-event-list.component.scss
+++ b/src/app/components/scenario-event-list/scenario-event-list.component.scss
@@ -5,14 +5,17 @@
@use "@angular/material" as mat;
:host {
- @include mat.form-field-density(-5); // Adjust -3 for this specific form field
+ @include mat.form-field-density(-5);
+ flex: 1;
+ overflow: hidden;
+ min-height: 0;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
- height: calc(100% - 60px);
+ height: 100%;
overflow: auto;
background-color: var(--mat-sys-background);
color: var(--mat-sys-on-background);
diff --git a/src/app/components/scenario-event-list/scenario-event-list.component.ts b/src/app/components/scenario-event-list/scenario-event-list.component.ts
index a3a4370b..49bfabb3 100755
--- a/src/app/components/scenario-event-list/scenario-event-list.component.ts
+++ b/src/app/components/scenario-event-list/scenario-event-list.component.ts
@@ -254,6 +254,7 @@ export class ScenarioEventListComponent
this.allDataFields = dataFields;
this.setSortedDataFields();
this.scenarioEventDataService.updateScenarioEventViewDataFields(this);
+ this.scenarioEventDataService.updateScenarioEventViewDataValues(this);
this.scenarioEventDataService.updateScenarioEventViewDisplayedEvents(
this
);
@@ -453,7 +454,7 @@ export class ScenarioEventListComponent
getMselUsers(): User[] {
let users = [];
- this.msel.teams.forEach((team) => {
+ this.msel.teams?.forEach((team) => {
team.users.forEach((user) => {
users.push({ ...user });
});
@@ -488,7 +489,7 @@ export class ScenarioEventListComponent
this.organizationList.forEach((o) => {
orgs.push(o.shortName);
});
- this.msel.teams.forEach((t) => {
+ this.msel.teams?.forEach((t) => {
orgs.push(t.shortName);
});
// Remove duplicates
@@ -499,7 +500,7 @@ export class ScenarioEventListComponent
getSortedTeamOptions(): string[] {
let teams: string[] = [];
- this.msel.teams.forEach((t) => {
+ this.msel.teams?.forEach((t) => {
teams.push(t.shortName);
});
teams = teams.sort((a, b) => (a < b ? -1 : 1));
diff --git a/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.html b/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.html
new file mode 100644
index 00000000..eabc6fa3
--- /dev/null
+++ b/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.html
@@ -0,0 +1,50 @@
+
+
+{{ data.action === 'add' ? 'Add' : 'Remove' }} Team from Related
+
+
Also {{ actionVerb }} team {{ data.teamName }} {{ preposition }} the {{ data.competencies.length }}
+ related competenc{{ data.competencies.length === 1 ? 'y' : 'ies' }} on the MSEL?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ {{ mc.competency?.idNumber }}
+
+
+
+ Name
+
+ {{ mc.competency?.shortName }}
+
+
+
+
+
+
+
+
+
+ No
+
+ Yes ({{ selection.selected.length }})
+
+
diff --git a/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.scss b/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.scss
new file mode 100644
index 00000000..230b6c45
--- /dev/null
+++ b/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.scss
@@ -0,0 +1,34 @@
+/*
+ Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+ Released under a MIT (SEI)-style license. See LICENSE.md in the
+ project root for license information.
+*/
+
+.propagate-table {
+ max-height: 300px;
+ overflow: auto;
+ margin-top: 8px;
+}
+
+.col-select {
+ max-width: 48px;
+ flex: 0 0 48px;
+}
+
+.col-id {
+ max-width: 120px;
+ flex: 0 0 120px;
+}
+
+.col-name {
+ flex: 1;
+}
+
+.action-spacer {
+ flex: 1;
+}
+
+mat-dialog-actions {
+ gap: 8px;
+ padding: 8px 24px 16px;
+}
diff --git a/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.ts b/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.ts
new file mode 100644
index 00000000..235862dc
--- /dev/null
+++ b/src/app/components/team-competency-propagate-dialog/team-competency-propagate-dialog.component.ts
@@ -0,0 +1,62 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the
+// project root for license information.
+
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { SelectionModel } from '@angular/cdk/collections';
+import { MselCompetency } from 'src/app/generated/blueprint.api';
+
+export interface TeamCompetencyPropagateData {
+ teamName: string;
+ action: 'add' | 'remove';
+ competencies: MselCompetency[];
+}
+
+@Component({
+ selector: 'app-team-competency-propagate-dialog',
+ templateUrl: './team-competency-propagate-dialog.component.html',
+ styleUrls: ['./team-competency-propagate-dialog.component.scss'],
+ standalone: false
+})
+export class TeamCompetencyPropagateDialogComponent {
+ selection = new SelectionModel(true, []);
+ displayedColumns = ['select', 'idNumber', 'shortName'];
+
+ constructor(
+ public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: TeamCompetencyPropagateData
+ ) {
+ this.dialogRef.disableClose = true;
+ // Pre-select all
+ this.selection.select(...data.competencies);
+ }
+
+ get actionVerb(): string {
+ return this.data.action === 'add' ? 'add' : 'remove';
+ }
+
+ get preposition(): string {
+ return this.data.action === 'add' ? 'to' : 'from';
+ }
+
+ isAllSelected(): boolean {
+ return this.selection.selected.length === this.data.competencies.length;
+ }
+
+ toggleAll(): void {
+ if (this.isAllSelected()) {
+ this.selection.clear();
+ } else {
+ this.selection.select(...this.data.competencies);
+ }
+ }
+
+ onConfirm(): void {
+ this.dialogRef.close(this.selection.selected);
+ }
+
+ onCancel(): void {
+ this.dialogRef.close(null);
+ }
+}
diff --git a/src/app/data/competency-framework/competency-framework-data.service.ts b/src/app/data/competency-framework/competency-framework-data.service.ts
new file mode 100644
index 00000000..fcfc8973
--- /dev/null
+++ b/src/app/data/competency-framework/competency-framework-data.service.ts
@@ -0,0 +1,180 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { CompetencyFrameworkStore } from './competency-framework.store';
+import { CompetencyFrameworkQuery } from './competency-framework.query';
+import { Injectable } from '@angular/core';
+import { UntypedFormControl } from '@angular/forms';
+import { PageEvent } from '@angular/material/paginator';
+import { Router, ActivatedRoute } from '@angular/router';
+import {
+ CompetencyFramework,
+ CompetencyFrameworkService,
+} from 'src/app/generated/blueprint.api';
+import { map, take, tap } from 'rxjs/operators';
+import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class CompetencyFrameworkDataService {
+ readonly CompetencyFrameworkList: Observable;
+ readonly filterControl = new UntypedFormControl();
+ private filterTerm: Observable;
+ private sortColumn: Observable;
+ private sortIsAscending: Observable;
+ private _pageEvent: PageEvent = { length: 0, pageIndex: 0, pageSize: 10 };
+ readonly pageEvent = new BehaviorSubject(this._pageEvent);
+ private pageSize: Observable;
+ private pageIndex: Observable;
+
+ constructor(
+ private competencyFrameworkStore: CompetencyFrameworkStore,
+ private competencyFrameworkQuery: CompetencyFrameworkQuery,
+ private competencyFrameworkService: CompetencyFrameworkService,
+ private router: Router,
+ private activatedRoute: ActivatedRoute
+ ) {
+ this.filterTerm = activatedRoute.queryParamMap.pipe(
+ map((params) => params.get('competencyFrameworkmask') || '')
+ );
+ this.filterControl.valueChanges.subscribe((term) => {
+ this.router.navigate([], {
+ queryParams: { competencyFrameworkmask: term },
+ queryParamsHandling: 'merge',
+ });
+ });
+ this.sortColumn = activatedRoute.queryParamMap.pipe(
+ map((params) => params.get('sorton') || 'name')
+ );
+ this.sortIsAscending = activatedRoute.queryParamMap.pipe(
+ map((params) => (params.get('sortdir') || 'asc') === 'asc')
+ );
+ this.pageSize = activatedRoute.queryParamMap.pipe(
+ map((params) => parseInt(params.get('pagesize') || '20', 10))
+ );
+ this.pageIndex = activatedRoute.queryParamMap.pipe(
+ map((params) => parseInt(params.get('pageindex') || '0', 10))
+ );
+ this.CompetencyFrameworkList = combineLatest([
+ this.competencyFrameworkQuery.selectAll(),
+ this.filterTerm,
+ this.sortColumn,
+ this.sortIsAscending,
+ this.pageSize,
+ this.pageIndex,
+ ]).pipe(
+ map(
+ ([
+ items,
+ filterTerm,
+ sortColumn,
+ sortIsAscending,
+ pageSize,
+ pageIndex,
+ ]) =>
+ items
+ ? (items as CompetencyFramework[])
+ .filter(
+ (cf) =>
+ cf.name?.toLowerCase().includes(filterTerm.toLowerCase()) ||
+ cf.source?.toLowerCase().includes(filterTerm.toLowerCase()) ||
+ cf.version?.toLowerCase().includes(filterTerm.toLowerCase()))
+ : []
+ )
+ );
+ }
+
+ load() {
+ this.competencyFrameworkStore.setLoading(true);
+ this.competencyFrameworkService
+ .getCompetencyFrameworks()
+ .pipe(
+ tap(() => {
+ this.competencyFrameworkStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe(
+ (templates) => {
+ this.competencyFrameworkStore.upsertMany(templates);
+ },
+ (error) => { }
+ );
+ }
+
+ loadById(id: string) {
+ this.competencyFrameworkStore.setLoading(true);
+ return this.competencyFrameworkService
+ .getCompetencyFramework(id)
+ .pipe(
+ tap(() => {
+ this.competencyFrameworkStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe((s) => {
+ this.competencyFrameworkStore.upsert(s.id, { ...s });
+ });
+ }
+
+ unload() {
+ this.competencyFrameworkStore.set([]);
+ }
+
+ add(competencyFramework: CompetencyFramework) {
+ this.competencyFrameworkStore.setLoading(true);
+ this.competencyFrameworkService
+ .createCompetencyFramework(competencyFramework)
+ .pipe(
+ tap(() => {
+ this.competencyFrameworkStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe((s) => {
+ this.competencyFrameworkStore.add(s);
+ });
+ }
+
+ update(competencyFramework: CompetencyFramework) {
+ this.competencyFrameworkStore.setLoading(true);
+ this.competencyFrameworkService
+ .updateCompetencyFramework(competencyFramework.id, competencyFramework)
+ .pipe(
+ tap(() => {
+ this.competencyFrameworkStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe((n) => {
+ this.updateStore(n);
+ });
+ }
+
+ delete(id: string) {
+ this.competencyFrameworkService
+ .deleteCompetencyFramework(id)
+ .pipe(take(1))
+ .subscribe((r) => {
+ this.deleteFromStore(id);
+ });
+ }
+
+ setActive(id: string) {
+ this.competencyFrameworkStore.setActive(id);
+ }
+
+ setPageEvent(pageEvent: PageEvent) {
+ this.competencyFrameworkStore.update({ pageEvent: pageEvent });
+ }
+
+ updateStore(competencyFramework: CompetencyFramework) {
+ this.competencyFrameworkStore.upsert(competencyFramework.id, competencyFramework);
+ }
+
+ deleteFromStore(id: string) {
+ this.competencyFrameworkStore.remove(id);
+ }
+
+}
diff --git a/src/app/data/competency-framework/competency-framework.query.ts b/src/app/data/competency-framework/competency-framework.query.ts
new file mode 100644
index 00000000..67d778c9
--- /dev/null
+++ b/src/app/data/competency-framework/competency-framework.query.ts
@@ -0,0 +1,28 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { Order, QueryConfig, QueryEntity } from '@datorama/akita';
+import {
+ CompetencyFrameworkState,
+ CompetencyFrameworkStore,
+} from './competency-framework.store';
+import { CompetencyFramework } from 'src/app/generated/blueprint.api';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+
+@QueryConfig({
+ sortBy: 'name',
+ sortByOrder: Order.ASC,
+})
+@Injectable({
+ providedIn: 'root',
+})
+export class CompetencyFrameworkQuery extends QueryEntity {
+ constructor(protected store: CompetencyFrameworkStore) {
+ super(store);
+ }
+
+ selectById(id: string): Observable {
+ return this.selectEntity(id);
+ }
+}
diff --git a/src/app/data/competency-framework/competency-framework.store.ts b/src/app/data/competency-framework/competency-framework.store.ts
new file mode 100644
index 00000000..8232be1a
--- /dev/null
+++ b/src/app/data/competency-framework/competency-framework.store.ts
@@ -0,0 +1,18 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
+import { CompetencyFramework } from 'src/app/generated/blueprint.api';
+import { Injectable } from '@angular/core';
+
+export interface CompetencyFrameworkState extends EntityState {}
+
+@Injectable({
+ providedIn: 'root',
+})
+@StoreConfig({ name: 'competencyFrameworks' })
+export class CompetencyFrameworkStore extends EntityStore {
+ constructor() {
+ super();
+ }
+}
diff --git a/src/app/data/msel-competency/msel-competency-data.service.ts b/src/app/data/msel-competency/msel-competency-data.service.ts
new file mode 100644
index 00000000..387100a7
--- /dev/null
+++ b/src/app/data/msel-competency/msel-competency-data.service.ts
@@ -0,0 +1,83 @@
+// Copyright 2024 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { MselCompetencyStore } from './msel-competency.store';
+import { MselCompetencyQuery } from './msel-competency.query';
+import { Injectable } from '@angular/core';
+import {
+ MselCompetency,
+ MselCompetencyService,
+} from 'src/app/generated/blueprint.api';
+import { take, tap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class MselCompetencyDataService {
+ readonly MselCompetencyList: Observable;
+
+ constructor(
+ private mselCompetencyStore: MselCompetencyStore,
+ private mselCompetencyQuery: MselCompetencyQuery,
+ private mselCompetencyService: MselCompetencyService
+ ) {
+ this.MselCompetencyList = this.mselCompetencyQuery.selectAll();
+ }
+
+ loadByMsel(mselId: string) {
+ this.mselCompetencyStore.setLoading(true);
+ this.mselCompetencyService
+ .getMselCompetencies(mselId)
+ .pipe(
+ tap(() => {
+ this.mselCompetencyStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe(
+ (mselCompetencies) => {
+ this.mselCompetencyStore.set(mselCompetencies);
+ },
+ (error) => {
+ this.mselCompetencyStore.set([]);
+ }
+ );
+ }
+
+ unload() {
+ this.mselCompetencyStore.set([]);
+ }
+
+ add(mselCompetency: MselCompetency) {
+ this.mselCompetencyStore.setLoading(true);
+ this.mselCompetencyService
+ .createMselCompetency(mselCompetency)
+ .pipe(
+ tap(() => {
+ this.mselCompetencyStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe((s) => {
+ this.mselCompetencyStore.add(s);
+ });
+ }
+
+ delete(id: string) {
+ this.mselCompetencyService
+ .deleteMselCompetency(id)
+ .pipe(take(1))
+ .subscribe((r) => {
+ this.mselCompetencyStore.remove(id);
+ });
+ }
+
+ updateStore(mselCompetency: MselCompetency) {
+ this.mselCompetencyStore.upsert(mselCompetency.id, mselCompetency);
+ }
+
+ deleteFromStore(id: string) {
+ this.mselCompetencyStore.remove(id);
+ }
+}
diff --git a/src/app/data/msel-competency/msel-competency.query.ts b/src/app/data/msel-competency/msel-competency.query.ts
new file mode 100644
index 00000000..9755bb07
--- /dev/null
+++ b/src/app/data/msel-competency/msel-competency.query.ts
@@ -0,0 +1,28 @@
+// Copyright 2024 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { Order, QueryConfig, QueryEntity } from '@datorama/akita';
+import {
+ MselCompetencyState,
+ MselCompetencyStore,
+} from './msel-competency.store';
+import { MselCompetency } from 'src/app/generated/blueprint.api';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+
+@QueryConfig({
+ sortBy: 'competencyId',
+ sortByOrder: Order.ASC,
+})
+@Injectable({
+ providedIn: 'root',
+})
+export class MselCompetencyQuery extends QueryEntity {
+ constructor(protected store: MselCompetencyStore) {
+ super(store);
+ }
+
+ selectById(id: string): Observable {
+ return this.selectEntity(id);
+ }
+}
diff --git a/src/app/data/msel-competency/msel-competency.store.ts b/src/app/data/msel-competency/msel-competency.store.ts
new file mode 100644
index 00000000..26c37cb7
--- /dev/null
+++ b/src/app/data/msel-competency/msel-competency.store.ts
@@ -0,0 +1,18 @@
+// Copyright 2024 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
+import { MselCompetency } from 'src/app/generated/blueprint.api';
+import { Injectable } from '@angular/core';
+
+export interface MselCompetencyState extends EntityState {}
+
+@Injectable({
+ providedIn: 'root',
+})
+@StoreConfig({ name: 'mselCompetencies' })
+export class MselCompetencyStore extends EntityStore {
+ constructor() {
+ super();
+ }
+}
diff --git a/src/app/data/msel/msel-data.service.ts b/src/app/data/msel/msel-data.service.ts
index 602e7aac..4cb0eff1 100755
--- a/src/app/data/msel/msel-data.service.ts
+++ b/src/app/data/msel/msel-data.service.ts
@@ -66,6 +66,10 @@ export class MselPlus implements Msel {
showIntegrationTargetOnScenarioEventList?: boolean;
showIntegrationTargetOnExerciseView?: boolean;
integrationTargetDisplayOrder?: number;
+ showTimeOnAssessorView?: boolean;
+ showMoveOnAssessorView?: boolean;
+ showGroupOnAssessorView?: boolean;
+ showIntegrationTargetOnAssessorView?: boolean;
moves?: Array;
dataFields?: Array;
scenarioEvents?: Array;
@@ -80,6 +84,12 @@ export class MselPlus implements Msel {
galleryIntegrationType?: IntegrationType;
citeIntegrationType?: IntegrationType;
steamfitterIntegrationType?: IntegrationType;
+ educationalLevel?: string;
+ subject?: string;
+ keywords?: string;
+ educationalUse?: string;
+ courseMode?: string;
+ language?: string;
hasRole(userId: string, scenarioEventId: string) {
// initialize to no roles
diff --git a/src/app/data/scenario-event/scenario-event-data.service.ts b/src/app/data/scenario-event/scenario-event-data.service.ts
index 2e86648d..0e088680 100755
--- a/src/app/data/scenario-event/scenario-event-data.service.ts
+++ b/src/app/data/scenario-event/scenario-event-data.service.ts
@@ -105,6 +105,7 @@ export class ScenarioEventDataService {
DataFieldType.Checkbox,
DataFieldType.User,
DataFieldType.Url,
+ DataFieldType.Competency,
];
loadByMsel(mselId: string) {
diff --git a/src/app/data/team-competency/team-competency-data.service.ts b/src/app/data/team-competency/team-competency-data.service.ts
new file mode 100644
index 00000000..f99dbf78
--- /dev/null
+++ b/src/app/data/team-competency/team-competency-data.service.ts
@@ -0,0 +1,95 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { TeamCompetencyStore } from './team-competency.store';
+import { TeamCompetencyQuery } from './team-competency.query';
+import { Injectable } from '@angular/core';
+import {
+ TeamCompetency,
+ TeamCompetencyService,
+} from 'src/app/generated/blueprint.api';
+import { take, tap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class TeamCompetencyDataService {
+ readonly TeamCompetencyList: Observable;
+
+ constructor(
+ private teamCompetencyStore: TeamCompetencyStore,
+ private teamCompetencyQuery: TeamCompetencyQuery,
+ private teamCompetencyService: TeamCompetencyService
+ ) {
+ this.TeamCompetencyList = this.teamCompetencyQuery.selectAll();
+ }
+
+ loadByMsel(mselId: string) {
+ this.teamCompetencyStore.setLoading(true);
+ this.teamCompetencyService
+ .getMselTeamCompetencies(mselId)
+ .pipe(
+ tap(() => {
+ this.teamCompetencyStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe(
+ (teamCompetencies) => {
+ this.teamCompetencyStore.set(teamCompetencies);
+ },
+ (error) => {
+ this.teamCompetencyStore.set([]);
+ }
+ );
+ }
+
+ loadByTeam(teamId: string) {
+ this.teamCompetencyStore.setLoading(true);
+ this.teamCompetencyService
+ .getTeamCompetencies(teamId)
+ .pipe(
+ tap(() => {
+ this.teamCompetencyStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe(
+ (teamCompetencies) => {
+ this.teamCompetencyStore.set(teamCompetencies);
+ },
+ (error) => {
+ this.teamCompetencyStore.set([]);
+ }
+ );
+ }
+
+ unload() {
+ this.teamCompetencyStore.set([]);
+ }
+
+ add(teamCompetency: TeamCompetency) {
+ this.teamCompetencyStore.setLoading(true);
+ this.teamCompetencyService
+ .createTeamCompetency(teamCompetency)
+ .pipe(
+ tap(() => {
+ this.teamCompetencyStore.setLoading(false);
+ }),
+ take(1)
+ )
+ .subscribe((s) => {
+ this.teamCompetencyStore.add(s);
+ });
+ }
+
+ delete(id: string) {
+ this.teamCompetencyService
+ .deleteTeamCompetency(id)
+ .pipe(take(1))
+ .subscribe((r) => {
+ this.teamCompetencyStore.remove(id);
+ });
+ }
+}
diff --git a/src/app/data/team-competency/team-competency.query.ts b/src/app/data/team-competency/team-competency.query.ts
new file mode 100644
index 00000000..8a65155d
--- /dev/null
+++ b/src/app/data/team-competency/team-competency.query.ts
@@ -0,0 +1,28 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { Order, QueryConfig, QueryEntity } from '@datorama/akita';
+import {
+ TeamCompetencyState,
+ TeamCompetencyStore,
+} from './team-competency.store';
+import { TeamCompetency } from 'src/app/generated/blueprint.api';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+
+@QueryConfig({
+ sortBy: 'competencyId',
+ sortByOrder: Order.ASC,
+})
+@Injectable({
+ providedIn: 'root',
+})
+export class TeamCompetencyQuery extends QueryEntity {
+ constructor(protected store: TeamCompetencyStore) {
+ super(store);
+ }
+
+ selectById(id: string): Observable {
+ return this.selectEntity(id);
+ }
+}
diff --git a/src/app/data/team-competency/team-competency.store.ts b/src/app/data/team-competency/team-competency.store.ts
new file mode 100644
index 00000000..e9257f44
--- /dev/null
+++ b/src/app/data/team-competency/team-competency.store.ts
@@ -0,0 +1,18 @@
+// Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.
+
+import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
+import { TeamCompetency } from 'src/app/generated/blueprint.api';
+import { Injectable } from '@angular/core';
+
+export interface TeamCompetencyState extends EntityState {}
+
+@Injectable({
+ providedIn: 'root',
+})
+@StoreConfig({ name: 'teamCompetencies' })
+export class TeamCompetencyStore extends EntityStore {
+ constructor() {
+ super();
+ }
+}
diff --git a/src/app/data/team/team-data.service.ts b/src/app/data/team/team-data.service.ts
index 4411e403..bd0387e8 100755
--- a/src/app/data/team/team-data.service.ts
+++ b/src/app/data/team/team-data.service.ts
@@ -135,7 +135,7 @@ export class TeamDataService {
load() {
this.teamStore.setLoading(true);
this.teamService
- .getTeams()
+ .getMyTeams()
.pipe(
tap(() => {
this.teamStore.setLoading(false);
diff --git a/src/app/generated/blueprint.api/.openapi-generator/FILES b/src/app/generated/blueprint.api/.openapi-generator/FILES
index 1bf3a776..b26272e5 100644
--- a/src/app/generated/blueprint.api/.openapi-generator/FILES
+++ b/src/app/generated/blueprint.api/.openapi-generator/FILES
@@ -11,6 +11,7 @@ api/catalogUnit.service.ts
api/cite.service.ts
api/citeAction.service.ts
api/citeDuty.service.ts
+api/competencyFramework.service.ts
api/dataField.service.ts
api/dataOption.service.ts
api/dataValue.service.ts
@@ -19,24 +20,30 @@ api/healthCheck.service.ts
api/inject.service.ts
api/injectType.service.ts
api/invitation.service.ts
+api/lmt.service.ts
api/move.service.ts
api/msel.service.ts
+api/mselCompetency.service.ts
api/mselPage.service.ts
api/mselUnit.service.ts
api/organization.service.ts
api/player.service.ts
api/playerApplication.service.ts
api/playerApplicationTeam.service.ts
+api/proficiencyLevel.service.ts
+api/proficiencyScale.service.ts
api/scenarioEvent.service.ts
api/systemPermissions.service.ts
api/systemRoles.service.ts
api/team.service.ts
+api/teamCompetency.service.ts
api/teamUser.service.ts
api/unit.service.ts
api/unitUser.service.ts
api/user.service.ts
api/userMselRole.service.ts
api/userTeamRole.service.ts
+api/xApi.service.ts
configuration.ts
encoder.ts
git_push.sh
@@ -55,6 +62,10 @@ model/catalogUnit.ts
model/citeAction.ts
model/citeDuty.ts
model/compareInfo.ts
+model/competency.ts
+model/competencyAssertion.ts
+model/competencyFramework.ts
+model/competencyFrameworkImportPreview.ts
model/constructorInfo.ts
model/createFromInjectsForm.ts
model/cultureInfo.ts
@@ -66,6 +77,8 @@ model/dataColumn.ts
model/dataField.ts
model/dataFieldType.ts
model/dataOption.ts
+model/dataOptionImportPreview.ts
+model/dataOptionImportPreviewItem.ts
model/dataSet.ts
model/dataSetDateTime.ts
model/dataTable.ts
@@ -73,12 +86,14 @@ model/dataValue.ts
model/dateTimeFormatInfo.ts
model/dayOfWeek.ts
model/digitShapes.ts
+model/elementTypeCount.ts
model/eventAttributes.ts
model/eventInfo.ts
model/eventType.ts
model/exception.ts
model/fieldAttributes.ts
model/fieldInfo.ts
+model/frameworkDeleteCheck.ts
model/genericParameterAttributes.ts
model/group.ts
model/groupMembership.ts
@@ -106,8 +121,10 @@ model/module.ts
model/moduleHandle.ts
model/move.ts
model/msel.ts
+model/mselCompetency.ts
model/mselItemStatus.ts
model/mselPage.ts
+model/mselReference.ts
model/mselRole.ts
model/mselUnit.ts
model/numberFormatInfo.ts
@@ -118,6 +135,8 @@ model/permission.ts
model/playerApplication.ts
model/playerApplicationTeam.ts
model/problemDetails.ts
+model/proficiencyLevel.ts
+model/proficiencyScale.ts
model/propertyAttributes.ts
model/propertyInfo.ts
model/rightSideDisplay.ts
@@ -142,6 +161,8 @@ model/structLayoutAttribute.ts
model/systemPermission.ts
model/systemRole.ts
model/team.ts
+model/teamCompetency.ts
+model/teamPermission.ts
model/teamRole.ts
model/teamType.ts
model/teamUser.ts
diff --git a/src/app/generated/blueprint.api/api/api.ts b/src/app/generated/blueprint.api/api/api.ts
index 37e50522..9a7f4aca 100644
--- a/src/app/generated/blueprint.api/api/api.ts
+++ b/src/app/generated/blueprint.api/api/api.ts
@@ -20,6 +20,8 @@ export * from './citeAction.service';
import { CiteActionService } from './citeAction.service';
export * from './citeDuty.service';
import { CiteDutyService } from './citeDuty.service';
+export * from './competencyFramework.service';
+import { CompetencyFrameworkService } from './competencyFramework.service';
export * from './dataField.service';
import { DataFieldService } from './dataField.service';
export * from './dataOption.service';
@@ -36,10 +38,14 @@ export * from './injectType.service';
import { InjectTypeService } from './injectType.service';
export * from './invitation.service';
import { InvitationService } from './invitation.service';
+export * from './lmt.service';
+import { LmtService } from './lmt.service';
export * from './move.service';
import { MoveService } from './move.service';
export * from './msel.service';
import { MselService } from './msel.service';
+export * from './mselCompetency.service';
+import { MselCompetencyService } from './mselCompetency.service';
export * from './mselPage.service';
import { MselPageService } from './mselPage.service';
export * from './mselUnit.service';
@@ -52,6 +58,10 @@ export * from './playerApplication.service';
import { PlayerApplicationService } from './playerApplication.service';
export * from './playerApplicationTeam.service';
import { PlayerApplicationTeamService } from './playerApplicationTeam.service';
+export * from './proficiencyLevel.service';
+import { ProficiencyLevelService } from './proficiencyLevel.service';
+export * from './proficiencyScale.service';
+import { ProficiencyScaleService } from './proficiencyScale.service';
export * from './scenarioEvent.service';
import { ScenarioEventService } from './scenarioEvent.service';
export * from './systemPermissions.service';
@@ -60,6 +70,8 @@ export * from './systemRoles.service';
import { SystemRolesService } from './systemRoles.service';
export * from './team.service';
import { TeamService } from './team.service';
+export * from './teamCompetency.service';
+import { TeamCompetencyService } from './teamCompetency.service';
export * from './teamUser.service';
import { TeamUserService } from './teamUser.service';
export * from './unit.service';
@@ -72,4 +84,6 @@ export * from './userMselRole.service';
import { UserMselRoleService } from './userMselRole.service';
export * from './userTeamRole.service';
import { UserTeamRoleService } from './userTeamRole.service';
-export const APIS = [CardService, CardTeamService, CatalogService, CatalogInjectService, CatalogUnitService, CiteService, CiteActionService, CiteDutyService, DataFieldService, DataOptionService, DataValueService, GroupService, HealthCheckService, InjectService, InjectTypeService, InvitationService, MoveService, MselService, MselPageService, MselUnitService, OrganizationService, PlayerService, PlayerApplicationService, PlayerApplicationTeamService, ScenarioEventService, SystemPermissionsService, SystemRolesService, TeamService, TeamUserService, UnitService, UnitUserService, UserService, UserMselRoleService, UserTeamRoleService];
+export * from './xApi.service';
+import { XApiService } from './xApi.service';
+export const APIS = [CardService, CardTeamService, CatalogService, CatalogInjectService, CatalogUnitService, CiteService, CiteActionService, CiteDutyService, CompetencyFrameworkService, DataFieldService, DataOptionService, DataValueService, GroupService, HealthCheckService, InjectService, InjectTypeService, InvitationService, LmtService, MoveService, MselService, MselCompetencyService, MselPageService, MselUnitService, OrganizationService, PlayerService, PlayerApplicationService, PlayerApplicationTeamService, ProficiencyLevelService, ProficiencyScaleService, ScenarioEventService, SystemPermissionsService, SystemRolesService, TeamService, TeamCompetencyService, TeamUserService, UnitService, UnitUserService, UserService, UserMselRoleService, UserTeamRoleService, XApiService];
diff --git a/src/app/generated/blueprint.api/api/cite.service.ts b/src/app/generated/blueprint.api/api/cite.service.ts
index b5e2f12f..46b37a83 100644
--- a/src/app/generated/blueprint.api/api/cite.service.ts
+++ b/src/app/generated/blueprint.api/api/cite.service.ts
@@ -27,6 +27,8 @@ import { ProblemDetails } from '../model/problemDetails';
// @ts-ignore
import { ScoringModel } from '../model/scoringModel';
// @ts-ignore
+import { TeamRole } from '../model/teamRole';
+// @ts-ignore
import { TeamType } from '../model/teamType';
// @ts-ignore
@@ -102,15 +104,15 @@ export class CiteService extends BaseService {
}
/**
- * Gets all TeamTypes
- * Returns a list of all of the TeamTypes.
+ * Gets all TeamRoles from CITE
+ * Returns a list of all of the TeamRoles.
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public getTeamTypes(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
- public getTeamTypes(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
- public getTeamTypes(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
- public getTeamTypes(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ public getTeamRoles(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getTeamRoles(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getTeamRoles(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getTeamRoles(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
let localVarHeaders = this.defaultHeaders;
@@ -142,9 +144,9 @@ export class CiteService extends BaseService {
}
}
- let localVarPath = `/api/teamtypes`;
+ let localVarPath = `/api/teamroles`;
const { basePath, withCredentials } = this.configuration;
- return this.httpClient.request>('get', `${basePath}${localVarPath}`,
+ return this.httpClient.request>('get', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: responseType_,
@@ -158,15 +160,15 @@ export class CiteService extends BaseService {
}
/**
- * Gets all TeamRoles from CITE
- * Returns a list of all of the TeamRoles.
+ * Gets all TeamTypes
+ * Returns a list of all of the TeamTypes.
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
- public getTeamRoles(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
- public getTeamRoles(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
- public getTeamRoles(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
- public getTeamRoles(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ public getTeamTypes(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getTeamTypes(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getTeamTypes(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getTeamTypes(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
let localVarHeaders = this.defaultHeaders;
@@ -198,9 +200,9 @@ export class CiteService extends BaseService {
}
}
- let localVarPath = `/api/teamroles`;
+ let localVarPath = `/api/teamtypes`;
const { basePath, withCredentials } = this.configuration;
- return this.httpClient.request>('get', `${basePath}${localVarPath}`,
+ return this.httpClient.request>('get', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: responseType_,
@@ -214,9 +216,3 @@ export class CiteService extends BaseService {
}
}
-
-export interface CiteTeamRole {
- id?: string;
- name?: string;
-}
-
diff --git a/src/app/generated/blueprint.api/api/competencyElement.service.ts b/src/app/generated/blueprint.api/api/competencyElement.service.ts
new file mode 100644
index 00000000..524972b4
--- /dev/null
+++ b/src/app/generated/blueprint.api/api/competencyElement.service.ts
@@ -0,0 +1,361 @@
+/*
+ Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+ Released under a MIT (SEI)-style license. See LICENSE.md in the
+ project root for license information.
+*/
+
+/**
+ * Blueprint API
+ *
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+/* tslint:disable:no-unused-variable member-ordering */
+
+import { Inject, Injectable, Optional } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams,
+ HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
+ } from '@angular/common/http';
+import { CustomHttpParameterCodec } from '../encoder';
+import { Observable } from 'rxjs';
+
+// @ts-ignore
+import { CompetencyElement } from '../model/competencyElement';
+// @ts-ignore
+import { ProblemDetails } from '../model/problemDetails';
+
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
+import { Configuration } from '../configuration';
+import { BaseService } from '../api.base.service';
+
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class CompetencyElementService extends BaseService {
+
+ constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
+ super(basePath, configuration);
+ }
+
+ /**
+ * Creates a new CompetencyElement
+ * @param competencyElement
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public createCompetencyElement(competencyElement?: CompetencyElement, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public createCompetencyElement(competencyElement?: CompetencyElement, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createCompetencyElement(competencyElement?: CompetencyElement, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createCompetencyElement(competencyElement?: CompetencyElement, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyelements`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: competencyElement,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Deletes a CompetencyElement
+ * @param id
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public deleteCompetencyElement(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public deleteCompetencyElement(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteCompetencyElement(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteCompetencyElement(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling deleteCompetencyElement.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'application/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyelements/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('delete', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Gets a specific CompetencyElement by id
+ * @param id
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getCompetencyElement(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public getCompetencyElement(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getCompetencyElement(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getCompetencyElement(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling getCompetencyElement.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyelements/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Gets CompetencyElements by CompetencyFramework
+ * @param frameworkId
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getCompetencyElementsByFramework(frameworkId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getCompetencyElementsByFramework(frameworkId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getCompetencyElementsByFramework(frameworkId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getCompetencyElementsByFramework(frameworkId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (frameworkId === null || frameworkId === undefined) {
+ throw new Error('Required parameter frameworkId was null or undefined when calling getCompetencyElementsByFramework.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/${this.configuration.encodeParam({name: "frameworkId", value: frameworkId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/competencyelements`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request>('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Updates a CompetencyElement
+ * @param id
+ * @param competencyElement
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public updateCompetencyElement(id: string, competencyElement?: CompetencyElement, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public updateCompetencyElement(id: string, competencyElement?: CompetencyElement, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public updateCompetencyElement(id: string, competencyElement?: CompetencyElement, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public updateCompetencyElement(id: string, competencyElement?: CompetencyElement, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling updateCompetencyElement.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyelements/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('put', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: competencyElement,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+}
diff --git a/src/app/generated/blueprint.api/api/competencyFramework.service.ts b/src/app/generated/blueprint.api/api/competencyFramework.service.ts
new file mode 100644
index 00000000..b2104920
--- /dev/null
+++ b/src/app/generated/blueprint.api/api/competencyFramework.service.ts
@@ -0,0 +1,1143 @@
+/*
+ Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+ Released under a MIT (SEI)-style license. See LICENSE.md in the
+ project root for license information.
+*/
+
+/**
+ * Blueprint API
+ *
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+/* tslint:disable:no-unused-variable member-ordering */
+
+import { Inject, Injectable, Optional } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams,
+ HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
+ } from '@angular/common/http';
+import { CustomHttpParameterCodec } from '../encoder';
+import { Observable } from 'rxjs';
+
+// @ts-ignore
+import { Competency } from '../model/competency';
+// @ts-ignore
+import { CompetencyFramework } from '../model/competencyFramework';
+// @ts-ignore
+import { CompetencyFrameworkImportPreview } from '../model/competencyFrameworkImportPreview';
+// @ts-ignore
+import { FrameworkDeleteCheck } from '../model/frameworkDeleteCheck';
+// @ts-ignore
+import { ProblemDetails } from '../model/problemDetails';
+
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
+import { Configuration } from '../configuration';
+import { BaseService } from '../api.base.service';
+
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class CompetencyFrameworkService extends BaseService {
+
+ constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
+ super(basePath, configuration);
+ }
+
+ /**
+ * Checks if a Competency Framework can be deleted
+ * Returns dependency information showing which MSELs, data fields, and teams are using competencies from this framework.
+ * @param id The id of the Competency Framework to check
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public checkCanDeleteCompetencyFramework(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public checkCanDeleteCompetencyFramework(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public checkCanDeleteCompetencyFramework(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public checkCanDeleteCompetencyFramework(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling checkCanDeleteCompetencyFramework.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/can-delete`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Creates a new Competency within a Framework
+ * @param frameworkId The id of the parent CompetencyFramework
+ * @param competency The data to create the Competency with
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public createCompetency(frameworkId: string, competency?: Competency, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public createCompetency(frameworkId: string, competency?: Competency, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createCompetency(frameworkId: string, competency?: Competency, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createCompetency(frameworkId: string, competency?: Competency, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (frameworkId === null || frameworkId === undefined) {
+ throw new Error('Required parameter frameworkId was null or undefined when calling createCompetency.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/${this.configuration.encodeParam({name: "frameworkId", value: frameworkId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/competencies`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: competency,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Creates a new Competency Framework
+ * @param competencyFramework The data to create the CompetencyFramework with
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public createCompetencyFramework(competencyFramework?: CompetencyFramework, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public createCompetencyFramework(competencyFramework?: CompetencyFramework, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createCompetencyFramework(competencyFramework?: CompetencyFramework, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createCompetencyFramework(competencyFramework?: CompetencyFramework, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: competencyFramework,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Deletes a Competency
+ * @param competencyId The id of the Competency to delete
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public deleteCompetency(competencyId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public deleteCompetency(competencyId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteCompetency(competencyId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteCompetency(competencyId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (competencyId === null || competencyId === undefined) {
+ throw new Error('Required parameter competencyId was null or undefined when calling deleteCompetency.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'application/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencies/${this.configuration.encodeParam({name: "competencyId", value: competencyId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('delete', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Deletes a Competency Framework
+ * Deletes the framework and all associated competencies and relationships (cascade). Will fail with BadRequest if the framework is in use by any MSELs, data fields, or teams.
+ * @param id The id of the Competency Framework to delete
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public deleteCompetencyFramework(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public deleteCompetencyFramework(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteCompetencyFramework(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteCompetencyFramework(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling deleteCompetencyFramework.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'application/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('delete', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Gets a specific Competency Framework by id
+ * Returns the framework with all competencies and relationships.
+ * @param id The id of the Competency Framework
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getCompetencyFramework(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public getCompetencyFramework(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getCompetencyFramework(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getCompetencyFramework(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling getCompetencyFramework.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Gets all Competency Frameworks
+ * Returns a list of all competency frameworks (without competencies).
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getCompetencyFrameworks(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getCompetencyFrameworks(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getCompetencyFrameworks(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getCompetencyFrameworks(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request>('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Imports a Competency Framework from a Moodle-format CSV
+ * Accepts a CSV file in the Moodle lpimportcsv 14-column format. Creates the framework, all competencies with hierarchy, and cross-reference relationships.
+ * @param source Framework source (e.g. \"NICE\", \"DCWF\")
+ * @param version Framework version (e.g. \"5.1\")
+ * @param file The CSV file
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public importCompetencyFramework(source?: string, version?: string, file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public importCompetencyFramework(source?: string, version?: string, file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public importCompetencyFramework(source?: string, version?: string, file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public importCompetencyFramework(source?: string, version?: string, file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarQueryParameters = new HttpParams({encoder: this.encoder});
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ source, 'source');
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ version, 'version');
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/import`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ params: localVarQueryParameters,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Imports a Competency Framework from a NICE-format JSON file
+ * Accepts a JSON file in the NICE/NIST CPRT format (response.elements with documents, elements, and relationships). Creates the framework, all competencies with hierarchy, and work-role-to-TKSA relationships.
+ * @param file The JSON file
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public importCompetencyFrameworkJson(file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public importCompetencyFrameworkJson(file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public importCompetencyFrameworkJson(file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public importCompetencyFrameworkJson(file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/import-json`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Imports a Competency Framework from a DCWF-format XLSX file
+ * Accepts an XLSX file with columns: ID, Name, Description, ParentID, RelatedIDs. Creates the framework, all competencies with hierarchy, and cross-reference relationships.
+ * @param source Framework source (e.g. \"DCWF\")
+ * @param version Framework version (e.g. \"1.0\")
+ * @param file The XLSX file
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public importCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public importCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public importCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public importCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarQueryParameters = new HttpParams({encoder: this.encoder});
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ source, 'source');
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ version, 'version');
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/import-xlsx`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ params: localVarQueryParameters,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Preview a Competency Framework from a Moodle CSV file
+ * Returns preview information: element counts, relationships, source/version.
+ * @param source Framework source (e.g. \"NICE\", \"DCWF\")
+ * @param version Framework version (e.g. \"5.1\")
+ * @param file The CSV file
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public previewCompetencyFrameworkCsv(source?: string, version?: string, file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public previewCompetencyFrameworkCsv(source?: string, version?: string, file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewCompetencyFrameworkCsv(source?: string, version?: string, file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewCompetencyFrameworkCsv(source?: string, version?: string, file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarQueryParameters = new HttpParams({encoder: this.encoder});
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ source, 'source');
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ version, 'version');
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/preview-csv`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ params: localVarQueryParameters,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Preview a Competency Framework from a NICE JSON file
+ * Returns preview information: element counts, relationships, source/version.
+ * @param file The JSON file
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public previewCompetencyFrameworkJson(file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public previewCompetencyFrameworkJson(file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewCompetencyFrameworkJson(file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewCompetencyFrameworkJson(file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/preview-json`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Preview a Competency Framework from a DCWF XLSX file
+ * Returns preview information: element counts, relationships, source/version.
+ * @param source Framework source (e.g. \"DCWF\")
+ * @param version Framework version (e.g. \"1.0\")
+ * @param file The XLSX file
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public previewCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public previewCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewCompetencyFrameworkXlsx(source?: string, version?: string, file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarQueryParameters = new HttpParams({encoder: this.encoder});
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ source, 'source');
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters,
+ version, 'version');
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/preview-xlsx`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ params: localVarQueryParameters,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Updates a Competency
+ * @param competencyId The id of the Competency to update
+ * @param competency The updated data
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public updateCompetency(competencyId: string, competency?: Competency, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public updateCompetency(competencyId: string, competency?: Competency, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public updateCompetency(competencyId: string, competency?: Competency, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public updateCompetency(competencyId: string, competency?: Competency, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (competencyId === null || competencyId === undefined) {
+ throw new Error('Required parameter competencyId was null or undefined when calling updateCompetency.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencies/${this.configuration.encodeParam({name: "competencyId", value: competencyId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('put', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: competency,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Updates a Competency Framework
+ * @param id The id of the CompetencyFramework to update
+ * @param competencyFramework The updated data
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public updateCompetencyFramework(id: string, competencyFramework?: CompetencyFramework, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public updateCompetencyFramework(id: string, competencyFramework?: CompetencyFramework, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public updateCompetencyFramework(id: string, competencyFramework?: CompetencyFramework, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public updateCompetencyFramework(id: string, competencyFramework?: CompetencyFramework, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling updateCompetencyFramework.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/competencyframeworks/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('put', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: competencyFramework,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+}
diff --git a/src/app/generated/blueprint.api/api/dataOption.service.ts b/src/app/generated/blueprint.api/api/dataOption.service.ts
index a2f90342..35e9d614 100644
--- a/src/app/generated/blueprint.api/api/dataOption.service.ts
+++ b/src/app/generated/blueprint.api/api/dataOption.service.ts
@@ -25,6 +25,8 @@ import { Observable } from 'rxjs';
// @ts-ignore
import { DataOption } from '../model/dataOption';
// @ts-ignore
+import { DataOptionImportPreview } from '../model/dataOptionImportPreview';
+// @ts-ignore
import { ProblemDetails } from '../model/problemDetails';
// @ts-ignore
@@ -350,6 +352,90 @@ export class DataOptionService extends BaseService {
);
}
+ /**
+ * Preview imported DataOptions from a file
+ * Parses a JSON, CSV, or XLSX file and returns a preview of data options that would be imported. Shows which options already exist and which are new. Supports formats: JSON (arrays or NICE Framework), CSV, XLSX/XLS. <para /> Accessible only to a ContentDeveloper or an Administrator
+ * @param dataFieldId The id of the DataField to import options for
+ * @param file The file to parse (JSON, CSV, or XLSX)
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public previewDataOptionImport(dataFieldId: string, file?: Blob, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public previewDataOptionImport(dataFieldId: string, file?: Blob, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewDataOptionImport(dataFieldId: string, file?: Blob, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public previewDataOptionImport(dataFieldId: string, file?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (dataFieldId === null || dataFieldId === undefined) {
+ throw new Error('Required parameter dataFieldId was null or undefined when calling previewDataOptionImport.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'multipart/form-data'
+ ];
+
+ const canConsumeForm = this.canConsumeForm(consumes);
+
+ let localVarFormParams: { append(param: string, value: any): any; };
+ let localVarUseForm = false;
+ let localVarConvertFormParamsToString = false;
+ // use FormData to transmit files using content-type "multipart/form-data"
+ // see https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
+ localVarUseForm = canConsumeForm;
+ if (localVarUseForm) {
+ localVarFormParams = new FormData();
+ } else {
+ localVarFormParams = new HttpParams({encoder: this.encoder});
+ }
+
+ if (file !== undefined) {
+ localVarFormParams = localVarFormParams.append('file', file) as any || localVarFormParams;
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/datafields/${this.configuration.encodeParam({name: "dataFieldId", value: dataFieldId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/options/preview`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: localVarConvertFormParamsToString ? localVarFormParams.toString() : localVarFormParams,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
/**
* Updates a DataOption
* Updates a DataOption with the attributes specified. The ID from the route MUST MATCH the ID contained in the dataOption parameter <para /> Accessible only to a ContentDeveloper or an Administrator
diff --git a/src/app/generated/blueprint.api/api/lmt.service.ts b/src/app/generated/blueprint.api/api/lmt.service.ts
new file mode 100644
index 00000000..251f003c
--- /dev/null
+++ b/src/app/generated/blueprint.api/api/lmt.service.ts
@@ -0,0 +1,104 @@
+/*
+ Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+ Released under a MIT (SEI)-style license. See LICENSE.md in the
+ project root for license information.
+*/
+
+/**
+ * Blueprint API
+ *
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+/* tslint:disable:no-unused-variable member-ordering */
+
+import { Inject, Injectable, Optional } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams,
+ HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
+ } from '@angular/common/http';
+import { CustomHttpParameterCodec } from '../encoder';
+import { Observable } from 'rxjs';
+
+// @ts-ignore
+import { ProblemDetails } from '../model/problemDetails';
+
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
+import { Configuration } from '../configuration';
+import { BaseService } from '../api.base.service';
+
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class LmtService extends BaseService {
+
+ constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
+ super(basePath, configuration);
+ }
+
+ /**
+ * Gets IEEE 2881 (LMT) metadata for an MSEL as JSON-LD
+ * Returns LMT (Learning Metadata) as JSON-LD conforming to IEEE 2881 schema. This endpoint is publicly accessible to enable catalog discovery by LMS systems, PCTE registries, and other TLA components. The JSON-LD includes: - Exercise name, description, objectives - Competencies assessed (lrmi:assesses from MselCompetency associations) - Educational metadata (difficulty, purpose, mode, keywords) - Prerequisites (if configured) Any LMS that supports LTI 1.3 Deep Linking or Content-Item can consume this metadata to auto-link competencies, tags, and prerequisites when importing the exercise.
+ * @param mselId
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getLmtResource(mselId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public getLmtResource(mselId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getLmtResource(mselId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getLmtResource(mselId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (mselId === null || mselId === undefined) {
+ throw new Error('Required parameter mselId was null or undefined when calling getLmtResource.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/lmt/resource/${this.configuration.encodeParam({name: "mselId", value: mselId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+}
diff --git a/src/app/generated/blueprint.api/api/msel.service.ts b/src/app/generated/blueprint.api/api/msel.service.ts
index aa8514c0..c04734d0 100644
--- a/src/app/generated/blueprint.api/api/msel.service.ts
+++ b/src/app/generated/blueprint.api/api/msel.service.ts
@@ -175,6 +175,64 @@ export class MselService extends BaseService {
);
}
+ /**
+ * Cancel pushing integrations and remove any partial integrations
+ * Cancels an in-progress integration push and removes any partially created integrations <para /> Accessible only to a ContentDeveloper or MSEL owner
+ * @param id The id of the MSEL
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public cancelIntegrations(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public cancelIntegrations(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public cancelIntegrations(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public cancelIntegrations(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling cancelIntegrations.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'application/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/msels/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/integrations/cancel`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
/**
* Creates a new MSEL by copying an existing MSEL
* Creates a new MSEL from the specified existing MSEL <para /> Accessible only to a ContentDeveloper or an Administrator
@@ -1066,49 +1124,10 @@ export class MselService extends BaseService {
);
}
- /**
- * Cancel pushing integrations and remove any partial integrations
- * Cancels an in-progress integration push and removes any partially created integrations <para /> Accessible only to a ContentDeveloper or MSEL owner
- * @param id The id of the MSEL
- * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
- * @param reportProgress flag to report request and response progress.
- */
- public cancelIntegrations(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable;
- public cancelIntegrations(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>;
- public cancelIntegrations(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>;
- public cancelIntegrations(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable {
- if (id === null || id === undefined) {
- throw new Error('Required parameter id was null or undefined when calling cancelIntegrations.');
- }
-
- let localVarHeaders = this.defaultHeaders;
-
- // authentication (oauth2) required
- localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
-
- const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
-
- const localVarTransferCache: boolean = options?.transferCache ?? true;
-
- let localVarPath = `/api/msels/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/integrations/cancel`;
- const { basePath, withCredentials } = this.configuration;
- return this.httpClient.request('post', `${basePath}${localVarPath}`,
- {
- context: localVarHttpContext,
- responseType: 'text' as 'json',
- ...(withCredentials ? { withCredentials } : {}),
- headers: localVarHeaders,
- observe: observe,
- transferCache: localVarTransferCache,
- reportProgress: reportProgress
- }
- );
- }
-
/**
* Push Integrations
* Pushes all MSEL Integrations to the associated applications <para /> Accessible only to a ContentDeveloper or MSEL owner
- * @param id
+ * @param id
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
diff --git a/src/app/generated/blueprint.api/api/mselCompetency.service.ts b/src/app/generated/blueprint.api/api/mselCompetency.service.ts
new file mode 100644
index 00000000..12a23ab3
--- /dev/null
+++ b/src/app/generated/blueprint.api/api/mselCompetency.service.ts
@@ -0,0 +1,355 @@
+/*
+ Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+ Released under a MIT (SEI)-style license. See LICENSE.md in the
+ project root for license information.
+*/
+
+/**
+ * Blueprint API
+ *
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+/* tslint:disable:no-unused-variable member-ordering */
+
+import { Inject, Injectable, Optional } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams,
+ HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
+ } from '@angular/common/http';
+import { CustomHttpParameterCodec } from '../encoder';
+import { Observable } from 'rxjs';
+
+// @ts-ignore
+import { MselCompetency } from '../model/mselCompetency';
+// @ts-ignore
+import { ProblemDetails } from '../model/problemDetails';
+
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
+import { Configuration } from '../configuration';
+import { BaseService } from '../api.base.service';
+
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class MselCompetencyService extends BaseService {
+
+ constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
+ super(basePath, configuration);
+ }
+
+ /**
+ * Creates a new MselCompetency
+ * Creates a new MselCompetency with the attributes specified <para />
+ * @param mselCompetency The data to create the MselCompetency with
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public createMselCompetency(mselCompetency?: MselCompetency, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public createMselCompetency(mselCompetency?: MselCompetency, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createMselCompetency(mselCompetency?: MselCompetency, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createMselCompetency(mselCompetency?: MselCompetency, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ // to determine the Content-Type header
+ const consumes: string[] = [
+ 'application/json',
+ 'text/json',
+ 'application/*+json'
+ ];
+ const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
+ if (httpContentTypeSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
+ }
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/mselcompetencies`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('post', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ body: mselCompetency,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Deletes a MselCompetency
+ * Deletes a MselCompetency with the specified id <para />
+ * @param id The id of the MselCompetency to delete
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public deleteMselCompetency(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public deleteMselCompetency(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteMselCompetency(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteMselCompetency(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling deleteMselCompetency.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'application/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/mselcompetencies/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('delete', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Deletes a MselCompetency by msel ID and competency ID
+ * Deletes a MselCompetency with the specified msel ID and competency ID <para />
+ * @param mselId ID of a msel.
+ * @param competencyId ID of a competency.
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public deleteMselCompetencyByIds(mselId: string, competencyId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public deleteMselCompetencyByIds(mselId: string, competencyId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteMselCompetencyByIds(mselId: string, competencyId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public deleteMselCompetencyByIds(mselId: string, competencyId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (mselId === null || mselId === undefined) {
+ throw new Error('Required parameter mselId was null or undefined when calling deleteMselCompetencyByIds.');
+ }
+ if (competencyId === null || competencyId === undefined) {
+ throw new Error('Required parameter competencyId was null or undefined when calling deleteMselCompetencyByIds.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'application/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/msels/${this.configuration.encodeParam({name: "mselId", value: mselId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/competencies/${this.configuration.encodeParam({name: "competencyId", value: competencyId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('delete', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Gets all MselCompetencies for a MSEL
+ * Returns a list of all of the MselCompetencies for the msel.
+ * @param mselId The id of the Msel
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getMselCompetencies(mselId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getMselCompetencies(mselId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getMselCompetencies(mselId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>>;
+ public getMselCompetencies(mselId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (mselId === null || mselId === undefined) {
+ throw new Error('Required parameter mselId was null or undefined when calling getMselCompetencies.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/msels/${this.configuration.encodeParam({name: "mselId", value: mselId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}/mselcompetencies`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request>('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+ /**
+ * Gets a specific MselCompetency by id
+ * Returns the MselCompetency with the id specified <para />
+ * @param id The id of the MselCompetency
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public getMselCompetency(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public getMselCompetency(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getMselCompetency(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getMselCompetency(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable {
+ if (id === null || id === undefined) {
+ throw new Error('Required parameter id was null or undefined when calling getMselCompetency.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ // authentication (oauth2) required
+ localVarHeaders = this.configuration.addCredentialToHeaders('oauth2', 'Authorization', localVarHeaders, 'Bearer ');
+
+ const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
+ 'text/plain',
+ 'application/json',
+ 'text/json'
+ ]);
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
+
+ const localVarTransferCache: boolean = options?.transferCache ?? true;
+
+
+ let responseType_: 'text' | 'json' | 'blob' = 'json';
+ if (localVarHttpHeaderAcceptSelected) {
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
+ responseType_ = 'json';
+ } else {
+ responseType_ = 'blob';
+ }
+ }
+
+ let localVarPath = `/api/mselcompetencies/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid"})}`;
+ const { basePath, withCredentials } = this.configuration;
+ return this.httpClient.request('get', `${basePath}${localVarPath}`,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ ...(withCredentials ? { withCredentials } : {}),
+ headers: localVarHeaders,
+ observe: observe,
+ transferCache: localVarTransferCache,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
+}
diff --git a/src/app/generated/blueprint.api/api/proficiencyLevel.service.ts b/src/app/generated/blueprint.api/api/proficiencyLevel.service.ts
new file mode 100644
index 00000000..0a0909bd
--- /dev/null
+++ b/src/app/generated/blueprint.api/api/proficiencyLevel.service.ts
@@ -0,0 +1,361 @@
+/*
+ Copyright 2026 Carnegie Mellon University. All Rights Reserved.
+ Released under a MIT (SEI)-style license. See LICENSE.md in the
+ project root for license information.
+*/
+
+/**
+ * Blueprint API
+ *
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+/* tslint:disable:no-unused-variable member-ordering */
+
+import { Inject, Injectable, Optional } from '@angular/core';
+import { HttpClient, HttpHeaders, HttpParams,
+ HttpResponse, HttpEvent, HttpParameterCodec, HttpContext
+ } from '@angular/common/http';
+import { CustomHttpParameterCodec } from '../encoder';
+import { Observable } from 'rxjs';
+
+// @ts-ignore
+import { ProblemDetails } from '../model/problemDetails';
+// @ts-ignore
+import { ProficiencyLevel } from '../model/proficiencyLevel';
+
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
+import { Configuration } from '../configuration';
+import { BaseService } from '../api.base.service';
+
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ProficiencyLevelService extends BaseService {
+
+ constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) {
+ super(basePath, configuration);
+ }
+
+ /**
+ * Creates a new ProficiencyLevel
+ * @param proficiencyLevel
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public createProficiencyLevel(proficiencyLevel?: ProficiencyLevel, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public createProficiencyLevel(proficiencyLevel?: ProficiencyLevel, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createProficiencyLevel(proficiencyLevel?: ProficiencyLevel, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createProficiencyLevel(proficiencyLevel?: ProficiencyLevel, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'text/plain' | 'application/json' | 'text/json', context?: HttpContext, transferCache?: boolean}): Observable