Skip to content

Commit c3755d2

Browse files
committed
[NAE-2416] AbstractFileDefaultFieldComponent does not push upload event notification to TaskEventService
- Integrated `FrontActionService` in `AbstractFileDefaultFieldComponent` to handle front-end actions based on outcomes. - Enhanced file upload logic with better error handling and front action triggers, such as displaying success feedback via snackbar actions. - Updated tests for `AbstractFileListDefaultFieldComponent` to include `FrontActionService`. - Registered a new `snackBar` front action in the `FrontActionModule`. - Refactored the file upload methods to utilize modern RXJS subscription patterns with error handling improvements. - Improved readability and maintainability of file download and preview logic by addressing structuring and formatting issues. - Removed unused imports and simplified test components. These changes aim to improve the user experience by handling outcome-based front actions and offering better feedback on file operations.
1 parent 8c5cf10 commit c3755d2

7 files changed

Lines changed: 128 additions & 106 deletions

File tree

projects/netgrif-components-core/src/lib/actions/front-action.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NgModule } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import {FrontActionRegistryService} from "../registry/front-action-registry.service";
4-
import {redirectAction} from "./model/router-action-definitions";
4+
import {redirectAction, snackBarAction} from "./model/router-action-definitions";
55
import {reloadTaskAction, validateTaskAction} from "./model/task-action-definitions";
66

