From: Devika Babrekar Date: Wed, 8 Jul 2026 13:10:12 +0000 (+0530) Subject: mgr/dashboard: fixing manual deployment option for osd creation flow X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F69302%2Fhead;p=ceph.git mgr/dashboard: fixing manual deployment option for osd creation flow Signed-off-by: Devika Babrekar --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts index c52784b5155..eafd0baaf3d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts @@ -106,13 +106,13 @@ export class InventoryDevicesComponent implements OnInit, OnDestroy { ]; const columns = [ { - name: $localize`Hostname`, - prop: 'hostname', + name: $localize`Device path`, + prop: 'path', flexGrow: 1 }, { - name: $localize`Device path`, - prop: 'path', + name: $localize`Hostname`, + prop: 'hostname', flexGrow: 1 }, { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.html index 5e0b0c90cc2..08ccc032996 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.html @@ -15,53 +15,77 @@
- @if (devices.length === 0 && inlineSelection) { - @if (!canSelect) { + @if (inlineSelection) { + @if (showAddPrimaryFirstAlert) { {{ tooltips.addPrimaryFirst }} } - @if (canSelect) { - @if (availDevices.length === 0) { + @if (showNoAvailDevicesAlert) { No available devices } - @if (availDevices.length > 0 && !canInlineSubmit) { + @if (showFilterSection) { + @if (!canInlineSubmit) { - At least one of these filters must be applied in order to proceed: + Select at least one of these filters to fetch the data: @for (filter of requiredFilters; track filter) { {{ filter }} } } - +
+ @for (field of filterFields; track field.prop) { +
+ + + @for (option of field.options; track option.raw) { + + } + +
+ } +
+
+ @if (showSelectionSummary) { +
+ @for (filter of displayedFilters; track filter.prop) { + {{ filter.name }}: {{ filter.value.formatted }} + } + + + Clear + +
+ Raw capacity: {{ inlineCapacity | dimlessBinary }}. +
+
+ } + + [filterColumns]="[]"> - @if (canInlineSubmit) { -
- Number of devices: {{ inlineFilteredDevices.length }}. Raw capacity: - {{ inlineCapacity | dimlessBinary }}. -
- } - } } @else { @if (devices.length === 0) { @@ -94,11 +118,6 @@ [filterColumns]="[]"> - @if (type === 'data') { -
- Raw capacity: {{ capacity | dimlessBinary }} -
- } } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.scss index bf3223c234d..742a942df5a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.scss @@ -15,3 +15,25 @@ width: 100%; } } + +.device-filter-form { + margin-bottom: 1rem; +} + +.filter-dropdown-row { + display: flex; + flex-wrap: nowrap; + gap: 0.75rem; + width: 100%; +} + +.filter-dropdown { + flex: 1 1 0; + min-width: 0; +} + +.selection-summary { + .selection-capacity { + text-align: center; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.spec.ts index 8e6b1e9865c..d66f7ad93e6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.spec.ts @@ -16,11 +16,6 @@ describe('OsdDevicesSelectionGroupsComponent', () => { let fixtureHelper: FixtureHelper; const devices: InventoryDevice[] = [Mocks.getInventoryDevice('node0', '1')]; - const buttonSelector = '.cd-col-form-input button'; - const getButton = () => { - const debugElement = fixtureHelper.getElementByCss(buttonSelector); - return debugElement.nativeElement; - }; const clearTextSelector = '.tc_clearSelections'; const getClearText = () => { const debugElement = fixtureHelper.getElementByCss(clearTextSelector); @@ -42,7 +37,9 @@ describe('OsdDevicesSelectionGroupsComponent', () => { fixture = TestBed.createComponent(OsdDevicesSelectionGroupsComponent); fixtureHelper = new FixtureHelper(fixture); component = fixture.componentInstance; + component.type = 'data'; component.canSelect = true; + component.inlineSelection = true; }); describe('without available devices', () => { @@ -55,14 +52,11 @@ describe('OsdDevicesSelectionGroupsComponent', () => { expect(component).toBeTruthy(); }); - it('should display Add button in disabled state', () => { - const button = getButton(); - expect(button).toBeTruthy(); - expect(button.disabled).toBe(true); - expect(button.textContent).toBe('Add'); + it('should not display filter form', () => { + fixtureHelper.expectElementVisible('.device-filter-form', false); }); - it('should not display devices table', () => { + it('should display devices table', () => { fixtureHelper.expectElementVisible('cd-inventory-devices', false); }); }); @@ -77,23 +71,29 @@ describe('OsdDevicesSelectionGroupsComponent', () => { expect(component).toBeTruthy(); }); - it('should display Add button in enabled state', () => { - const button = getButton(); - expect(button).toBeTruthy(); - expect(button.disabled).toBe(false); - expect(button.textContent).toBe('Add'); + it('should display filter form', () => { + fixtureHelper.expectElementVisible('.device-filter-form', true); }); - it('should not display devices table', () => { - fixtureHelper.expectElementVisible('cd-inventory-devices', false); + it('should display empty devices table', () => { + fixtureHelper.expectElementVisible('cd-inventory-devices', true); + expect(component.tableDevices).toEqual([]); }); }); describe('with devices selected', () => { beforeEach(() => { component.isOsdPage = true; - component.availDevices = []; + component.availDevices = devices; component.devices = devices; + component.appliedFilters = [ + { + name: 'Type', + prop: 'human_readable_type', + value: { raw: 'nvme/ssd', formatted: 'nvme/ssd' } + } + ]; + component.selectedFilters = { human_readable_type: 'nvme/ssd' }; component.ngOnInit(); fixture.detectChanges(); }); @@ -111,12 +111,87 @@ describe('OsdDevicesSelectionGroupsComponent', () => { it('should clear devices by clicking Clear link', () => { spyOn(component.cleared, 'emit'); fixtureHelper.clickElement(clearTextSelector); - fixtureHelper.expectElementVisible('cd-inventory-devices', false); + fixtureHelper.expectElementVisible('cd-inventory-devices', true); const event: Record = { - type: undefined, + type: 'data', clearedDevices: devices }; expect(component.cleared.emit).toHaveBeenCalledWith(event); + expect(component.devices).toEqual([]); + }); + }); + + describe('wal inline selection', () => { + beforeEach(() => { + component.type = 'wal'; + component.name = 'WAL'; + component.availDevices = devices; + component.canSelect = true; + component.inlineSelection = true; + fixture.detectChanges(); + }); + + it('should display filter form instead of Add button', () => { + fixtureHelper.expectElementVisible('.device-filter-form', true); + fixtureHelper.expectElementVisible('cd-inventory-devices', true); + fixtureHelper.expectElementVisible('.cd-col-form-input button.btn-light', false); + }); + + it('should display empty devices table before filters are applied', () => { + expect(component.tableDevices).toEqual([]); + }); + + it('should auto-apply filtered devices when a required filter is selected', () => { + spyOn(component.selected, 'emit'); + component.onFilterFieldChange('human_readable_type', 'nvme/ssd'); + fixture.detectChanges(); + + expect(component.tableDevices.length).toBe(1); + expect(component.devices.length).toBe(1); + expect(component.selected.emit).toHaveBeenCalled(); + }); + }); + + describe('wal inline selection without primary devices', () => { + beforeEach(() => { + component.type = 'wal'; + component.name = 'WAL'; + component.availDevices = devices; + component.canSelect = false; + component.inlineSelection = true; + fixture.detectChanges(); + }); + + it('should display add primary first alert without filter form', () => { + fixtureHelper.expectElementVisible('.device-filter-form', false); + fixtureHelper.expectElementVisible('cd-inventory-devices', false); + }); + }); + + describe('db inline selection', () => { + beforeEach(() => { + component.type = 'db'; + component.name = 'DB'; + component.availDevices = devices; + component.canSelect = true; + component.inlineSelection = true; + fixture.detectChanges(); + }); + + it('should display filter form instead of Add button', () => { + fixtureHelper.expectElementVisible('.device-filter-form', true); + fixtureHelper.expectElementVisible('cd-inventory-devices', true); + fixtureHelper.expectElementVisible('.cd-col-form-input button.btn-light', false); + }); + + it('should auto-apply filtered devices when a required filter is selected', () => { + spyOn(component.selected, 'emit'); + component.onFilterFieldChange('sys_api.vendor', 'AAA'); + fixture.detectChanges(); + + expect(component.tableDevices.length).toBe(1); + expect(component.devices.length).toBe(1); + expect(component.selected.emit).toHaveBeenCalled(); }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.ts index 79d03d10b45..b9c454c2565 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-devices-selection-groups/osd-devices-selection-groups.component.ts @@ -1,4 +1,14 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + PipeTransform +} from '@angular/core'; import { Router } from '@angular/router'; import _ from 'lodash'; @@ -7,16 +17,30 @@ import { InventoryDevice } from '~/app/ceph/cluster/inventory/inventory-devices/ import { OsdService } from '~/app/shared/api/osd.service'; import { Icons } from '~/app/shared/enum/icons.enum'; import { CdTableColumnFiltersChange } from '~/app/shared/models/cd-table-column-filters-change'; +import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe'; import { ModalService } from '~/app/shared/services/modal.service'; import { OsdDevicesSelectionModalComponent } from '../osd-devices-selection-modal/osd-devices-selection-modal.component'; import { DevicesSelectionChangeEvent } from './devices-selection-change-event.interface'; import { DevicesSelectionClearEvent } from './devices-selection-clear-event.interface'; +interface DeviceFilterOption { + raw: string; + formatted: string; +} + +interface DeviceFilterField { + prop: string; + name: string; + pipe?: PipeTransform; + options: DeviceFilterOption[]; +} + @Component({ selector: 'cd-osd-devices-selection-groups', templateUrl: './osd-devices-selection-groups.component.html', styleUrls: ['./osd-devices-selection-groups.component.scss'], - standalone: false + standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush }) export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { // data, wal, db @@ -60,9 +84,12 @@ export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { $localize`Model`, $localize`Size` ]; - inlineFilteredDevices: InventoryDevice[] = []; + filterFields: DeviceFilterField[] = []; + selectedFilters: Record = {}; + filteredDevices: InventoryDevice[] = []; inlineCapacity = 0; canInlineSubmit = false; + hasAnyFilter = false; inlineFilterEvent?: CdTableColumnFiltersChange; tooltips = { noAvailDevices: $localize`No available devices`, @@ -70,12 +97,27 @@ export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { addByFilters: $localize`Add devices by using filters` }; + private filterFieldConfig: Array<{ + prop: string; + name: string; + pipe?: PipeTransform; + }>; + constructor( private modalService: ModalService, public osdService: OsdService, - private router: Router + private router: Router, + private dimlessBinary: DimlessBinaryPipe, + private cdr: ChangeDetectorRef ) { this.isOsdPage = this.router.url.includes('/osd'); + this.filterFieldConfig = [ + { prop: 'human_readable_type', name: $localize`Type` }, + { prop: 'hostname', name: $localize`Hostname` }, + { prop: 'sys_api.vendor', name: $localize`Vendor` }, + { prop: 'sys_api.model', name: $localize`Model` }, + { prop: 'sys_api.size', name: $localize`Size`, pipe: this.dimlessBinary } + ]; } ngOnInit() { @@ -87,12 +129,78 @@ export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { this.osdService?.osdDevices ? (this.expansionCanSelect = this.osdService?.osdDevices['disableSelect']) : (this.expansionCanSelect = false); + if (this.inlineSelection) { + this.restoreSelectedFiltersFromApplied(); + } } - this.updateAddButtonTooltip(); + if (this.inlineSelection) { + this.initDefaultFilterValues(); + this.updateFilterFields(); + this.updateFilteredDevices(); + } else { + this.updateAddButtonTooltip(); + } + this.cdr.markForCheck(); } ngOnChanges() { - this.updateAddButtonTooltip(); + if (this.inlineSelection) { + this.initDefaultFilterValues(); + this.updateFilterFields(); + this.updateFilteredDevices(); + } else { + this.updateAddButtonTooltip(); + } + this.cdr.markForCheck(); + } + + get showAddPrimaryFirstAlert(): boolean { + return this.type !== 'data' && !this.canSelect && this.devices.length === 0; + } + + get showFilterSection(): boolean { + if (this.type === 'data') { + return this.availDevices?.length > 0 || this.devices?.length > 0; + } + + if (this.devices?.length > 0) { + return true; + } + + return this.canSelect && this.availDevices?.length > 0; + } + + get showNoAvailDevicesAlert(): boolean { + if (this.type !== 'data' && !this.canSelect) { + return false; + } + + return !this.showFilterSection; + } + + get tableDevices(): InventoryDevice[] { + return this.canInlineSubmit ? this.filteredDevices : []; + } + + get displayedFilters(): CdTableColumnFiltersChange['filters'] { + return this.inlineFilterEvent?.filters ?? this.appliedFilters ?? []; + } + + get showSelectionSummary(): boolean { + return ( + (this.canInlineSubmit && this.tableDevices.length > 0) || + (this.devices.length > 0 && this.appliedFilters.length > 0) + ); + } + + onFilterFieldChange(prop: string, value: string) { + const normalizedValue = value === '' ? undefined : value; + const hadDevices = this.devices.length > 0; + const clearedDevices = [...this.devices]; + this.selectedFilters[prop] = normalizedValue; + this.updateFilteredDevices(); + this.syncSelectionState(hadDevices, clearedDevices); + this.cdr.markForCheck(); } showSelectionModal() { @@ -112,33 +220,143 @@ export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { }); } - onInlineFilterChange(event: CdTableColumnFiltersChange) { + clearDevices() { + if (!this.isOsdPage) { + this.expansionCanSelect = false; + this.osdService.osdDevices['disableSelect'] = false; + this.osdService.osdDevices = []; + } + const event = { + type: this.type, + clearedDevices: [...this.devices] + }; + this.devices = []; + this.appliedFilters = []; + this.capacity = 0; + this.selectedFilters = {}; this.inlineCapacity = 0; this.canInlineSubmit = false; this.inlineFilterEvent = undefined; + if (this.inlineSelection) { + this.initDefaultFilterValues(); + this.updateFilterFields(); + this.updateFilteredDevices(); + } + this.cleared.emit(event); + this.cdr.markForCheck(); + } + + private get devicesForFiltering(): InventoryDevice[] { + return _.uniqBy([...(this.availDevices || []), ...(this.devices || [])], 'uid'); + } - if (_.isEmpty(event.filters)) { - this.inlineFilteredDevices = []; + private get canApplySelection(): boolean { + return this.type === 'data' || this.canSelect; + } + + private initDefaultFilterValues() { + if (this.hostname && !this.selectedFilters['hostname']) { + this.selectedFilters['hostname'] = this.hostname; + } + } + + private restoreSelectedFiltersFromApplied() { + if (_.isEmpty(this.appliedFilters)) { return; } + this.appliedFilters.forEach((filter) => { + this.selectedFilters[filter.prop as string] = filter.value?.raw; + }); + } + + private updateFilterFields() { + this.filterFields = this.filterFieldConfig + .filter((field) => this.filterColumns.includes(field.prop)) + .map((field) => ({ + ...field, + options: this.buildFilterOptions(field.prop, field.pipe) + })); + } - const filters = event.filters.filter((filter) => filter.prop !== 'hostname'); - this.canInlineSubmit = !_.isEmpty(filters); - this.inlineFilteredDevices = event.data; - this.inlineCapacity = _.sumBy(this.inlineFilteredDevices, 'sys_api.size'); - this.inlineFilterEvent = event; + private buildFilterOptions(prop: string, pipe?: PipeTransform): DeviceFilterOption[] { + const values = _.sortedUniq( + _.filter(_.map(this.devicesForFiltering, prop), (value) => { + return (_.isString(value) && value !== '') || _.isBoolean(value) || _.isFinite(value); + }).sort() + ); + + return values.map((value) => this.createFilterOption(value, pipe)); } - submitInlineSelection() { - if ( - !this.inlineFilterEvent || - !this.canInlineSubmit || - this.inlineFilteredDevices.length === 0 - ) { + private createFilterOption(value: unknown, pipe?: PipeTransform): DeviceFilterOption { + const raw = _.toString(value); + return { + raw, + formatted: pipe ? pipe.transform(value) : raw + }; + } + + private syncSelectionState(hadDevices: boolean, clearedDevices: InventoryDevice[]) { + if (this.canApplySelection && this.canInlineSubmit && this.filteredDevices.length > 0) { + this.applySelectionResult(this.inlineFilterEvent); return; } - this.applySelectionResult(this.inlineFilterEvent); + if (hadDevices) { + this.resetSelectionState(); + this.cleared.emit({ + type: this.type, + clearedDevices + }); + } + } + + private resetSelectionState() { + this.devices = []; + this.capacity = 0; + this.appliedFilters = []; + } + + private updateFilteredDevices() { + let data = [...this.devicesForFiltering]; + const appliedFilters: CdTableColumnFiltersChange['filters'] = []; + + this.filterFields.forEach((field) => { + const selectedValue = this.selectedFilters[field.prop]; + if (_.isUndefined(selectedValue)) { + return; + } + + const option = field.options.find((entry) => entry.raw === selectedValue); + if (!option) { + delete this.selectedFilters[field.prop]; + return; + } + + appliedFilters.push({ + name: field.name, + prop: field.prop, + value: option + }); + data = data.filter((row) => `${_.get(row, field.prop)}` === selectedValue); + }); + + this.hasAnyFilter = !_.isEmpty(appliedFilters); + const filtersWithoutHostname = appliedFilters.filter((filter) => filter.prop !== 'hostname'); + this.canInlineSubmit = !_.isEmpty(filtersWithoutHostname); + this.filteredDevices = this.hasAnyFilter ? data : []; + this.inlineCapacity = _.sumBy(this.filteredDevices, 'sys_api.size'); + + if (this.hasAnyFilter) { + const filteredUids = new Set(this.filteredDevices.map((device) => device.uid)); + this.inlineFilterEvent = { + filters: appliedFilters, + data: this.filteredDevices, + dataOut: this.devicesForFiltering.filter((device) => !filteredUids.has(device.uid)) + }; + } else { + this.inlineFilterEvent = undefined; + } } private applySelectionResult(result: CdTableColumnFiltersChange) { @@ -160,7 +378,6 @@ export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { this.addButtonTooltip = this.tooltips.noAvailDevices; } else { if (!this.canSelect) { - // No primary devices added yet. this.addButtonTooltip = this.tooltips.addPrimaryFirst; } else if (this.availDevices.length === 0) { this.addButtonTooltip = this.tooltips.noAvailDevices; @@ -169,22 +386,4 @@ export class OsdDevicesSelectionGroupsComponent implements OnInit, OnChanges { } } } - - clearDevices() { - if (!this.isOsdPage) { - this.expansionCanSelect = false; - this.osdService.osdDevices['disableSelect'] = false; - this.osdService.osdDevices = []; - } - const event = { - type: this.type, - clearedDevices: [...this.devices] - }; - this.devices = []; - this.inlineFilteredDevices = []; - this.inlineCapacity = 0; - this.canInlineSubmit = false; - this.inlineFilterEvent = undefined; - this.cleared.emit(event); - } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.html index 91dc4ca6ecc..700c8af631e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.html @@ -9,9 +9,9 @@ (closeRequested)="onCancel()" (stepChanged)="populateReviewData()" [isSubmitLoading]="isSubmitLoading" - [submitButtonLabel]="simpleDeployment ? createOsdsLabel : actionLabels.PREVIEW" + [submitButtonLabel]="createOsdsLabel"> type="wide"> - +
None selected

}
+ +
+

Device Groups

+
+ +
+

Service Type

+

{{driveGroup.spec.service_type}}

+
+ +
+

Service ID

+

{{driveGroup.spec.service_id}}

+
+ + + }
{ - this.navigateAfterCreate(); - }); - this.isSubmitLoading = false; - this.previewButtonPanel.submitButton.loading = false; + + // const user = this.authStorageService.getUsername(); + // this.driveGroup.setName(`dashboard-${user}-${_.now()}`); + // const modalRef = this.modalService.show(OsdCreationPreviewModalComponent, { + // driveGroups: [this.driveGroup.spec] + // }); + // modalRef.componentInstance.submitAction.subscribe(() => { + // this.navigateAfterCreate(); + // }); + // this.isSubmitLoading = false; + // this.previewButtonPanel.submitButton.loading = false; + + // let driveGroups = [this.driveGroup.spec]; + + const trackingId = _.join(_.map([this.driveGroup.spec], 'service_id'), ', '); + this.taskWrapper + .wrapTaskAroundCall({ + task: new FinishedTask('osd/' + URLVerbs.CREATE, { + tracking_id: trackingId + }), + call: this.osdService.create([this.driveGroup.spec], trackingId) + }) + .subscribe({ + error: () => { + this.isSubmitLoading = false; + }, + complete: () => { + this.isSubmitLoading = false; + this.navigateAfterCreate(); + this.previewButtonPanel.submitButton.loading = false; + } + }); } }