Skip to content

Commit 5a1839e

Browse files
Feature: resource files upload files with drag and drop to auto storage (#1009)
1 parent 9568723 commit 5a1839e

37 files changed

+912
-236
lines changed

test/app/components/base/form/complex-form/complex-form.spec.ts renamed to app/components/base/form/complex-form/complex-form.component.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ButtonComponent } from "app/components/base/buttons";
99
import {
1010
ComplexFormComponent, FormPageComponent, FormPickerComponent, FormSectionComponent,
1111
} from "app/components/base/form";
12+
import { FormFooterComponent } from "app/components/base/form/complex-form/footer";
1213
import { ServerErrorComponent } from "app/components/base/form/server-error";
1314
import { ServerError } from "app/models";
1415
import { AuthorizationHttpService } from "app/services";
@@ -98,6 +99,7 @@ describe("ComplexFormComponent", () => {
9899
FormPageComponent,
99100
FormSectionComponent,
100101
FormPickerComponent,
102+
FormFooterComponent,
101103
],
102104
providers: [
103105
{ provide: AuthorizationHttpService, useValue: null },
@@ -205,8 +207,8 @@ describe("ComplexFormComponent", () => {
205207
});
206208

207209
it("should toggle the error when clicking the warning button", () => {
208-
const toggleBtn = de.query(By.css(".toggle-error-btn > button"));
209-
expect(toggleBtn).not.toBeFalsy();
210+
const toggleBtn = de.query(By.css("bl-form-footer .toggle-error-btn > button"));
211+
expect(toggleBtn).not.toBeFalsy("Error toggle button should be defined");
210212

211213
// Toggle hidden
212214
click(toggleBtn);

app/components/base/form/complex-form/complex-form.component.ts

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import {
2-
AfterViewInit, ChangeDetectorRef, Component, ContentChildren, HostBinding, Input, QueryList, Type,
2+
AfterViewInit, ChangeDetectorRef, Component, ContentChildren, HostBinding, Input, OnChanges, QueryList, Type,
33
} from "@angular/core";
44
import { FormControl } from "@angular/forms";
55

6-
import { Dto, autobind } from "app/core";
6+
import { AsyncTask, Dto, autobind } from "app/core";
77
import { ServerError } from "app/models";
88
import { log } from "app/utils";
99
import { validJsonConfig } from "app/utils/validators";
10-
import { Observable } from "rxjs";
10+
import { Observable, Subscription } from "rxjs";
1111
import { FormBase } from "../form-base";
1212
import { FormPageComponent } from "../form-page";
13+
import { FormActionConfig } from "./footer";
14+
1315
import "./complex-form.scss";
1416

1517
export type FormSize = "small" | "medium" | "large";
@@ -34,7 +36,7 @@ export const defaultComplexFormConfig: ComplexFormConfig = {
3436
selector: "bl-complex-form",
3537
templateUrl: "complex-form.html",
3638
})
37-
export class ComplexFormComponent extends FormBase implements AfterViewInit {
39+
export class ComplexFormComponent extends FormBase implements AfterViewInit, OnChanges {
3840
/**
3941
* If the form should allow multi use. \
4042
* If true the form will have a "Save" AND a "Save and Close" button.
@@ -62,6 +64,7 @@ export class ComplexFormComponent extends FormBase implements AfterViewInit {
6264
* Needs to return an observable that will have a {ServerError} if failing.
6365
*/
6466
@Input() public submit: (dto?: Dto<any>) => Observable<any>;
67+
@Input() public asyncTasks: Observable<AsyncTask[]>;
6568

6669
@Input() @HostBinding("class") public size: FormSize = "large";
6770

@@ -73,8 +76,13 @@ export class ComplexFormComponent extends FormBase implements AfterViewInit {
7376
public currentPage: FormPageComponent;
7477
public showJsonEditor = false;
7578
public jsonValue = new FormControl(null, null, validJsonConfig);
79+
public waitingForAsyncTask = false;
80+
public asyncTaskList: AsyncTask[];
81+
public actionConfig: FormActionConfig;
7682

7783
private _pageStack: FormPageComponent[] = [];
84+
private _asyncTaskSub: Subscription;
85+
private _hasAsyncTask = false;
7886

7987
constructor(private changeDetector: ChangeDetectorRef) {
8088
super();
@@ -90,14 +98,28 @@ export class ComplexFormComponent extends FormBase implements AfterViewInit {
9098
this.changeDetector.detectChanges();
9199
}
92100

93-
public get isMainWindow() {
94-
return this.currentPage === this.mainPage;
101+
public ngOnChanges(changes) {
102+
if (changes.asyncTasks) {
103+
this._listenToAsyncTasks();
104+
}
105+
this._buildActionConfig();
95106
}
96107

97108
@autobind()
98109
public save(): Observable<any> {
110+
let ready;
111+
if (this._hasAsyncTask) {
112+
this.waitingForAsyncTask = true;
113+
ready = this.asyncTasks.filter(x => [...x].length === 0).first().do(() => {
114+
this.waitingForAsyncTask = false;
115+
}).share();
116+
} else {
117+
ready = Observable.of(null);
118+
}
99119
this.loading = true;
100-
const obs = this.submit(this.getCurrentDto());
120+
const obs = ready.flatMap(() => {
121+
return this.submit(this.getCurrentDto());
122+
}).shareReplay(1);
101123
obs.subscribe({
102124
next: () => {
103125
this.loading = false;
@@ -163,6 +185,14 @@ export class ComplexFormComponent extends FormBase implements AfterViewInit {
163185
this.closePage();
164186
}
165187

188+
public toggleJsonEditor(jsonEditor) {
189+
if (jsonEditor) {
190+
this.switchToJsonEditor();
191+
} else {
192+
this.switchToClassicForm();
193+
}
194+
}
195+
166196
public switchToJsonEditor() {
167197
if (!this.config.jsonEditor) { return; }
168198
const obj = this.getCurrentDto().toJS();
@@ -189,21 +219,6 @@ export class ComplexFormComponent extends FormBase implements AfterViewInit {
189219
}
190220
}
191221

192-
public get saveAndCloseText() {
193-
return this.multiUse ? `${this.actionName} and close` : this.actionName;
194-
}
195-
196-
/**
197-
* Enabled if the formGroup is valid or there is no formGroup
198-
*/
199-
public get submitEnabled() {
200-
if (this.showJsonEditor) {
201-
return this.jsonValue.valid;
202-
} else {
203-
return !this.formGroup || this.formGroup.valid;
204-
}
205-
}
206-
207222
/**
208223
* There are two cases that classic form selector in footer should be disabled
209224
* 1. showJsonEditor variable is false which means current form is already classic form
@@ -218,4 +233,27 @@ export class ComplexFormComponent extends FormBase implements AfterViewInit {
218233
const data = JSON.parse(this.jsonValue.value);
219234
return new this.config.jsonEditor.dtoType(data || {});
220235
}
236+
237+
private _listenToAsyncTasks() {
238+
if (this._asyncTaskSub) {
239+
this._asyncTaskSub.unsubscribe();
240+
this._asyncTaskSub = null;
241+
this._hasAsyncTask = false;
242+
}
243+
if (this.asyncTasks) {
244+
this._asyncTaskSub = this.asyncTasks.subscribe((asyncTasks) => {
245+
this.asyncTaskList = asyncTasks;
246+
this._hasAsyncTask = asyncTasks.length > 0;
247+
});
248+
}
249+
}
250+
251+
private _buildActionConfig() {
252+
this.actionConfig = {
253+
name: this.actionName,
254+
color: this.actionColor,
255+
cancel: this.cancelText,
256+
multiUse: this.multiUse,
257+
};
258+
}
221259
}
Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,39 @@
11
<div class="content" *ngIf="currentPage">
2-
<div class="header">
3-
<div *ngIf="_pageStack.length > 0">
4-
<bl-button type="wide" color="primary" [action]="closePage">Back</bl-button>
2+
<div class="content-wrapper">
3+
<div class="header">
4+
<div *ngIf="_pageStack.length > 0">
5+
<bl-button type="wide" color="primary" [action]="closePage">Back</bl-button>
6+
</div>
7+
<div class="main">
8+
<h1 *ngIf="currentPage.title">{{currentPage.title}}</h1>
9+
<p *ngIf="currentPage.subtitle">{{currentPage.subtitle}}</p>
10+
</div>
511
</div>
6-
<div class="main">
7-
<h1 *ngIf="currentPage.title">{{currentPage.title}}</h1>
8-
<p *ngIf="currentPage.subtitle">{{currentPage.subtitle}}</p>
12+
<div *ngIf="!showJsonEditor" class="classic-form-container">
13+
<form novalidate>
14+
<ng-template [ngTemplateOutlet]="currentPage.content"></ng-template>
15+
</form>
916
</div>
10-
</div>
11-
<div *ngIf="!showJsonEditor" class="classic-form-container">
12-
<form novalidate>
13-
<ng-template [ngTemplateOutlet]="currentPage.content"></ng-template>
14-
</form>
15-
<div class="loading-overlay" *ngIf="loading"></div>
16-
</div>
1717

18-
<div *ngIf="showJsonEditor" class="json-editor-container">
19-
<bl-form-json-editor [formControl]="jsonValue" [fileUri]="fileUri"></bl-form-json-editor>
18+
<div *ngIf="showJsonEditor" class="json-editor-container">
19+
<bl-form-json-editor [formControl]="jsonValue" [fileUri]="fileUri"></bl-form-json-editor>
20+
</div>
21+
<div class="loading-overlay" *ngIf="loading"></div>
2022
</div>
2123
</div>
2224
<div class="form-server-error" *ngIf="showError">
2325
<bl-server-error [error]="error"></bl-server-error>
2426
</div>
25-
<div class="form-footer" *ngIf="!hideFooter">
26-
<div class="toggle-mode" *ngIf="config.jsonEditor">
27-
<bl-button type="square" [disabled]="classicFormDisabled" (do)="switchToClassicForm()">
28-
<i class="fa fa-commenting"></i>
29-
</bl-button>
30-
<bl-button type="square" [disabled]="showJsonEditor" (do)="switchToJsonEditor()">
31-
<i class="fa fa-code"></i>
32-
</bl-button>
33-
</div>
34-
<div class="toggle-error-btn" *ngIf="error">
35-
<button mat-icon-button color="warn" (click)="toggleShowError()" matTooltip="There was an error submitting this form" matTooltipPosition="above">
36-
<mat-icon fontIcon="fa-exclamation-triangle"></mat-icon>
37-
</button>
38-
</div>
39-
<div class="summary">
40-
<ng-content select="[blFormSummary]"></ng-content>
41-
</div>
42-
<div class="form-buttons">
43-
<div *ngIf="isMainWindow">
44-
<bl-button type="wide" *ngIf="multiUse" [action]="save" [disabled]="!submitEnabled" class="add">{{actionName}}</bl-button>
45-
<bl-button type="wide" *ngIf="containerRef" [action]="saveAndClose" [disabled]="!submitEnabled" [color]="actionColor" class="add-and-close">{{saveAndCloseText}}</bl-button>
46-
<span style="display: inline-block">
47-
<bl-button type="wide" color="light" [disabled]="isSaving" [action]="close" class="close">{{cancelText}}</bl-button>
48-
</span>
49-
</div>
50-
<div *ngIf="!isMainWindow">
51-
<bl-button type="wide" class="cancel" color="primary" [action]="cancelPage">Cancel</bl-button>
52-
<bl-button type="wide" class="select" color="primary" [action]="closePageOrSubmit" [disabled]="!currentPage.submitEnabled">Select</bl-button>
53-
</div>
54-
</div>
55-
</div>
27+
<bl-form-footer class="form-footer" *ngIf="!hideFooter"
28+
[config]="config"
29+
[waitingForAsyncTask]="waitingForAsyncTask"
30+
[asyncTasks]="asyncTaskList"
31+
[jsonValue]="jsonValue"
32+
[showJsonEditor]="showJsonEditor"
33+
[actionConfig]="actionConfig"
34+
[currentPage]="currentPage"
35+
[formGroup]="formGroup"
36+
[error]="error"
37+
[(showError)]="showError"
38+
(showJsonEditorChanges)="toggleJsonEditor($event)">
39+
</bl-form-footer>

app/components/base/form/complex-form/complex-form.scss

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ bl-complex-form {
3434
.header {
3535
display: flex;
3636
margin: 10px;
37+
flex-shrink: 0;
3738

3839
> .main {
3940
flex: 1;
@@ -49,18 +50,25 @@ bl-complex-form {
4950
}
5051

5152
> .content {
52-
overflow-y: auto;
53+
// overflow-y: auto;
5354
position: relative;
5455
height: calc(100% - #{$footer-height});
5556

56-
> .loading-overlay {
57-
position: absolute;
58-
background: white;
59-
opacity: 0.5;
60-
top: 0;
61-
left: 0;
62-
bottom: 0;
63-
right: 0;
57+
> .content-wrapper {
58+
display: flex;
59+
flex-direction: column;
60+
overflow-y: auto;
61+
height: 100%;
62+
63+
.loading-overlay {
64+
position: absolute;
65+
background: white;
66+
opacity: 0.5;
67+
top: 0;
68+
left: 0;
69+
bottom: 0;
70+
right: 0;
71+
}
6472
}
6573

6674
.classic-form {
@@ -82,33 +90,5 @@ bl-complex-form {
8290

8391
> .form-footer {
8492
height: $footer-height;
85-
86-
// background: map-get($primary, 500);
87-
padding: 5px;
88-
display: flex;
89-
align-items: center;
90-
91-
.toggle-mode {
92-
margin: 0 10px;
93-
}
94-
95-
> .toggle-error-btn {
96-
button {
97-
font-size: 26px;
98-
}
99-
}
100-
101-
> .summary {
102-
margin: 0 5px;
103-
flex: 1;
104-
}
105-
106-
> .form-buttons {
107-
margin: 0 5px;
108-
109-
bl-button {
110-
margin-right: 5px;
111-
}
112-
}
11393
}
11494
}

0 commit comments

Comments
 (0)