77
@NgModule({
@@ -16,5 +16,6 @@ export class FrontActionModule {
1616
frontActionsRegistry.register('redirect', redirectAction);
1717
frontActionsRegistry.register('validate', validateTaskAction);
1818
frontActionsRegistry.register('reloadTask', reloadTaskAction);
19+
frontActionsRegistry.register('snackBar', snackBarAction);
1920
}
2021
}

projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ import {MockUserResourceService} from "../../../utility/tests/mocks/mock-user-re
1616
import {ConfigurationService} from "../../../configuration/configuration.service";
1717
import {TestConfigurationService} from "../../../utility/tests/test-config";
1818
import {Component, CUSTOM_ELEMENTS_SCHEMA, Inject, Optional} from "@angular/core";
19-
import {BrowserDynamicTestingModule} from "@angular/platform-browser-dynamic/testing";
20-
import {ErrorSnackBarComponent} from "../../../snack-bar/components/error-snack-bar/error-snack-bar.component";
21-
import {SuccessSnackBarComponent} from "../../../snack-bar/components/success-snack-bar/success-snack-bar.component";
2219
import {TaskResourceService} from "../../../resources/engine-endpoint/task-resource.service";
2320
import {LoggerService} from "../../../logger/services/logger.service";
2421
import {SnackBarService} from "../../../snack-bar/services/snack-bar.service";
@@ -29,6 +26,7 @@ import {AbstractFileDefaultFieldComponent} from "./abstract-file-default-field.c
2926
import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../../models/data-field-portal-data-injection-token";
3027
import {FormControl} from "@angular/forms";
3128
import {WrappedBoolean} from "../../data-field-template/models/wrapped-boolean";
29+
import {FrontActionService} from "../../../actions/services/front-action.service";
3230

3331
describe('AbstractFileDefaultFieldComponent', () => {
3432
let component: TestFileComponent;
@@ -48,6 +46,7 @@ describe('AbstractFileDefaultFieldComponent', () => {
4846
providers: [
4947
SideMenuService,
5048
EventService,
49+
FrontActionService,
5150
{provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService},
5251
{provide: AuthenticationService, useClass: MockAuthenticationService},
5352
{provide: UserResourceService, useClass: MockUserResourceService},
@@ -97,8 +96,9 @@ class TestFileComponent extends AbstractFileDefaultFieldComponent {
9796
translate: TranslateService,
9897
sanitizer: DomSanitizer,
9998
eventService: EventService,
99+
frontActionService: FrontActionService,
100100
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<FileField>) {
101-
super(taskResourceService, log, snackbar, translate, eventService, sanitizer, dataFieldPortalData);
101+
super(taskResourceService, log, snackbar, translate, eventService, sanitizer, frontActionService, dataFieldPortalData);
102102
}
103103
}
104104

projects/netgrif-components-core/src/lib/data-fields/file-field/file-default-field/abstract-file-default-field.component.ts

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../../models/data-fie
2626
import {FILE_FIELD_HEIGHT, FILE_FIELD_PADDING, PREVIEW, PREVIEW_BUTTON} from '../models/file-field-constants';
2727
import {FileFieldRequest} from "../../../resources/interface/file-field-request-body";
2828
import {AbstractFileFieldDefaultComponent} from '../../models/abstract-file-field-default-component';
29+
import {FrontAction} from "../../models/changed-fields";
30+
import {FrontActionService} from "../../../actions/services/front-action.service";
2931

3032
export interface FileState {
3133
progress: number;
@@ -114,6 +116,7 @@ export abstract class AbstractFileDefaultFieldComponent extends AbstractFileFiel
114116
protected _translate: TranslateService,
115117
protected _eventService: EventService,
116118
protected _sanitizer: DomSanitizer,
119+
protected _frontActionService: FrontActionService,
117120
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<FileField>) {
118121
super(_log, _snackbar, _translate, dataFieldPortalData);
119122
this.state = this.defaultState;
@@ -217,60 +220,68 @@ export abstract class AbstractFileDefaultFieldComponent extends AbstractFileFiel
217220
fileFormData.append('file', fileToUpload);
218221
fileFormData.append('data', new Blob([JSON.stringify(this.createRequestBody())], {type: 'application/json'}));
219222
this._taskResourceService.uploadFile(this.taskId, fileFormData, false)
220-
.subscribe((response: EventOutcomeMessageResource) => {
221-
if ((response as ProviderProgress).type && (response as ProviderProgress).type === ProgressType.UPLOAD) {
222-
this.state.progress = (response as ProviderProgress).progress;
223-
} else {
224-
this.state.completed = true;
225-
this.state.uploading = false;
226-
this.state.progress = 0;
223+
.subscribe({
224+
next: (response: EventOutcomeMessageResource) => {
225+
if ((response as ProviderProgress).type && (response as ProviderProgress).type === ProgressType.UPLOAD) {
226+
this.state.progress = (response as ProviderProgress).progress;
227+
} else {
228+
this.state.completed = true;
229+
this.state.uploading = false;
230+
this.state.progress = 0;
227231

228-
if (response.error) {
229-
this.state.error = true;
230-
this._log.error(
231-
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, response.error
232-
);
233232
if (response.error) {
234-
this._snackbar.openErrorSnackBar(this._translate.instant(response.error));
233+
this.state.error = true;
234+
this._log.error(
235+
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, response.error
236+
);
237+
if (response.error) {
238+
this._snackbar.openErrorSnackBar(this._translate.instant(response.error));
239+
} else {
240+
this._snackbar.openErrorSnackBar(this._translate.instant('dataField.snackBar.fileUploadFailed'));
241+
}
235242
} else {
236-
this._snackbar.openErrorSnackBar(this._translate.instant('dataField.snackBar.fileUploadFailed'));
243+
const changedFieldsMap: ChangedFieldsMap = this._eventService.parseChangedFieldsFromOutcomeTree(response.outcome);
244+
this.dataField.emitChangedFields(changedFieldsMap);
245+
this._log.debug(
246+
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0).name} was successfully uploaded`
247+
);
248+
this.state.error = false;
249+
this.dataField.downloaded = false;
250+
this.dataField.value.name = fileToUpload.name;
251+
if (this.isFilePreview) {
252+
this.initializePreviewIfDisplayable();
253+
}
254+
this.fullSource.next(undefined);
255+
this.fileForDownload = undefined;
256+
this.formControlRef.setValue(this.dataField.value.name);
257+
this._snackbar.openSuccessSnackBar(!!response.outcome.message ? response.outcome.message : this._translate.instant('tasks.snackbar.dataSaved'));
258+
const frontActions: Array<FrontAction> = this._eventService.parseFrontActionsFromOutcomeTree(response.outcome);
259+
if (frontActions?.length > 0) {
260+
this._frontActionService.runAll(frontActions);
261+
}
237262
}
263+
this.dataField.touch = true;
264+
this.dataField.update();
265+
this.fileUploadEl.nativeElement.value = '';
266+
}
267+
},
268+
error: (error) => {
269+
this.state.completed = true;
270+
this.state.error = true;
271+
this.state.uploading = false;
272+
this.state.progress = 0;
273+
this._log.error(
274+
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, error
275+
);
276+
if (error?.error?.message) {
277+
this._snackbar.openErrorSnackBar(this._translate.instant(error.error.message));
238278
} else {
239-
const changedFieldsMap: ChangedFieldsMap = this._eventService.parseChangedFieldsFromOutcomeTree(response.outcome);
240-
this.dataField.emitChangedFields(changedFieldsMap);
241-
this._log.debug(
242-
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0).name} was successfully uploaded`
243-
);
244-
this.state.error = false;
245-
this.dataField.downloaded = false;
246-
this.dataField.value.name = fileToUpload.name;
247-
if (this.isFilePreview) {
248-
this.initializePreviewIfDisplayable();
249-
}
250-
this.fullSource.next(undefined);
251-
this.fileForDownload = undefined;
252-
this.formControlRef.setValue(this.dataField.value.name);
279+
this._snackbar.openErrorSnackBar(this._translate.instant('dataField.snackBar.fileUploadFailed'));
253280
}
254281
this.dataField.touch = true;
255282
this.dataField.update();
256283
this.fileUploadEl.nativeElement.value = '';
257284
}
258-
}, error => {
259-
this.state.completed = true;
260-
this.state.error = true;
261-
this.state.uploading = false;
262-
this.state.progress = 0;
263-
this._log.error(
264-
`File [${this.dataField.stringId}] ${this.fileUploadEl.nativeElement.files.item(0)} uploading has failed!`, error
265-
);
266-
if (error?.error?.message) {
267-
this._snackbar.openErrorSnackBar(this._translate.instant(error.error.message));
268-
} else {
269-
this._snackbar.openErrorSnackBar(this._translate.instant('dataField.snackBar.fileUploadFailed'));
270-
}
271-
this.dataField.touch = true;
272-
this.dataField.update();
273-
this.fileUploadEl.nativeElement.value = '';
274285
});
275286
}
276287

@@ -410,7 +421,8 @@ export abstract class AbstractFileDefaultFieldComponent extends AbstractFileFiel
410421
this.state.downloading = true;
411422
let params = new HttpParams()
412423
params = params.set("fieldId", this.dataField.stringId);
413-
this._taskResourceService.downloadFilePreview(this.resolveParentTaskId(), params).subscribe(response => { if (response instanceof Blob) {
424+
this._taskResourceService.downloadFilePreview(this.resolveParentTaskId(), params).subscribe(response => {
425+
if (response instanceof Blob) {
414426
this._log.debug(`Preview of file [${this.dataField.stringId}] ${this.dataField.value.name} was successfully downloaded`);
415427
this.fileForPreview = new Blob([response], {type: 'application/octet-stream'});
416428
this.previewSource = this._sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(this.fileForPreview));
@@ -445,7 +457,8 @@ export abstract class AbstractFileDefaultFieldComponent extends AbstractFileFiel
445457
}
446458
let params = new HttpParams();
447459
params = params.set("fieldId", this.dataField.stringId);
448-
this._taskResourceService.downloadFile(this.resolveParentTaskId(), params).subscribe(response => { if (!(response as ProviderProgress).type || (response as ProviderProgress).type !== ProgressType.DOWNLOAD) {
460+
this._taskResourceService.downloadFile(this.resolveParentTaskId(), params).subscribe(response => {
461+
if (!(response as ProviderProgress).type || (response as ProviderProgress).type !== ProgressType.DOWNLOAD) {
449462
this._log.debug(`File [${this.dataField.stringId}] ${this.dataField.value.name} was successfully downloaded`);
450463
this.initDownloadFile(response);
451464
}

projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ import {MockUserResourceService} from "../../../utility/tests/mocks/mock-user-re
1616
import {ConfigurationService} from "../../../configuration/configuration.service";
1717
import {TestConfigurationService} from "../../../utility/tests/test-config";
1818
import {Component, CUSTOM_ELEMENTS_SCHEMA, Inject, Optional} from "@angular/core";
19-
import {BrowserDynamicTestingModule} from "@angular/platform-browser-dynamic/testing";
20-
import {ErrorSnackBarComponent} from "../../../snack-bar/components/error-snack-bar/error-snack-bar.component";
21-
import {SuccessSnackBarComponent} from "../../../snack-bar/components/success-snack-bar/success-snack-bar.component";
2219
import {TaskResourceService} from "../../../resources/engine-endpoint/task-resource.service";
2320
import {LoggerService} from "../../../logger/services/logger.service";
2421
import {SnackBarService} from "../../../snack-bar/services/snack-bar.service";
@@ -28,6 +25,7 @@ import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../../models/data-fie
2825
import {AbstractFileListDefaultFieldComponent} from "./abstract-file-list-default-field.component";
2926
import {FormControl} from "@angular/forms";
3027
import {WrappedBoolean} from "../../data-field-template/models/wrapped-boolean";
28+
import {FrontActionService} from "../../../actions/services/front-action.service";
3129

3230
describe('AbstractFileListDefaultFieldComponent', () => {
3331
let component: TestFileListComponent;
@@ -46,6 +44,7 @@ describe('AbstractFileListDefaultFieldComponent', () => {
4644
providers: [
4745
SideMenuService,
4846
EventService,
47+
FrontActionService,
4948
{provide: AuthenticationMethodService, useClass: MockAuthenticationMethodService},
5049
{provide: AuthenticationService, useClass: MockAuthenticationService},
5150
{provide: UserResourceService, useClass: MockUserResourceService},
@@ -94,8 +93,9 @@ class TestFileListComponent extends AbstractFileListDefaultFieldComponent {
9493
snackbar: SnackBarService,
9594
translate: TranslateService,
9695
eventService: EventService,
96+
frontActionService: FrontActionService,
9797
@Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData<FileListField>) {
98-
super(taskResourceService, log, snackbar, translate, eventService, dataFieldPortalData);
98+
super(taskResourceService, log, snackbar, translate, eventService, frontActionService, dataFieldPortalData);
9999
}
100100
}
101101

0 commit comments

Comments
 (0)