diff --git a/src/app/app-products.module.ts b/src/app/app-products.module.ts
index 7ac9468423b6ce9eba80f9217f8ea5fe95d7c3a2..d26ba40165e2953f0d7adc3282bd6d8430053b66 100644
--- a/src/app/app-products.module.ts
+++ b/src/app/app-products.module.ts
@@ -10,6 +10,8 @@ import { ListProductSpecsComponent } from './p_product/admin/productCatalogManag
import { EditProductCatalogsComponent } from './p_product/admin/productCatalogManagement/edit-product-catalogs/edit-product-catalogs.component';
import { EditProductCategoriesComponent } from './p_product/admin/productCatalogManagement/edit-product-categories/edit-product-categories.component';
import { EditProductSpecsComponent } from './p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component';
+import { EditProductSpecCharacteristicsComponent } from './p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component';
+import { DeleteProductSpecCharacteristicsComponent } from './p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component';
import { DeleteProductCatalogsComponent } from './p_product/admin/productCatalogManagement/delete-product-catalogs/delete-product-catalogs.component';
import { DeleteProductCategoriesComponent } from './p_product/admin/productCatalogManagement/delete-product-categories/delete-product-categories.component';
import { DeleteProductSpecsComponent } from './p_product/admin/productCatalogManagement/delete-product-specs/delete-product-specs.component';
@@ -21,7 +23,7 @@ import { EditProductOfferingsComponent } from './p_product/admin/productCatalogM
import { PreviewMarketPlaceItemComponent } from './p_product/marketplace/preview-market-place-item/preview-market-place-item.component';
import { AssignProductOfferingsComponent } from './p_product/admin/productCatalogManagement/edit-product-categories/assign-product-offerings/assign-product-offerings.component';
import { AssignSubcategoriesComponent } from './p_product/admin/productCatalogManagement/edit-product-categories/assign-subcategories/assign-subcategories.component';
-
+import { ConfirmCharacteristicAssignmentComponent } from './p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component';
@NgModule({
@@ -34,10 +36,13 @@ import { AssignSubcategoriesComponent } from './p_product/admin/productCatalogMa
EditProductCatalogsComponent,
EditProductCategoriesComponent,
EditProductSpecsComponent,
+ EditProductSpecCharacteristicsComponent,
+ DeleteProductSpecCharacteristicsComponent,
DeleteProductCatalogsComponent,
DeleteProductCategoriesComponent,
DeleteProductSpecsComponent,
AssignServiceSpecificationComponent,
+ ConfirmCharacteristicAssignmentComponent,
ListProductOfferingsComponent,
DeleteProductOfferingComponent,
EditProductOfferingsComponent,
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.html b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.html
index 3e3dd66de889b9e7aa8944ce51e4f50613acf112..4eae4f5292bddf312c4ce30cb272a3727db9ccc7 100644
--- a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.html
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.html
@@ -13,7 +13,10 @@
- {{spec.name}}
+ {{spec.name}}
+
+ {{ spec['@type'] === 'CustomerFacingServiceSpecification' ? 'CFSS' : (spec['@type'] === 'ResourceFacingServiceSpecification' ? 'RFSS' : 'null') }}
+
@@ -26,7 +29,11 @@
Assigned Service Specifications |
- {{element.name}} |
+ {{element.name}}
+
+ {{ element['@type'] === 'CustomerFacingServiceSpecification' ? 'CFSS' : (element['@type'] === 'ResourceFacingServiceSpecification' ? 'RFSS' : 'null') }}
+
+
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.ts b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.ts
index ede201f82c0daac51201b7b30a4631eaff3ec863..4dda413e18881854c51c7c283d35bec8f8c3059d 100644
--- a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.ts
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/assign-service-specification.component.ts
@@ -1,15 +1,18 @@
import { Component, OnInit, Inject, ViewChild, ElementRef } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
-import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
-import { Observable } from 'rxjs';
+import { ToastrService } from 'ngx-toastr';
+import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators'
import { ProductSpecification, ProductSpecificationUpdate } from 'src/app/openApis/productCatalogManagement/models';
import { ProductSpecificationService } from 'src/app/openApis/productCatalogManagement/services';
import { ServiceSpecification } from 'src/app/openApis/serviceCatalogManagement/models';
import { ServiceSpecificationService } from 'src/app/openApis/serviceCatalogManagement/services';
+import { ConfirmCharacteristicAssignmentComponent } from './confirm-characteristic-assignment/confirm-characteristic-assignment.component';
+
@Component({
selector: 'app-assign-service-specification',
@@ -24,7 +27,10 @@ export class AssignServiceSpecificationComponent implements OnInit {
},
private dialogRef: MatDialogRef,
private productSpecService: ProductSpecificationService,
- private serviceSpecService: ServiceSpecificationService
+ private serviceSpecService: ServiceSpecificationService,
+ private toast: ToastrService,
+ private dialog: MatDialog
+
) { }
@ViewChild('specInput') specInput: ElementRef;
@@ -37,44 +43,78 @@ export class AssignServiceSpecificationComponent implements OnInit {
@ViewChild(MatSort, {static: true}) sort: MatSort;
specInputCtrl = new UntypedFormControl();
- nonSelectedSpecs: ProductSpecification[]
+ nonSelectedSpecs: ProductSpecification[] = [];
filteredSpecs$: Observable
selectedSpecs: ProductSpecification[] = []
+ // the subject holds the data when it arrives from the api
+ private availableSpecsSubject = new BehaviorSubject([]);
+
ngOnInit(): void {
- this.listServiceSpecs()
+ this.listServiceSpecs();
+
+ // for the filtering to work when user starts typing in the input
+ this.filteredSpecs$ = combineLatest([
+ this.specInputCtrl.valueChanges.pipe(startWith('')),
+ this.availableSpecsSubject.asObservable()
+ ]).pipe(
+ map(([inputValue, specs]) => {
+ const filterValue = typeof inputValue === 'string' ? inputValue.toLowerCase() : '';
+ return this.performFilter(filterValue, specs);
+ })
+ );
}
listServiceSpecs() {
this.serviceSpecService.listServiceSpecification({}).subscribe(
- data => this.nonSelectedSpecs = data,
- error => console.error(error),
- () => {
- //remove self from available spec list as well as the allready assigned specs
- const initiallyAssignedSpecIDs: string[] = this.data.productSpec.serviceSpecification.map(el => el.id)
- this.nonSelectedSpecs = this.nonSelectedSpecs.filter(spec => !initiallyAssignedSpecIDs.includes(spec.id))
-
- this.selectedSpecs = this.data.productSpec.serviceSpecification.slice()
- this.dataSource.data = this.selectedSpecs
- this.dataSource.sort = this.sort
-
- this.filteredSpecs$ = this.specInputCtrl.valueChanges.pipe(
- startWith(null),
- map( (spec: string | ServiceSpecification) => typeof(spec) === 'string' ? this._filter(spec) : this.nonSelectedSpecs.slice() )
- )
- }
- )
+ data => {
+ // remove the already assigned specs
+ const initiallyAssignedIDs = this.data.productSpec.serviceSpecification.map(el => el.id);
+ const filteredData = data.filter(spec => !initiallyAssignedIDs.includes(spec.id));
+
+ // push data into subject
+ this.availableSpecsSubject.next(filteredData);
+
+ this.selectedSpecs = this.data.productSpec.serviceSpecification.slice();
+ this.dataSource.data = this.selectedSpecs;
+ this.dataSource.sort = this.sort;
+ },
+ error => this.toast.error('Failed to fetch Service Specifications', error)
+ );
}
- selected(event: MatAutocompleteSelectedEvent): void {
- this.selectedSpecs.push(event.option.value);
- this.dataSource.data = this.selectedSpecs
-
- this.nonSelectedSpecs = this.nonSelectedSpecs.filter(el => el.name != event.option.value.name)
+ private performFilter(filterValue: string, specs: ProductSpecification[]): ProductSpecification[] {
+ if (!filterValue) {
+ return specs;
+ }
+ return specs.filter(spec => {
+ const name = spec.name ? spec.name.toLowerCase() : '';
+ return name.indexOf(filterValue) !== -1;
+ });
+ }
+ selected(event: MatAutocompleteSelectedEvent): void {
+ const selectedLiteSpec = event.option.value;
+ //retrieve full service specs details, @type and spec characteristics once a service spec is selected
this.specInput.nativeElement.value = '';
this.specInputCtrl.setValue(null);
+
+ this.serviceSpecService.retrieveServiceSpecification({
+ id: selectedLiteSpec.id
+ }).subscribe(
+ (fullSpec: ServiceSpecification) => {
+
+ this.selectedSpecs.push(fullSpec);
+ this.dataSource.data = this.selectedSpecs;
+ this.nonSelectedSpecs = this.nonSelectedSpecs.filter(
+ el => el.name != selectedLiteSpec.name
+ );
+ },
+ (error) => {
+ this.toast.error('Failed to fetch full service specification details', error);
+ }
+ );
}
private _filter(value: string): ProductSpecification[] {
@@ -103,16 +143,174 @@ export class AssignServiceSpecificationComponent implements OnInit {
confirmAssignment() {
- const updateRelationshipsObj: ProductSpecificationUpdate = {
- serviceSpecification: this.selectedSpecs.map(spec =>{ return {id: spec.id, name: spec.name}})
+ const hasConfigurableCFSS = this.selectedSpecs.some(spec => {
+ const s = spec as any;
+ const type = s['@type'] || 'ServiceSpecification';
+ const chars = s.serviceSpecCharacteristic || [];
+ const hasConfigurable = chars.some(c => c.configurable === true);
+
+ return type === 'CustomerFacingServiceSpecification' && hasConfigurable;
+ });
+
+ if (hasConfigurableCFSS) {
+ // Open the dialog to ask the user
+ const dialogRef = this.dialog.open(ConfirmCharacteristicAssignmentComponent, {
+ width: '600px',
+ disableClose: true
+ });
+
+ dialogRef.afterClosed().subscribe(result => {
+ this.executeAssignment(result);
+ });
+ }
+ else {
+ this.executeAssignment(null);
}
+ }
- this.productSpecService.patchProductSpecification({id: this.data.productSpec.id, productSpecification: updateRelationshipsObj}).subscribe(
- data => {},
- error => console.error(error),
- () => {this.dialogRef.close('updated')}
- )
+ executeAssignment(importCharacteristics: boolean) {
+ const serviceRefs: any[] = [];
+
+ let tempCharacteristics = [...(this.data.productSpec.productSpecCharacteristic || [])];
+
+ this.selectedSpecs.forEach(spec => {
+ const sourceSpec = spec as any;
+ const type = sourceSpec['@type'] || 'ServiceSpecification';
+
+ serviceRefs.push({
+ id: sourceSpec.id,
+ name: sourceSpec.name,
+ '@type': type
+ });
+
+ // If user said YES, we manually prepare the chars to add
+ if (importCharacteristics) {
+ const serviceChars = sourceSpec.serviceSpecCharacteristic || [];
+ const configurableChars = serviceChars.filter(char => char.configurable === true);
+
+ configurableChars.forEach(sourceChar => {
+ const cleanValues = (sourceChar.serviceSpecCharacteristicValue || []).map(val => ({
+ valueType: val.valueType,
+ isDefault: val.isDefault,
+ value: val.value,
+ unitOfMeasure: val.unitOfMeasure,
+ valueFrom: val.valueFrom,
+ valueTo: val.valueTo,
+ rangeInterval: val.rangeInterval,
+ alias: val.alias
+ }));
+
+ const newCharObj = {
+ name: sourceChar.name,
+ description: sourceChar.description,
+ valueType: sourceChar.valueType,
+ configurable: sourceChar.configurable,
+ extensible: sourceChar.extensible,
+ regex: sourceChar.regex,
+ uiType: sourceChar.uiType,
+ unitOfMeasure: sourceChar.unitOfMeasure,
+ defaultValue: sourceChar.defaultValue,
+ minCardinality: sourceChar.minCardinality,
+ maxCardinality: sourceChar.maxCardinality,
+ productSpecCharacteristicValue: cleanValues,
+ productSpecCharRelationship: [],
+ validFor: sourceChar.validFor || { startDateTime: new Date().toISOString(), endDateTime: null },
+ lifecycleStatus: "In design",
+ '@type': 'ProductSpecCharacteristic',
+ '@baseType': 'BaseEntity',
+ '@schemaLocation': null
+ };
+
+ // if characteristic already exists by name, overwrite it anyway
+ const existingCharIndex = tempCharacteristics.findIndex(c => c.name === sourceChar.name);
+ if (existingCharIndex > -1) {
+ tempCharacteristics[existingCharIndex] = { ...newCharObj, uuid: tempCharacteristics[existingCharIndex].uuid };
+ } else {
+ tempCharacteristics.push(newCharObj);
+ }
+ });
+ }
+ });
+
+ const payloadStep1: ProductSpecificationUpdate = {
+ serviceSpecification: serviceRefs,
+ productSpecCharacteristic: tempCharacteristics
+ };
+
+ this.productSpecService.patchProductSpecification({
+ id: this.data.productSpec.id,
+ productSpecification: payloadStep1
+ }).subscribe(
+ (updatedSpec) => {
+
+ // If user said NO, and we have CFSS, we need to check if backend added them
+ // and remove them (patching the list without them)
+ if (!importCharacteristics) {
+ this.removeUnwantedCharacteristics(updatedSpec);
+ } else {
+ this.toast.success('Product Specification updated successfully');
+ this.dialogRef.close('updated');
+ }
+ },
+ error => {
+ this.toast.error('Failed to update Product Specification');
+ }
+ );
}
+ removeUnwantedCharacteristics(spec: ProductSpecification) {
+ const unwantedNames: string[] = [];
+
+ this.selectedSpecs.forEach(s => {
+ const sourceSpec = s as any;
+ if (sourceSpec['@type'] === 'CustomerFacingServiceSpecification') {
+ const chars = sourceSpec.serviceSpecCharacteristic || [];
+ chars.filter(c => c.configurable).forEach(c => unwantedNames.push(c.name));
+ }
+ });
+
+ const originalNames = (this.data.productSpec.productSpecCharacteristic || []).map(c => c.name);
+
+ const currentChars = spec.productSpecCharacteristic || [];
+
+ const newSpecCharacteristicArray = currentChars.filter(char => {
+ const isUnwanted = unwantedNames.includes(char.name);
+ const wasPreExisting = originalNames.includes(char.name);
+
+ // If it is NOT in the unwanted list, keep it.
+ if (!isUnwanted) return true;
+
+ // If it IS in the unwanted list, BUT it existed before we started, keep it.
+ if (wasPreExisting) return true;
+
+ return false;
+ });
+
+ // If there is nothing to change (lengths match), just close
+ if (newSpecCharacteristicArray.length === currentChars.length) {
+ this.toast.success('Product Specification updated successfully');
+ this.dialogRef.close('updated');
+ return;
+ }
+
+ const updateCharacteristicObj: ProductSpecificationUpdate = {
+ productSpecCharacteristic: newSpecCharacteristicArray
+ }
+
+ this.productSpecService.patchProductSpecification({
+ id: spec.id,
+ productSpecification: updateCharacteristicObj
+ }).subscribe(
+ data => {},
+ error => {
+ this.toast.warning('Service assigned, but failed to clean up auto-assigned characteristics.');
+ this.dialogRef.close('updated');
+ },
+ () => {
+ this.toast.success("Product Specification Characteristics list was successfully updated");
+ this.dialogRef.close('updated');
+ }
+ )
+ }
}
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component.html b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..46b908977beefa537c4f7e00cb71852d1ba504cf
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component.html
@@ -0,0 +1,13 @@
+Import Characteristics?
+
+
+ The selected CustomerFacingServiceSpecification(s) contain configurable characteristics.
+
+
+ Do you want to automatically add these as Product Characteristics to the Product Specification?
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component.ts b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e8910086710cb54abef00d7d57d75378ec35f630
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/assign-service-specification/confirm-characteristic-assignment/confirm-characteristic-assignment.component.ts
@@ -0,0 +1,22 @@
+import { Component } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+
+@Component({
+ selector: 'app-confirm-characteristic-assignment',
+ templateUrl: './confirm-characteristic-assignment.component.html',
+ styles: []
+})
+export class ConfirmCharacteristicAssignmentComponent {
+
+ constructor(
+ public dialogRef: MatDialogRef
+ ) {}
+
+ onNo(): void {
+ this.dialogRef.close(false);
+ }
+
+ onYes(): void {
+ this.dialogRef.close(true);
+ }
+}
\ No newline at end of file
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.html b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..29077a3548386a5a16c9a8a8497a550dbd36f251
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.html
@@ -0,0 +1,12 @@
+
+
+ Confirm deletion
+
+
+ Are you sure you want to permanently delete characteristic {{data.specToBeDeleted.name}} ?
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.scss b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.ts b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29bafc126cbdd13e9fbe09ae2f7a8c550adf671f
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/delete-product-spec-characteristics/delete-product-spec-characteristics.component.ts
@@ -0,0 +1,47 @@
+import { trigger } from "@angular/animations";
+import { Component, Inject, OnInit } from "@angular/core";
+import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
+import { Toast, ToastrService } from "ngx-toastr";
+import { ProductSpecification, ProductSpecificationCharacteristicRes, ProductSpecificationUpdate } from "src/app/openApis/productCatalogManagement/models";
+import { ProductSpecificationService } from "src/app/openApis/productCatalogManagement/services";
+import { fadeIn, simpleFade } from "src/app/shared/animations/animations";
+
+
+@Component({
+ selector: 'app-delete-product-spec-characteristics',
+ templateUrl: './delete-product-spec-characteristics.component.html',
+ styleUrls: ['./delete-product-spec-characteristics.component.scss'],
+ animations: [trigger('fadeIn', fadeIn()), trigger('simpleFade', simpleFade())]
+})
+export class DeleteProductSpecCharacteristicsComponent implements OnInit {
+ constructor(
+ @Inject(MAT_DIALOG_DATA) public data: {
+ productSpec: ProductSpecification
+ productSpecCharacteristicArray:ProductSpecificationCharacteristicRes[],
+ specToBeDeleted: ProductSpecificationCharacteristicRes
+ },
+ private dialogRef: MatDialogRef,
+ private specService: ProductSpecificationService,
+ private toast: ToastrService
+ ) { }
+
+ ngOnInit() {
+ }
+
+ confirmDelete() {
+ // this.dialogRef.close('deleted')
+ const updateSpecObj: ProductSpecificationUpdate = {
+ productSpecCharacteristic: this.data.productSpecCharacteristicArray
+ }
+
+ this.specService.patchProductSpecification({ id: this.data.productSpec.id, productSpecification: updateSpecObj }).subscribe(
+ data => {},
+ error => this.toast.error("An error occurred while deleting the Product Specification Characteristic"),
+ () => this.dialogRef.close('deleted')
+ )
+ }
+
+ closeDialog() {
+ this.dialogRef.close()
+ }
+}
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.html b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..572842c071391be3fa6ea3cf928b6f3cf2fe393e
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.html
@@ -0,0 +1,229 @@
+
+
+
+
+ Design New Product Characteristic
+
+
+
+
+ Edit Product Characteristic
+
+
+
+
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.scss b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..42ca63a3ceb08d362330fcc53601054553700687
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.scss
@@ -0,0 +1,15 @@
+.form-array-container {
+ max-height: 250px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.characteristic-value-container {
+ // padding-top: 0.75rem;
+ border: 1px;
+ border-style: dashed;
+ border-color: #ccc;
+ margin-bottom: 0.5rem;
+ background-color: #fafaff;
+ border-radius: 5px;
+}
\ No newline at end of file
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.ts b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2408ed24dd4cb077f8afa55c64212de19ecb193
--- /dev/null
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-spec-characteristics/edit-product-spec-characteristics.component.ts
@@ -0,0 +1,212 @@
+import { Component, OnInit, Inject } from '@angular/core';
+import { MatCheckboxChange } from '@angular/material/checkbox';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { UntypedFormGroup, UntypedFormControl, UntypedFormArray } from '@angular/forms';
+import { ToastrService } from 'ngx-toastr';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { trigger } from '@angular/animations';
+import { fadeIn, simpleFade } from 'src/app/shared/animations/animations';
+import { ProductSpecification, ProductSpecificationCharacteristicRes, ProductSpecificationCharacteristicValue, ProductSpecificationUpdate } from 'src/app/openApis/productCatalogManagement/models';
+import { ProductSpecificationService } from 'src/app/openApis/productCatalogManagement/services';
+
+
+
+@Component({
+ selector: 'app-edit-product-spec-characteristics',
+ templateUrl: './edit-product-spec-characteristics.component.html',
+ styleUrls: ['./edit-product-spec-characteristics.component.scss'],
+ animations: [trigger('fadeIn', fadeIn()), trigger('simpleFade', simpleFade())]
+})
+export class EditProductSpecCharacteristicsComponent implements OnInit {
+
+ constructor(
+ @Inject(MAT_DIALOG_DATA) public data: {
+ productSpec: ProductSpecification,
+ specToBeUpdated: ProductSpecificationCharacteristicRes,
+ },
+ private dialogRef: MatDialogRef,
+ private specService: ProductSpecificationService,
+ private toast: ToastrService
+ ) { }
+
+ editFormCharacteristic = new UntypedFormGroup({
+ name: new UntypedFormControl(),
+ description: new UntypedFormControl(),
+ configurable: new UntypedFormControl(),
+ extensible: new UntypedFormControl(),
+ maxCardinality: new UntypedFormControl(1),
+ minCardinality: new UntypedFormControl(0),
+ validFor: new UntypedFormGroup({
+ endDateTime: new UntypedFormControl(new Date(new Date().setFullYear(new Date().getFullYear() + 20))),
+ startDateTime: new UntypedFormControl(new Date())
+ }),
+ valueType: new UntypedFormControl(),
+ productSpecCharacteristicValue: new UntypedFormArray([])
+ })
+
+ subValueTypeCtrl = new UntypedFormControl('INTEGER')
+
+ valueTypes = ['INTEGER', 'SMALLINT', 'LONGINT', 'FLOAT', 'BINARY', 'BOOLEAN', 'ARRAY', 'SET', 'TEXT', 'LONGTEXT', 'ENUM', 'TIMESTAMP']
+ subValueTypes = ['INTEGER', 'SMALLINT', 'LONGINT', 'FLOAT', 'BINARY', 'BOOLEAN', 'TEXT', 'LONGTEXT', 'TIMESTAMP']
+
+ // valueSubType = new FormControl()
+ subTypeSelection: boolean = false
+
+ newSpec: boolean = false
+
+ compDestroy$ = new Subject()
+
+ isCharValueBlockExpanded: boolean[] = []
+
+ ngOnInit(): void {
+ if (this.data.specToBeUpdated) {
+ if (!this.data.specToBeUpdated.validFor) this.data.specToBeUpdated.validFor = { endDateTime: null, startDateTime: null }
+ this.editFormCharacteristic.patchValue(this.data.specToBeUpdated)
+
+ const formArray = this.editFormCharacteristic.get('productSpecCharacteristicValue') as UntypedFormArray
+ this.data.specToBeUpdated.productSpecCharacteristicValue.forEach(val => {
+ formArray.push(this.updateFormArrayItem(val))
+ this.isCharValueBlockExpanded.push(false)
+ })
+
+ this.subValueTypeCtrl.patchValue(this.data.specToBeUpdated.productSpecCharacteristicValue[0].valueType)
+ if (['SET', 'ARRAY', 'ENUM'].includes(this.data.specToBeUpdated.valueType)) {
+ this.subTypeSelection = true
+ }
+ }
+
+ else { this.newSpec = true }
+
+ this.editFormCharacteristic.get('valueType').valueChanges.pipe(
+ takeUntil(this.compDestroy$)
+ )
+ .subscribe(
+ val => {
+ this.editFormCharacteristic.setControl('productSpecCharacteristicValue', new UntypedFormArray([]))
+ this.createFormArrayItem()
+
+
+ if (['SET', 'ARRAY', 'ENUM'].includes(val)) {
+ this.subTypeSelection = true
+ this.subValueTypeCtrl.valueChanges.pipe(
+ takeUntil(this.compDestroy$)
+ )
+ .subscribe(
+ subVal => {
+ this.editFormCharacteristic.setControl('productSpecCharacteristicValue', new UntypedFormArray([]))
+ this.createFormArrayItem()
+ }
+ )
+ } else {
+ this.subTypeSelection = false
+ }
+ // const formArray = this.editFormCharacteristic.get('serviceSpecCharacteristicValue') as FormArray
+ // if (val !== 'ARRAY' && val !=='ENUM' && val !=='SET') {
+ // this.subTypeSelection = false
+ // formArray.setControl(0,
+ // new FormGroup({
+ // value: new FormGroup({
+ // alias: new FormControl(),
+ // value: new FormControl(),
+ // }),
+ // unitOfMeasure: new FormControl(),
+ // isDefault: new FormControl(),
+ // valueType: new FormControl(this.editFormCharacteristic.get('valueType').value)
+ // }))
+ // }
+ }
+ )
+ }
+
+
+ updateFormArrayItem(CharValue: ProductSpecificationCharacteristicValue): UntypedFormGroup {
+ return new UntypedFormGroup({
+ value: new UntypedFormGroup({
+ alias: new UntypedFormControl(CharValue.value.alias),
+ value: new UntypedFormControl(CharValue.value.value),
+ }),
+ unitOfMeasure: new UntypedFormControl(CharValue.unitOfMeasure),
+ isDefault: new UntypedFormControl({ value: (CharValue.isDefault || this.data.specToBeUpdated.valueType === 'ARRAY'), disabled: this.data.specToBeUpdated.valueType === 'ARRAY' }),
+ valueType: new UntypedFormControl(CharValue.valueType)
+ })
+ }
+
+
+ createFormArrayItem() {
+ const formArray = this.editFormCharacteristic.get('productSpecCharacteristicValue') as UntypedFormArray
+
+ // let isDisabled: boolean = true
+ let subType: string = this.editFormCharacteristic.get('valueType').value
+
+ if (['SET', 'ARRAY', 'ENUM'].includes(this.editFormCharacteristic.get('valueType').value)) {
+ // isDisabled = false
+ subType = this.subValueTypeCtrl.value
+ }
+
+ formArray.push(
+ new UntypedFormGroup({
+ value: new UntypedFormGroup({
+ alias: new UntypedFormControl(),
+ value: new UntypedFormControl(),
+ }),
+ unitOfMeasure: new UntypedFormControl(),
+ isDefault: new UntypedFormControl({ value: this.editFormCharacteristic.get('valueType').value === 'ARRAY', disabled: this.editFormCharacteristic.get('valueType').value === 'ARRAY' }),
+ valueType: new UntypedFormControl(subType)
+ })
+ )
+ this.isCharValueBlockExpanded.push(false)
+ }
+
+ deleteFormArrayItem(index) {
+ const formArray = this.editFormCharacteristic.get('productSpecCharacteristicValue') as UntypedFormArray
+ formArray.removeAt(index)
+ this.isCharValueBlockExpanded.splice(index, 1)
+ }
+
+ expandCharValueBlock(index) {
+ this.isCharValueBlockExpanded[index] = !this.isCharValueBlockExpanded[index]
+ }
+
+ isDefaultCheckboxChanged(index, event: MatCheckboxChange) {
+ if (this.editFormCharacteristic.get('valueType').value === "ENUM" && event.checked) {
+ const formArray = this.editFormCharacteristic.get('productSpecCharacteristicValue') as UntypedFormArray
+ for (let i = 0; i < formArray.controls.length; i++) {
+ if (i !== index) formArray.controls[i].get('isDefault').setValue(false)
+ }
+ }
+ }
+
+
+ closeDialog() {
+ this.dialogRef.close()
+ }
+
+ submitDialog() {
+
+
+
+ if (this.newSpec) {
+ this.data.productSpec.productSpecCharacteristic.push(this.editFormCharacteristic.getRawValue())
+ } else {
+ const updateCharacteristIndex = this.data.productSpec.productSpecCharacteristic.findIndex(char => char.uuid === this.data.specToBeUpdated.uuid)
+ this.data.productSpec.productSpecCharacteristic[updateCharacteristIndex] = this.editFormCharacteristic.getRawValue()
+ }
+
+ const updateCharacteristicObj: ProductSpecificationUpdate = {
+ productSpecCharacteristic: this.data.productSpec.productSpecCharacteristic
+ }
+
+ this.specService.patchProductSpecification({ id: this.data.productSpec.id, productSpecification: updateCharacteristicObj }).subscribe(
+ data => { },
+ error => this.toast.error("An error occurred upon updating Spec Characteristics"),
+ () => { this.dialogRef.close('updated') }
+ )
+ }
+
+ ngOnDestroy(): void {
+ this.compDestroy$.next("destroyed")
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.html b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.html
index 29881af9047ac6fc0bef8413ff9fdddaf911fcd8..a942f958cc538a2de2ceb7e020ebd314768d2a3a 100644
--- a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.html
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.html
@@ -168,7 +168,11 @@
-
+
{{relatedServiceSpecification.name}}
+
+ {{ relatedServiceSpecification['@type'] === 'CustomerFacingServiceSpecification' ? 'CFSS' : (relatedServiceSpecification['@type'] === 'ResourceFacingServiceSpecification' ? 'RFSS' : 'null') }}
+
+
@@ -200,6 +204,120 @@
+
+
+
+
+
+
+
+ Apply Filter...
+
+
+
+
+
+
+
+
+
+ {{tag}}
+
+
+
+
+
+
+ | Name
+ |
+
+ {{element.name}} |
+
+
+
+ Value
+ Type |
+
+ {{element.valueType}} |
+
+
+
+
+ Default Values |
+
+
+ {{val.value.value}}
+ ({{val.value.alias}})
+
+
+
+
+ {{val.unitOfMeasure}}
+
+
+ |
+
+
+
+
+ Configurable |
+
+ {{element.configurable}} |
+
+
+
+ Actions |
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.ts b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.ts
index b94127a7f94fbf827add89dc8735b82dff2cff40..77cb87e58eb1486be2a7c331c39baf05580c18ff 100644
--- a/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.ts
+++ b/src/app/p_product/admin/productCatalogManagement/edit-product-specs/edit-product-specs.component.ts
@@ -6,11 +6,14 @@ import { ActivatedRoute, ActivationEnd, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable, Subscription } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
-import { ProductSpecification, ProductSpecificationCreate, ProductSpecificationUpdate, ServiceSpecificationRef } from 'src/app/openApis/productCatalogManagement/models';
+import { ProductSpecification, ProductSpecificationCreate, ProductSpecificationUpdate, ServiceSpecificationRef, ProductSpecificationCharacteristicRes } from 'src/app/openApis/productCatalogManagement/models';
import { ProductSpecificationService } from 'src/app/openApis/productCatalogManagement/services';
import { fadeIn } from 'src/app/shared/animations/animations';
import { AppService } from 'src/app/shared/services/app.service';
import { AssignServiceSpecificationComponent } from './assign-service-specification/assign-service-specification.component';
+import { MatTableDataSource } from '@angular/material/table';
+import { EditProductSpecCharacteristicsComponent } from './edit-product-spec-characteristics/edit-product-spec-characteristics.component';
+import { DeleteProductSpecCharacteristicsComponent } from './delete-product-spec-characteristics/delete-product-spec-characteristics.component';
@Component({
selector: 'app-edit-product-specs',
@@ -20,11 +23,18 @@ import { AssignServiceSpecificationComponent } from './assign-service-specificat
})
export class EditProductSpecsComponent implements OnInit {
+ // to change
+ displayedColumnsCharacteristics = ['name', 'type', 'defaultValues', 'configurable', 'actions'];
+ specCharacteristicsTags: string[] = ["All"]
+ tagFiltervalue:string = "All"
+ dataSource = new MatTableDataSource();
+
+
constructor(
private activatedRoute: ActivatedRoute,
private specService: ProductSpecificationService,
private dialog: MatDialog,
- private toastrService: ToastrService,
+ private toast: ToastrService,
private router: Router,
public appService: AppService
) { }
@@ -35,7 +45,7 @@ export class EditProductSpecsComponent implements OnInit {
finishedLoading: boolean = false
newSpecification = false
- listItems = ["Main Properties", "Service Specification Relationships", "Related Parties"]
+ listItems = ["Main Properties", "Service Specification Relationships", "Related Parties", "Product Specification Characteristics"]
activeListItem = "Main Properties"
editForm = new UntypedFormGroup({
@@ -100,21 +110,29 @@ export class EditProductSpecsComponent implements OnInit {
retrieveProductSpec() {
this.specService.retrieveProductSpecification({id: this.specID}).subscribe(
data => this.spec = data,
- error => {console.error(error), this.toastrService.error("An error occurred while loading Product Specification")},
+ error => this.toast.error("An error occurred while loading Product Specification"),
() => {
if (this.spec) {
this.finishedLoading = true
-
//populate General Panel Info
if (!this.spec.validFor) this.spec.validFor = { endDateTime: new Date(new Date().setFullYear(new Date().getFullYear()+20)).toISOString(), startDateTime: new Date().toISOString() }
this.editForm.patchValue(this.spec)
this.editForm.markAsPristine()
-
+
//populate Service Specification Relationship Panel Info
this.filteredServiceSpecificationRel$ = this.serviceSpecificationRelationshipFilterCtrl.valueChanges.pipe(
startWith(null),
map( (value:null | string) => value ? this._filterOnRelatedSpecs(value) : this.spec.serviceSpecification.slice() )
)
+
+ //populate Specification Characteristic Panel Info
+ this.dataSource.data = this.spec.productSpecCharacteristic;
+ // this.dataSource.paginator = this.paginator;
+
+ this.specCharacteristicsTags = ["All"]
+ this.tagFiltervalue = "All"
+ this.specCharacteristicsTags = this.retrieveSpecCharaceristicsTags(this.dataSource.data)
+
}
else {
@@ -124,6 +142,16 @@ export class EditProductSpecsComponent implements OnInit {
)
}
+ retrieveSpecCharaceristicsTags(dataSource: ProductSpecificationCharacteristicRes[]) {
+ let tagsArray = this.specCharacteristicsTags
+ dataSource.forEach(char => {
+ char.productSpecCharRelationship.filter( e => e.relationshipType === "tag").forEach(rel => {
+ if (!tagsArray.includes(rel.name)) tagsArray.push(rel.name)
+ })
+ });
+ return tagsArray
+ }
+
private _filterOnRelatedSpecs(filterValue: string) {
filterValue = filterValue.trim();
filterValue = filterValue.toLowerCase();
@@ -142,7 +170,7 @@ export class EditProductSpecsComponent implements OnInit {
dialogRef.afterClosed().subscribe (
result => {
if (result) {
- this.toastrService.success("Service Specification Relationship list was successfully updated")
+ this.toast.success("Product Specification Relationship list was successfully updated")
this.retrieveProductSpec()
}
}
@@ -164,10 +192,10 @@ export class EditProductSpecsComponent implements OnInit {
if (this.newSpecification) {
this.specService.createProductSpecification(updateObj).subscribe(
data => { updatedSpec = data },
- error => console.error(error),
+ error => this.toast.error("An error occurred while creating the Product Specification"),
() => {
this.newSpecification = false
- this.toastrService.success("Product Specification was successfully created")
+ this.toast.success("Product Specification was successfully created")
this.refreshProductSpecification(updatedSpec)
}
)
@@ -175,9 +203,9 @@ export class EditProductSpecsComponent implements OnInit {
else {
this.specService.patchProductSpecification({ id: this.specID, productSpecification: updateObj }).subscribe(
data => { updatedSpec = data },
- error => console.error(error),
+ error => this.toast.error("An error occurred while updating the Product Specification"),
() => {
- this.toastrService.success("Product Specification was successfully updated")
+ this.toast.success("Product Specification was successfully updated")
this.refreshProductSpecification(updatedSpec)
}
)
@@ -191,6 +219,97 @@ export class EditProductSpecsComponent implements OnInit {
this.retrieveProductSpec()
}
+
+ cloneProductSpecCharacteristic(characteristic: ProductSpecificationCharacteristicRes) {
+ const cloneCharacteristic: ProductSpecificationCharacteristicRes = {
+ name: `Copy of ${characteristic.name}`,
+ description: characteristic.description,
+ configurable: characteristic.configurable,
+ extensible: characteristic.extensible,
+ minCardinality: characteristic.minCardinality,
+ maxCardinality: characteristic.maxCardinality,
+ productSpecCharRelationship: characteristic.productSpecCharRelationship,
+ productSpecCharacteristicValue: characteristic.productSpecCharacteristicValue,
+ validFor: characteristic.validFor,
+ valueType: characteristic.valueType
+ }
+
+ this.spec.productSpecCharacteristic.push(cloneCharacteristic);
+
+ const updateCharacteristicObj: ProductSpecificationUpdate = {
+ productSpecCharacteristic: this.spec.productSpecCharacteristic
+ }
+
+ this.specService.patchProductSpecification({id: this.spec.id, productSpecification: updateCharacteristicObj}).subscribe(
+ data => {},
+ error => this.toast.error("An error occurred while cloning the Product Specification Characteristic"),
+ () => {
+ this.toast.success("Product Specification Characteristics list was successfully updated");
+ this.retrieveProductSpec();
+ }
+ )
+ }
+
+ applySpecCharFilter(filterValue: string) {
+ filterValue = filterValue.trim();
+ filterValue = filterValue.toLowerCase();
+ this.dataSource.filter = filterValue;
+ }
+
+ openCharacteristicDeleteDialog(characteristic: ProductSpecificationCharacteristicRes) {
+ const specToBeDeletedIndex = this.spec.productSpecCharacteristic.findIndex(char => char.uuid === characteristic.uuid)
+
+ const newSpecCharacteristicArray: ProductSpecificationCharacteristicRes[] = this.spec.productSpecCharacteristic.slice()
+
+ newSpecCharacteristicArray.splice(specToBeDeletedIndex, 1)
+
+ const dialogRef = this.dialog.open(DeleteProductSpecCharacteristicsComponent, {
+ data: {
+ productSpec: this.spec,
+ productSpecCharacteristicArray: newSpecCharacteristicArray,
+ specToBeDeleted: this.spec.productSpecCharacteristic[specToBeDeletedIndex]
+ }
+ })
+
+ dialogRef.afterClosed().subscribe (
+ result => {
+ if (result){
+ this.toast.success("Product Specification Characteristics list was successfully updated");
+ this.retrieveProductSpec()
+ }
+ }
+ )
+ }
+
+ openCharacteristicDesignDialog(characteristic: ProductSpecificationCharacteristicRes) {
+ const dialogRef = this.dialog.open(EditProductSpecCharacteristicsComponent, {
+ data: {
+ productSpec: this.spec,
+ specToBeUpdated: characteristic
+ },
+ disableClose: true
+ })
+
+ dialogRef.afterClosed().subscribe (
+ result => {
+ if (result) {
+ this.toast.success("Product Specification Characteristics list was successfully updated");
+ this.retrieveProductSpec();
+ }
+ }
+ )
+ }
+
+ filterCharacteristicsByTag(tagName) {
+ this.tagFiltervalue = tagName
+ if (tagName === "All") {
+ this.dataSource.data = this.spec.productSpecCharacteristic.filter(specCharacteristic => specCharacteristic.valueType);
+ } else {
+ this.dataSource.data = this.spec.productSpecCharacteristic.filter(specCharacteristic => specCharacteristic.valueType)
+ .filter(specChar => specChar.productSpecCharRelationship.some( rel => rel.name === tagName ));
+ }
+ }
+
ngOnDestroy() {
this.subscriptions.unsubscribe()
}
diff --git a/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.html b/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.html
index e5645e7ff0ad9aa5893d52374cadfb03ae629c0f..ccb2bacd4d8ab7783148d3af4c96390a8ce311ae 100644
--- a/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.html
+++ b/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.html
@@ -39,14 +39,14 @@
-
+
diff --git a/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.ts b/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.ts
index 2d5fff5821ed29c4c9edc03a5705a982bd932da7..c4e2e85cdaf0ff224638f1883b80def43319e64b 100644
--- a/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.ts
+++ b/src/app/p_product/marketplace/preview-market-place-item/preview-market-place-item.component.ts
@@ -4,6 +4,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ProductOffering, ProductSpecification } from 'src/app/openApis/productCatalogManagement/models';
import { ProductSpecificationService } from 'src/app/openApis/productCatalogManagement/services';
import { fadeIn } from 'src/app/shared/animations/animations';
+import { AuthService } from 'src/app/shared/services/auth.service';
import { ThemingService } from 'src/app/theming/theming.service';
@Component({
@@ -20,7 +21,8 @@ export class PreviewMarketPlaceItemComponent implements OnInit {
},
private dialogRef: MatDialogRef,
private specService: ProductSpecificationService,
- private themingService: ThemingService
+ private themingService: ThemingService,
+ public authService: AuthService
) { }
productOffering: ProductOffering
@@ -54,6 +56,8 @@ export class PreviewMarketPlaceItemComponent implements OnInit {
}
+ placeInOrderList() {}
+
closeDialog() {
this.dialogRef.close()
}
diff --git a/src/app/shared/navbar/navbar.component.html b/src/app/shared/navbar/navbar.component.html
index b607aac83e5ade4e1bed7403bb3cefbcd6fc40e7..61d2099e9c28400d4d20a0fece63654667e240ef 100644
--- a/src/app/shared/navbar/navbar.component.html
+++ b/src/app/shared/navbar/navbar.component.html
@@ -269,6 +269,14 @@
+
+
{{authService.portalUserJWT.preferred_username}}