@universis/ngx-tables 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +163 -0
- package/README.md +121 -0
- package/bundles/universis-ngx-tables.umd.js +4591 -0
- package/bundles/universis-ngx-tables.umd.js.map +1 -0
- package/bundles/universis-ngx-tables.umd.min.js +2 -0
- package/bundles/universis-ngx-tables.umd.min.js.map +1 -0
- package/esm2015/lib/components/add-items/add-items.component.js +142 -0
- package/esm2015/lib/components/advanced-aggregator/advanced-aggregator.component.js +120 -0
- package/esm2015/lib/components/advanced-aggregator-list/advanced-aggregator-list.component.js +53 -0
- package/esm2015/lib/components/advanced-list/advanced-list.component.js +173 -0
- package/esm2015/lib/components/advanced-row-action/advanced-row-action.component.js +175 -0
- package/esm2015/lib/components/advanced-search-form/advanced-search-form.component.js +134 -0
- package/esm2015/lib/components/advanced-select/advanced-select.component.js +51 -0
- package/esm2015/lib/components/advanced-select/advanced-select.service.js +40 -0
- package/esm2015/lib/components/advanced-table/advanced-filter-value-provider.service.js +37 -0
- package/esm2015/lib/components/advanced-table/advanced-table-resolvers.js +44 -0
- package/esm2015/lib/components/advanced-table/advanced-table-search-base.js +23 -0
- package/esm2015/lib/components/advanced-table/advanced-table-search.component.js +519 -0
- package/esm2015/lib/components/advanced-table/advanced-table-settings.component.js +109 -0
- package/esm2015/lib/components/advanced-table/advanced-table.component.js +1007 -0
- package/esm2015/lib/components/advanced-table/advanced-table.formatters.interface.js +16 -0
- package/esm2015/lib/components/advanced-table/advanced-table.formatters.js +271 -0
- package/esm2015/lib/components/advanced-table/advanced-table.interfaces.js +2 -0
- package/esm2015/lib/components/advanced-table-modal/advanced-table-modal-base.component.js +379 -0
- package/esm2015/lib/components/table-simple-header/table-simple-header.component.js +52 -0
- package/esm2015/lib/directives/advanced-table-editor.directive.js +157 -0
- package/esm2015/lib/directives/btnexp.directive.js +225 -0
- package/esm2015/lib/i18n/tables.el.js +104 -0
- package/esm2015/lib/i18n/tables.en.js +103 -0
- package/esm2015/lib/pipes/advanced-aggregator.pipe.js +35 -0
- package/esm2015/lib/pipes/advansed-aggregator-list-title-parse.pipe.js +46 -0
- package/esm2015/lib/services/advanced-aggregator/advanced-aggregator.service.js +117 -0
- package/esm2015/lib/tables.activated-table.service.js +36 -0
- package/esm2015/lib/tables.module.js +134 -0
- package/esm2015/public_api.js +22 -0
- package/esm2015/universis-ngx-tables.js +14 -0
- package/esm5/lib/components/add-items/add-items.component.js +153 -0
- package/esm5/lib/components/advanced-aggregator/advanced-aggregator.component.js +169 -0
- package/esm5/lib/components/advanced-aggregator-list/advanced-aggregator-list.component.js +56 -0
- package/esm5/lib/components/advanced-list/advanced-list.component.js +185 -0
- package/esm5/lib/components/advanced-row-action/advanced-row-action.component.js +179 -0
- package/esm5/lib/components/advanced-search-form/advanced-search-form.component.js +137 -0
- package/esm5/lib/components/advanced-select/advanced-select.component.js +56 -0
- package/esm5/lib/components/advanced-select/advanced-select.service.js +43 -0
- package/esm5/lib/components/advanced-table/advanced-filter-value-provider.service.js +50 -0
- package/esm5/lib/components/advanced-table/advanced-table-resolvers.js +48 -0
- package/esm5/lib/components/advanced-table/advanced-table-search-base.js +29 -0
- package/esm5/lib/components/advanced-table/advanced-table-search.component.js +556 -0
- package/esm5/lib/components/advanced-table/advanced-table-settings.component.js +115 -0
- package/esm5/lib/components/advanced-table/advanced-table.component.js +1035 -0
- package/esm5/lib/components/advanced-table/advanced-table.formatters.interface.js +16 -0
- package/esm5/lib/components/advanced-table/advanced-table.formatters.js +313 -0
- package/esm5/lib/components/advanced-table/advanced-table.interfaces.js +2 -0
- package/esm5/lib/components/advanced-table-modal/advanced-table-modal-base.component.js +295 -0
- package/esm5/lib/components/table-simple-header/table-simple-header.component.js +58 -0
- package/esm5/lib/directives/advanced-table-editor.directive.js +175 -0
- package/esm5/lib/directives/btnexp.directive.js +253 -0
- package/esm5/lib/i18n/tables.el.js +104 -0
- package/esm5/lib/i18n/tables.en.js +103 -0
- package/esm5/lib/pipes/advanced-aggregator.pipe.js +39 -0
- package/esm5/lib/pipes/advansed-aggregator-list-title-parse.pipe.js +49 -0
- package/esm5/lib/services/advanced-aggregator/advanced-aggregator.service.js +130 -0
- package/esm5/lib/tables.activated-table.service.js +43 -0
- package/esm5/lib/tables.module.js +139 -0
- package/esm5/public_api.js +22 -0
- package/esm5/universis-ngx-tables.js +14 -0
- package/fesm2015/universis-ngx-tables.js +4127 -0
- package/fesm2015/universis-ngx-tables.js.map +1 -0
- package/fesm5/universis-ngx-tables.js +4313 -0
- package/fesm5/universis-ngx-tables.js.map +1 -0
- package/lib/advanced-aggregator.d.ts +124 -0
- package/lib/components/add-items/add-items.component.d.ts +44 -0
- package/lib/components/advanced-aggregator/advanced-aggregator.component.d.ts +45 -0
- package/lib/components/advanced-aggregator-list/advanced-aggregator-list.component.d.ts +25 -0
- package/lib/components/advanced-list/advanced-list.component.d.ts +42 -0
- package/lib/components/advanced-row-action/advanced-row-action.component.d.ts +42 -0
- package/lib/components/advanced-search-form/advanced-search-form.component.d.ts +27 -0
- package/lib/components/advanced-select/advanced-select.component.d.ts +14 -0
- package/lib/components/advanced-select/advanced-select.service.d.ts +9 -0
- package/lib/components/advanced-table/advanced-filter-value-provider.service.d.ts +8 -0
- package/lib/components/advanced-table/advanced-table-resolvers.d.ts +12 -0
- package/lib/components/advanced-table/advanced-table-search-base.d.ts +7 -0
- package/lib/components/advanced-table/advanced-table-search.component.d.ts +80 -0
- package/lib/components/advanced-table/advanced-table-settings.component.d.ts +35 -0
- package/lib/components/advanced-table/advanced-table.component.d.ts +142 -0
- package/lib/components/advanced-table/advanced-table.formatters.d.ts +62 -0
- package/lib/components/advanced-table/advanced-table.formatters.interface.d.ts +15 -0
- package/lib/components/advanced-table/advanced-table.interfaces.d.ts +27 -0
- package/lib/components/advanced-table-modal/advanced-table-modal-base.component.d.ts +82 -0
- package/lib/components/table-simple-header/table-simple-header.component.d.ts +14 -0
- package/lib/directives/advanced-table-editor.directive.d.ts +25 -0
- package/lib/directives/btnexp.directive.d.ts +68 -0
- package/lib/i18n/tables.el.d.ts +102 -0
- package/lib/i18n/tables.en.d.ts +101 -0
- package/lib/pipes/advanced-aggregator.pipe.d.ts +21 -0
- package/lib/pipes/advansed-aggregator-list-title-parse.pipe.d.ts +21 -0
- package/lib/services/advanced-aggregator/advanced-aggregator.service.d.ts +32 -0
- package/lib/tables.activated-table.service.d.ts +9 -0
- package/lib/tables.module.d.ts +7 -0
- package/package.json +51 -0
- package/public_api.d.ts +18 -0
- package/universis-ngx-tables.d.ts +12 -0
- package/universis-ngx-tables.metadata.json +1 -0
|
@@ -0,0 +1,4127 @@
|
|
|
1
|
+
import { at } from 'lodash';
|
|
2
|
+
import { getNativeName } from 'iso-639-1/build';
|
|
3
|
+
import * as jQuery_ from 'jquery';
|
|
4
|
+
import 'datatables.net';
|
|
5
|
+
import 'datatables.net-scroller';
|
|
6
|
+
import 'rxjs/add/observable/combineLatest';
|
|
7
|
+
import { HttpClient } from '@angular/common/http';
|
|
8
|
+
import { DatePipe, CommonModule } from '@angular/common';
|
|
9
|
+
import { ClientDataQueryable, ResponseError, TextUtils } from '@themost/client';
|
|
10
|
+
import { FormArray, FormBuilder, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
11
|
+
import { FormioModule } from 'angular-formio';
|
|
12
|
+
import { BsModalService, BsModalRef, ModalModule } from 'ngx-bootstrap/modal';
|
|
13
|
+
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
|
|
14
|
+
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
|
|
15
|
+
import { __awaiter } from 'tslib';
|
|
16
|
+
import { GroupByPipe, NgPipesModule } from 'ngx-pipes';
|
|
17
|
+
import { Injectable, Pipe, EventEmitter, Input, Output, Component, ElementRef, ViewChild, ViewEncapsulation, Directive, ChangeDetectorRef, Inject, InjectionToken, Injector, HostListener, defineInjectable, inject, NgModule, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy } from '@angular/core';
|
|
18
|
+
import { RouterModalOkCancel, RouterModalModule } from '@universis/common/routing';
|
|
19
|
+
import { ActivatedRoute, Router, NavigationStart, RouterModule } from '@angular/router';
|
|
20
|
+
import { BehaviorSubject } from 'rxjs';
|
|
21
|
+
import { TranslateService, TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
|
22
|
+
import { AngularDataContext } from '@themost/angular';
|
|
23
|
+
import { ServiceUrlPreProcessor, TranslatePreProcessor, AdvancedFormsModule } from '@universis/forms';
|
|
24
|
+
import { TemplatePipe, LoadingService, ErrorService, DIALOG_BUTTONS, ModalService, UserActivityService, ToastService, AppEventService, UserStorageService, ConfigurationService, SharedModule } from '@universis/common';
|
|
25
|
+
|
|
26
|
+
class AdvancedColumnFormatter {
|
|
27
|
+
}
|
|
28
|
+
class NestedPropertyFormatter extends AdvancedColumnFormatter {
|
|
29
|
+
render(data, type, row, meta) {
|
|
30
|
+
// get column
|
|
31
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
32
|
+
if (column && column.data) {
|
|
33
|
+
return at(row, column.data.replace(/\//g, '.'));
|
|
34
|
+
}
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
class ButtonFormatter extends AdvancedColumnFormatter {
|
|
42
|
+
render(data, type, row, meta) {
|
|
43
|
+
if (data == null) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
// get column
|
|
47
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
48
|
+
// get format options
|
|
49
|
+
const formatOptions = Object.assign({
|
|
50
|
+
buttonClass: 'btn btn-default'
|
|
51
|
+
}, this.formatOptions);
|
|
52
|
+
// get text content
|
|
53
|
+
let buttonText;
|
|
54
|
+
let buttonTitle = '';
|
|
55
|
+
if (formatOptions.buttonText) {
|
|
56
|
+
buttonText = this.injector.get(TranslateService).instant(formatOptions.buttonText);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
buttonText = formatOptions.buttonContent;
|
|
60
|
+
}
|
|
61
|
+
if (formatOptions.buttonTitle) {
|
|
62
|
+
buttonTitle = this.injector.get(TranslateService).instant(formatOptions.buttonTitle);
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(this.formatOptions.commands)) {
|
|
65
|
+
const activatedRoute = this.injector.get(ActivatedRoute);
|
|
66
|
+
const router = this.injector.get(Router);
|
|
67
|
+
const commands = this.formatOptions.commands.map(command => {
|
|
68
|
+
if (typeof command === 'string') {
|
|
69
|
+
return new TemplatePipe().transform(command, row);
|
|
70
|
+
}
|
|
71
|
+
if (command.outlets) {
|
|
72
|
+
const commandOutlets = {};
|
|
73
|
+
Object.keys(command.outlets).forEach((key) => {
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(command.outlets, key)) {
|
|
75
|
+
const outlet = command.outlets[key];
|
|
76
|
+
if (Array.isArray(outlet)) {
|
|
77
|
+
Object.defineProperty(commandOutlets, key, {
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: true,
|
|
80
|
+
writable: true,
|
|
81
|
+
value: outlet.map((x) => {
|
|
82
|
+
return new TemplatePipe().transform(x, row);
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
Object.defineProperty(commandOutlets, key, {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: true,
|
|
90
|
+
writable: true,
|
|
91
|
+
value: outlet
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
outlets: commandOutlets
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return command;
|
|
101
|
+
});
|
|
102
|
+
const queryParams = {};
|
|
103
|
+
let replaceUrl = true;
|
|
104
|
+
if (this.formatOptions.navigationExtras && this.formatOptions.navigationExtras.queryParams) {
|
|
105
|
+
Object.keys(this.formatOptions.navigationExtras.queryParams).map(key => {
|
|
106
|
+
queryParams[key] = new TemplatePipe().transform(this.formatOptions.navigationExtras.queryParams[key], row);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (this.formatOptions.navigationExtras && typeof this.formatOptions.navigationExtras.replaceUrl !== 'undefined') {
|
|
110
|
+
replaceUrl = this.formatOptions.navigationExtras.replaceUrl;
|
|
111
|
+
}
|
|
112
|
+
let skipLocationChange = false;
|
|
113
|
+
if (this.formatOptions.navigationExtras && typeof this.formatOptions.navigationExtras.skipLocationChange !== 'undefined') {
|
|
114
|
+
skipLocationChange = this.formatOptions.navigationExtras.skipLocationChange;
|
|
115
|
+
}
|
|
116
|
+
const routerLink = router.createUrlTree(commands, {
|
|
117
|
+
relativeTo: activatedRoute,
|
|
118
|
+
queryParams: queryParams,
|
|
119
|
+
replaceUrl: replaceUrl,
|
|
120
|
+
skipLocationChange: skipLocationChange
|
|
121
|
+
}).toString();
|
|
122
|
+
return `<a class="${formatOptions.buttonClass}" title="${buttonTitle}" href="#${routerLink}">${buttonText}</a>`;
|
|
123
|
+
}
|
|
124
|
+
// and return button
|
|
125
|
+
const href = new TemplatePipe().transform(this.formatString, row);
|
|
126
|
+
return `<a class="${formatOptions.buttonClass}" title="${buttonTitle}" href="${href}">${buttonText}</a>`;
|
|
127
|
+
}
|
|
128
|
+
constructor() {
|
|
129
|
+
super();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
class LinkFormatter extends AdvancedColumnFormatter {
|
|
133
|
+
render(data, type, row, meta) {
|
|
134
|
+
// get column
|
|
135
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
136
|
+
if (column && column.data) {
|
|
137
|
+
return `<a href="${(new TemplatePipe().transform(this.formatString, row))}">${data}</a>`;
|
|
138
|
+
}
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
141
|
+
constructor() {
|
|
142
|
+
super();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
class ActionLinkFormatter extends AdvancedColumnFormatter {
|
|
146
|
+
render(data, type, row, meta) {
|
|
147
|
+
// get column
|
|
148
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
149
|
+
if (column && column.data) {
|
|
150
|
+
this.actions = this.actions || [];
|
|
151
|
+
const allowedActions = this.actions.filter(x => {
|
|
152
|
+
if (x.access && x.access.length > 0) {
|
|
153
|
+
let access = x.access;
|
|
154
|
+
access = access.filter(y => {
|
|
155
|
+
if (y.studentStatuses) {
|
|
156
|
+
return y.studentStatuses.find(z => z.alternateName === row.studentStatus);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
if (access && access.length > 0) {
|
|
160
|
+
return x;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
return x;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
// return the dropdown menu with 3 dots
|
|
168
|
+
return new TemplatePipe().transform(`<div class='dropdown details-control' data-id='${data}' data-type='student' >
|
|
169
|
+
<a id="dropdownMenuButton-${data}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
170
|
+
<span class="fa-2x text-dots"></span></a>
|
|
171
|
+
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton-${data}">
|
|
172
|
+
${allowedActions.map(x => `<a data-name='${x.name}' class="dropdown-item" href="${x.href}">
|
|
173
|
+
${this.injector.get(TranslateService).instant(new TemplatePipe().transform(x.title, ({ value: data })))}
|
|
174
|
+
</a>`)
|
|
175
|
+
.join('')}
|
|
176
|
+
</div>
|
|
177
|
+
</div>`, row);
|
|
178
|
+
}
|
|
179
|
+
return data;
|
|
180
|
+
}
|
|
181
|
+
constructor() {
|
|
182
|
+
super();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
class SelectRowFormatter extends AdvancedColumnFormatter {
|
|
186
|
+
render(data, type, row, meta) {
|
|
187
|
+
// get column
|
|
188
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
189
|
+
if (column && column.data) {
|
|
190
|
+
let _tpl = `<input class="checkbox checkbox-theme" type="checkbox"
|
|
191
|
+
id="s-${meta.row}" /><label for="s-${meta.row}"> </label>`;
|
|
192
|
+
return new TemplatePipe().transform(_tpl, row);
|
|
193
|
+
}
|
|
194
|
+
return data;
|
|
195
|
+
}
|
|
196
|
+
constructor() {
|
|
197
|
+
super();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
class TemplateFormatter extends AdvancedColumnFormatter {
|
|
201
|
+
render(data, type, row, meta) {
|
|
202
|
+
// get column
|
|
203
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
204
|
+
if (column && column.data) {
|
|
205
|
+
return new TemplatePipe().transform(this.formatString, row);
|
|
206
|
+
}
|
|
207
|
+
return data;
|
|
208
|
+
}
|
|
209
|
+
constructor() {
|
|
210
|
+
super();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
class TrueFalseFormatter extends AdvancedColumnFormatter {
|
|
214
|
+
render(data, type, row, meta) {
|
|
215
|
+
return data ? this.injector.get(TranslateService).instant('TrueFalse.long.Yes') :
|
|
216
|
+
this.injector.get(TranslateService).instant('TrueFalse.long.No');
|
|
217
|
+
}
|
|
218
|
+
constructor() {
|
|
219
|
+
super();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
class TranslationFormatter extends AdvancedColumnFormatter {
|
|
223
|
+
render(data, type, row, meta) {
|
|
224
|
+
// get column
|
|
225
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
226
|
+
if (column && data != null) {
|
|
227
|
+
return this.injector.get(TranslateService).instant(new TemplatePipe().transform(this.formatString, { value: data }));
|
|
228
|
+
}
|
|
229
|
+
return this.injector.get(TranslateService).instant('Unknown');
|
|
230
|
+
}
|
|
231
|
+
constructor() {
|
|
232
|
+
super();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
class DateTimeFormatter extends AdvancedColumnFormatter {
|
|
236
|
+
render(data, type, row, meta) {
|
|
237
|
+
// get column
|
|
238
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
239
|
+
if (column && column.data) {
|
|
240
|
+
const datePipe = new DatePipe(this.injector.get(TranslateService).currentLang);
|
|
241
|
+
if (typeof data === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{2}$/.test(data)) {
|
|
242
|
+
return datePipe.transform(new Date(data + ':00'), this.formatString);
|
|
243
|
+
}
|
|
244
|
+
return datePipe.transform(data, this.formatString);
|
|
245
|
+
}
|
|
246
|
+
return data;
|
|
247
|
+
}
|
|
248
|
+
constructor() {
|
|
249
|
+
super();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
class NgClassFormatter extends AdvancedColumnFormatter {
|
|
253
|
+
render(data, type, row, meta) {
|
|
254
|
+
// get column
|
|
255
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
256
|
+
if (column && column.data) {
|
|
257
|
+
// get formatOptions
|
|
258
|
+
if (this.formatOptions) {
|
|
259
|
+
const ngClass = this.formatOptions.ngClass;
|
|
260
|
+
// check condition for each key
|
|
261
|
+
for (const key of Object.keys(ngClass)) {
|
|
262
|
+
const result = new TemplatePipe().transform(ngClass[key], row);
|
|
263
|
+
const fn = new Function(`return ${result};`);
|
|
264
|
+
// if condition returns true add class
|
|
265
|
+
if (fn() === true) {
|
|
266
|
+
return `<span class="${key}">${data}</span>`;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return data;
|
|
271
|
+
}
|
|
272
|
+
return data;
|
|
273
|
+
}
|
|
274
|
+
constructor() {
|
|
275
|
+
super();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
class LanguageFormatter extends AdvancedColumnFormatter {
|
|
279
|
+
render(data, type, row, meta) {
|
|
280
|
+
if (data) {
|
|
281
|
+
return getNativeName(data);
|
|
282
|
+
}
|
|
283
|
+
return data;
|
|
284
|
+
}
|
|
285
|
+
constructor() {
|
|
286
|
+
super();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
class AdvancedFilterValueProvider {
|
|
291
|
+
constructor() {
|
|
292
|
+
this.values = {};
|
|
293
|
+
this._template = new TemplatePipe();
|
|
294
|
+
}
|
|
295
|
+
buildFilter(filter) {
|
|
296
|
+
if (filter) {
|
|
297
|
+
return this._template.transform(filter, this.values);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
asyncBuildFilter(filter) {
|
|
301
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
if (filter) {
|
|
303
|
+
return this._template.transform(filter, yield this.getValues());
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
getValues() {
|
|
308
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
309
|
+
return this.values;
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
AdvancedFilterValueProvider.decorators = [
|
|
314
|
+
{ type: Injectable, args: [{
|
|
315
|
+
providedIn: 'root'
|
|
316
|
+
},] }
|
|
317
|
+
];
|
|
318
|
+
/** @nocollapse */
|
|
319
|
+
AdvancedFilterValueProvider.ctorParameters = () => [];
|
|
320
|
+
AdvancedFilterValueProvider.ngInjectableDef = defineInjectable({ factory: function AdvancedFilterValueProvider_Factory() { return new AdvancedFilterValueProvider(); }, token: AdvancedFilterValueProvider, providedIn: "root" });
|
|
321
|
+
|
|
322
|
+
// restore jquery import to avoid Cannot call a namespace ('jquery') error
|
|
323
|
+
const jQuery = jQuery_;
|
|
324
|
+
const DataTablesApi = jQuery.fn.dataTable.Api;
|
|
325
|
+
DataTablesApi.register('processing()', function (show) {
|
|
326
|
+
return this.iterator('table', function (ctx) {
|
|
327
|
+
ctx.oApi._fnProcessingDisplay(ctx, show);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
class TableTranslationChangeDetector extends ChangeDetectorRef {
|
|
331
|
+
checkNoChanges() {
|
|
332
|
+
}
|
|
333
|
+
detach() {
|
|
334
|
+
}
|
|
335
|
+
detectChanges() {
|
|
336
|
+
}
|
|
337
|
+
markForCheck() {
|
|
338
|
+
}
|
|
339
|
+
reattach() {
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
let COLUMN_FORMATTERS = new InjectionToken('column.formatters');
|
|
343
|
+
class ColumnFormatter extends AdvancedColumnFormatter {
|
|
344
|
+
constructor(injector) {
|
|
345
|
+
super();
|
|
346
|
+
this.injector = injector;
|
|
347
|
+
}
|
|
348
|
+
render(data, type, row, meta) {
|
|
349
|
+
//
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function forceCast(input) {
|
|
353
|
+
return input;
|
|
354
|
+
}
|
|
355
|
+
class AdvancedTableConfiguration {
|
|
356
|
+
static cast(input, clone) {
|
|
357
|
+
if (clone) {
|
|
358
|
+
return forceCast(JSON.parse(JSON.stringify(input)));
|
|
359
|
+
}
|
|
360
|
+
return forceCast(input);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function getRandomIdentifier() {
|
|
364
|
+
return (Number(String(Math.random()).slice(2))).toString(36);
|
|
365
|
+
}
|
|
366
|
+
class AdvancedTableComponent {
|
|
367
|
+
constructor(_context, _columnFormatters, _activatedRoute, _translateService, _injector, _advancedFilterValueProvider, _element, _template, _loading, _http) {
|
|
368
|
+
this._context = _context;
|
|
369
|
+
this._columnFormatters = _columnFormatters;
|
|
370
|
+
this._activatedRoute = _activatedRoute;
|
|
371
|
+
this._translateService = _translateService;
|
|
372
|
+
this._injector = _injector;
|
|
373
|
+
this._advancedFilterValueProvider = _advancedFilterValueProvider;
|
|
374
|
+
this._element = _element;
|
|
375
|
+
this._template = _template;
|
|
376
|
+
this._loading = _loading;
|
|
377
|
+
this._http = _http;
|
|
378
|
+
this._firstLoad = true;
|
|
379
|
+
this._showFooter = true;
|
|
380
|
+
this._showPaging = false;
|
|
381
|
+
this._id = getRandomIdentifier();
|
|
382
|
+
this.smartSelect = false;
|
|
383
|
+
this.configChanges = new BehaviorSubject(null);
|
|
384
|
+
this.height = 800;
|
|
385
|
+
this.showActions = true;
|
|
386
|
+
this.autoLoad = true;
|
|
387
|
+
this.load = new EventEmitter();
|
|
388
|
+
this.init = new EventEmitter();
|
|
389
|
+
this.reload = new EventEmitter();
|
|
390
|
+
this.select = new EventEmitter();
|
|
391
|
+
this.selectedItems = new EventEmitter();
|
|
392
|
+
this.scrollable = true;
|
|
393
|
+
this.serverSide = true;
|
|
394
|
+
this.lengthMenu = [50, 100, 200, 500];
|
|
395
|
+
this.paging = true;
|
|
396
|
+
this.pageLength = 25;
|
|
397
|
+
this.scrollY = 800;
|
|
398
|
+
this.scrollX = true;
|
|
399
|
+
/**
|
|
400
|
+
* Enables row selection. The defaulr value is false
|
|
401
|
+
*/
|
|
402
|
+
this.selectable = false;
|
|
403
|
+
/**
|
|
404
|
+
* Enables or disable multiple selection. The default values is true.
|
|
405
|
+
*/
|
|
406
|
+
this.multipleSelect = true;
|
|
407
|
+
/**
|
|
408
|
+
* Enables custom selection and hides select column. The default value is false
|
|
409
|
+
*/
|
|
410
|
+
this.customSelect = false;
|
|
411
|
+
this.selected = [];
|
|
412
|
+
this.unselected = [];
|
|
413
|
+
// initialize translate pipe
|
|
414
|
+
this.translator = new TranslatePipe(this._translateService, new TableTranslationChangeDetector());
|
|
415
|
+
jQuery.fn.dataTable.ext.classes.sPageButton = 'btn';
|
|
416
|
+
jQuery.fn.dataTable.ext.classes.sPageButtonActive = 'btn btn-light';
|
|
417
|
+
this.reloadSubscription = this.reload.subscribe(data => {
|
|
418
|
+
this.fetch();
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Gets or sets table configuration
|
|
423
|
+
*/
|
|
424
|
+
set config(value) {
|
|
425
|
+
this._config = value;
|
|
426
|
+
// raise subscription
|
|
427
|
+
this.configChanges.next(this._config);
|
|
428
|
+
}
|
|
429
|
+
get config() {
|
|
430
|
+
return this._config;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Indicates whether table footer will be visible or not
|
|
434
|
+
*/
|
|
435
|
+
set showFooter(value) {
|
|
436
|
+
this._showFooter = value;
|
|
437
|
+
// show or hide footer
|
|
438
|
+
if (this._element && this._element.nativeElement) {
|
|
439
|
+
const element = this._element.nativeElement
|
|
440
|
+
.querySelector('.dataTables_info');
|
|
441
|
+
if (element == null) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (value) {
|
|
445
|
+
element.classList.remove('d-none');
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
element.classList.add('d-none');
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
get showFooter() {
|
|
453
|
+
return this._showFooter;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Indicates whether paging information will be visible or not
|
|
457
|
+
*/
|
|
458
|
+
set showPaging(value) {
|
|
459
|
+
this._showPaging = value;
|
|
460
|
+
// show or hide footer
|
|
461
|
+
if (this._element && this._element.nativeElement) {
|
|
462
|
+
const element = this._element.nativeElement
|
|
463
|
+
.querySelector('.dataTables_paginate');
|
|
464
|
+
if (element == null) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (value) {
|
|
468
|
+
jQuery(element).show();
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
jQuery(element).hide();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
get showPaging() {
|
|
476
|
+
return this._showPaging;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Gets or sets an active query for this table
|
|
480
|
+
*/
|
|
481
|
+
set query(value) {
|
|
482
|
+
this._query = value;
|
|
483
|
+
}
|
|
484
|
+
get query() {
|
|
485
|
+
return this._query;
|
|
486
|
+
}
|
|
487
|
+
// simple search
|
|
488
|
+
search(searchText) {
|
|
489
|
+
this.query = this.query || this._context.model(this.config.model).asQueryable();
|
|
490
|
+
if (searchText && this.config.searchExpression) {
|
|
491
|
+
// validate search text in double quotes
|
|
492
|
+
if (/^"(.*?)"$/.test(searchText)) {
|
|
493
|
+
this.query.setParam('$filter', this._template.transform(this.config.searchExpression, {
|
|
494
|
+
text: searchText.replace(/^"|"$/g, '')
|
|
495
|
+
}));
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
// try to split words
|
|
499
|
+
const words = searchText.split(' ');
|
|
500
|
+
// join words to a single filter
|
|
501
|
+
const filter = words.map(word => {
|
|
502
|
+
return this._template.transform(this.config.searchExpression, {
|
|
503
|
+
text: word
|
|
504
|
+
});
|
|
505
|
+
}).join(' and ');
|
|
506
|
+
// set filter
|
|
507
|
+
this.query.setParam('$filter', filter);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
// clear filter
|
|
512
|
+
this.query.setParam('$filter', null);
|
|
513
|
+
}
|
|
514
|
+
this._firstLoad = false;
|
|
515
|
+
this.dataTable.draw();
|
|
516
|
+
}
|
|
517
|
+
fetch(clearSelected) {
|
|
518
|
+
// if data table is null
|
|
519
|
+
if (this.dataTable == null) {
|
|
520
|
+
// initialize data table
|
|
521
|
+
return this.ngOnInit();
|
|
522
|
+
}
|
|
523
|
+
// clear selection if parameter is not declared or it's true
|
|
524
|
+
if (clearSelected == null || clearSelected === true) {
|
|
525
|
+
// set smart select to false
|
|
526
|
+
this.smartSelect = false;
|
|
527
|
+
// clear selected items
|
|
528
|
+
if (this.selected.length) {
|
|
529
|
+
this.selected.splice(0, this.selected.length);
|
|
530
|
+
}
|
|
531
|
+
// clear unselected items
|
|
532
|
+
if (this.unselected.length) {
|
|
533
|
+
this.unselected.splice(0, this.unselected.length);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// otherwise load data only
|
|
537
|
+
this.dataTable.draw();
|
|
538
|
+
}
|
|
539
|
+
destroy() {
|
|
540
|
+
if (this.dataTable) {
|
|
541
|
+
this.dataTable.destroy();
|
|
542
|
+
this._firstLoad = true;
|
|
543
|
+
jQuery(this.table.nativeElement).empty();
|
|
544
|
+
this.dataTable = null;
|
|
545
|
+
// clear selected items
|
|
546
|
+
if (this.selected.length) {
|
|
547
|
+
this.selected.splice(0, this.selected.length);
|
|
548
|
+
}
|
|
549
|
+
// clear unselected items
|
|
550
|
+
if (this.unselected.length) {
|
|
551
|
+
this.unselected.splice(0, this.unselected.length);
|
|
552
|
+
}
|
|
553
|
+
// set smart select to false
|
|
554
|
+
this.smartSelect = false;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Selects the specified data row
|
|
559
|
+
*/
|
|
560
|
+
selectRow(row, silent) {
|
|
561
|
+
if (row) {
|
|
562
|
+
// get row
|
|
563
|
+
const rowNode = row.node();
|
|
564
|
+
// if multiple select is false
|
|
565
|
+
if (this.multipleSelect === false) {
|
|
566
|
+
if (this.customSelect === false) {
|
|
567
|
+
// enumerate row
|
|
568
|
+
this.dataTable.rows().nodes().each((node) => {
|
|
569
|
+
if (node !== rowNode) {
|
|
570
|
+
jQuery(node).find(':checkbox').prop('checked', false);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
this.dataTable.rows('.selected').nodes().each((node) => {
|
|
576
|
+
if (node !== rowNode) {
|
|
577
|
+
jQuery(node).removeClass('selected');
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
// clear selected
|
|
582
|
+
this.selected.splice(0, this.selected.length);
|
|
583
|
+
}
|
|
584
|
+
// if custom select mode is off
|
|
585
|
+
if (this.customSelect === false) {
|
|
586
|
+
jQuery(rowNode).find(':checkbox').prop('checked', true);
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
// otherwise add class
|
|
590
|
+
jQuery(row.node()).addClass('selected');
|
|
591
|
+
}
|
|
592
|
+
// get row data
|
|
593
|
+
const key = this.config.columns[0].property || this.config.columns[0].name;
|
|
594
|
+
const data = row.data();
|
|
595
|
+
// search selected items
|
|
596
|
+
let findIndex = this.selected.findIndex((x) => {
|
|
597
|
+
return x[key] === data[key];
|
|
598
|
+
});
|
|
599
|
+
if (findIndex >= 0) {
|
|
600
|
+
// remove previously selected row
|
|
601
|
+
this.selected.splice(findIndex, 1);
|
|
602
|
+
}
|
|
603
|
+
this.selected.push(data);
|
|
604
|
+
// validate unselected data
|
|
605
|
+
findIndex = this.unselected.findIndex((x) => {
|
|
606
|
+
return x[key] === data[key];
|
|
607
|
+
});
|
|
608
|
+
if (findIndex >= 0) {
|
|
609
|
+
// remove previously selected row
|
|
610
|
+
this.unselected.splice(findIndex, 1);
|
|
611
|
+
}
|
|
612
|
+
if (silent) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
this.selectedItems.emit(this.selected);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Unselects the specified data row if it's selected
|
|
620
|
+
*/
|
|
621
|
+
unSelectRow(row, silent) {
|
|
622
|
+
if (row) {
|
|
623
|
+
// if custom select mode is off
|
|
624
|
+
if (this.customSelect === false) {
|
|
625
|
+
jQuery(row.node()).find(':checkbox').prop('checked', false);
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// otherwise add class
|
|
629
|
+
jQuery(row.node()).removeClass('selected');
|
|
630
|
+
}
|
|
631
|
+
// get first column key
|
|
632
|
+
const key = this.config.columns[0].property || this.config.columns[0].name;
|
|
633
|
+
const data = row.data();
|
|
634
|
+
const id = data[key];
|
|
635
|
+
let findIndex = this.selected.findIndex(x => {
|
|
636
|
+
return x[key] === id;
|
|
637
|
+
});
|
|
638
|
+
if (findIndex >= 0) {
|
|
639
|
+
this.selected.splice(findIndex, 1);
|
|
640
|
+
if (this.smartSelect) {
|
|
641
|
+
// add this row to unselected rows
|
|
642
|
+
findIndex = this.unselected.findIndex(x => {
|
|
643
|
+
return x[key] === id;
|
|
644
|
+
});
|
|
645
|
+
if (findIndex < 0) {
|
|
646
|
+
this.unselected.push(data);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (silent) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
this.selectedItems.emit(this.selected);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
selectAny() {
|
|
657
|
+
if (this.dataTable && this.selectable) {
|
|
658
|
+
// set smart select flag
|
|
659
|
+
this.smartSelect = true;
|
|
660
|
+
// select any row
|
|
661
|
+
this.dataTable.rows().every((index) => {
|
|
662
|
+
const row = this.dataTable.row(index);
|
|
663
|
+
this.selectRow(row, true);
|
|
664
|
+
});
|
|
665
|
+
// emit selection event
|
|
666
|
+
this.selectedItems.emit(this.selected);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
selectNone() {
|
|
670
|
+
if (this.dataTable && this.selectable) {
|
|
671
|
+
// set smart select flag
|
|
672
|
+
this.smartSelect = false;
|
|
673
|
+
this.unselected.splice(0, this.unselected.length);
|
|
674
|
+
// select any row
|
|
675
|
+
this.dataTable.rows().every((index) => {
|
|
676
|
+
const row = this.dataTable.row(index);
|
|
677
|
+
this.unSelectRow(row, true);
|
|
678
|
+
});
|
|
679
|
+
this.selected.splice(0, this.selected.length);
|
|
680
|
+
// emit selection event
|
|
681
|
+
this.selectedItems.emit(this.selected);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
toggleSelectRow(row) {
|
|
685
|
+
if (row) {
|
|
686
|
+
const data = row.data();
|
|
687
|
+
// get first column key
|
|
688
|
+
const key = this.config.columns[0].property || this.config.columns[0].name;
|
|
689
|
+
// get id
|
|
690
|
+
const id = data[key];
|
|
691
|
+
// find if row is selected
|
|
692
|
+
const findIndex = this.selected.find(x => {
|
|
693
|
+
return x[key] === id;
|
|
694
|
+
});
|
|
695
|
+
// if row is selected
|
|
696
|
+
if (findIndex >= 0) {
|
|
697
|
+
// unselect
|
|
698
|
+
this.unSelectRow(row);
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
this.selectRow(row);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
// noinspection JSMethodCanBeStatic
|
|
706
|
+
/**
|
|
707
|
+
* Datatables column renderer for select checkbox
|
|
708
|
+
* @param data
|
|
709
|
+
* @param type
|
|
710
|
+
* @param row
|
|
711
|
+
* @param meta
|
|
712
|
+
* @private
|
|
713
|
+
*/
|
|
714
|
+
_renderSelect(data, type, row, meta) {
|
|
715
|
+
// get column
|
|
716
|
+
const column = meta.settings.aoColumns[meta.col];
|
|
717
|
+
// render template
|
|
718
|
+
return this._template.transform(`<input class="checkbox checkbox-theme s-row" type="checkbox"
|
|
719
|
+
id="s-${this._id}-${meta.row}" data-id="${data}" /><label for="s-${this._id}-${meta.row}"> </label>`, row);
|
|
720
|
+
}
|
|
721
|
+
ngOnInit() {
|
|
722
|
+
const self = this;
|
|
723
|
+
if (this.dataTable) {
|
|
724
|
+
this.destroy();
|
|
725
|
+
}
|
|
726
|
+
if (this.config) {
|
|
727
|
+
// parse properties
|
|
728
|
+
if (Object.prototype.hasOwnProperty.call(this.config, 'selectable')) {
|
|
729
|
+
this.selectable = this.config.selectable;
|
|
730
|
+
}
|
|
731
|
+
if (Object.prototype.hasOwnProperty.call(this.config, 'multipleSelect')) {
|
|
732
|
+
this.multipleSelect = this.config.multipleSelect;
|
|
733
|
+
}
|
|
734
|
+
// prepare configuration for columns
|
|
735
|
+
const tableColumns = this.config.columns
|
|
736
|
+
// filter action link column
|
|
737
|
+
.filter(column => {
|
|
738
|
+
if (column.formatter && column.formatter === 'ActionLinkFormatter' && this.showActions === false) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
return true;
|
|
742
|
+
}).map(column => {
|
|
743
|
+
// convert column to advance table column
|
|
744
|
+
return Object.assign(new ColumnFormatter(this._injector), column);
|
|
745
|
+
}).map(column => {
|
|
746
|
+
const columnDefinition = {
|
|
747
|
+
data: column.property || column.name,
|
|
748
|
+
defaultContent: column.hasOwnProperty('defaultContent') ? column.defaultContent : '',
|
|
749
|
+
title: this.translator.transform(column.title),
|
|
750
|
+
sortable: column.hasOwnProperty('sortable') ? column.sortable : true,
|
|
751
|
+
visible: column.hasOwnProperty('hidden') ? !column.hidden : true,
|
|
752
|
+
name: column.name
|
|
753
|
+
};
|
|
754
|
+
if (column.className) {
|
|
755
|
+
Object.assign(columnDefinition, {
|
|
756
|
+
className: column.className
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
Object.assign(columnDefinition, {
|
|
761
|
+
className: ''
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
if (column.formatters) {
|
|
765
|
+
Object.assign(columnDefinition, {
|
|
766
|
+
render: function (data, type, row, meta) {
|
|
767
|
+
let result = data;
|
|
768
|
+
column.formatters.forEach(columnFormatter => {
|
|
769
|
+
const formatter = self._columnFormatters[columnFormatter.formatter];
|
|
770
|
+
if (formatter && typeof formatter.render === 'function') {
|
|
771
|
+
column.formatString = columnFormatter.formatString;
|
|
772
|
+
column.formatOptions = columnFormatter.formatOptions;
|
|
773
|
+
}
|
|
774
|
+
result = formatter.render.bind(column)(result, type, row, meta);
|
|
775
|
+
});
|
|
776
|
+
delete column.formatString;
|
|
777
|
+
delete column.formatOptions;
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
if (column.formatter) {
|
|
783
|
+
const formatter = self._columnFormatters[column.formatter];
|
|
784
|
+
if (formatter && typeof formatter.render === 'function') {
|
|
785
|
+
Object.assign(columnDefinition, {
|
|
786
|
+
render: formatter.render.bind(column)
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return columnDefinition;
|
|
791
|
+
});
|
|
792
|
+
// get table element
|
|
793
|
+
const tableElement = jQuery(this.table.nativeElement);
|
|
794
|
+
// allow selection
|
|
795
|
+
if (this.selectable) {
|
|
796
|
+
// get first column (as row primary key)
|
|
797
|
+
// todo::allow row primary key configuration
|
|
798
|
+
const firstColumn = this.config.columns[0];
|
|
799
|
+
// add select column
|
|
800
|
+
const selectColumnDefinition = {
|
|
801
|
+
className: 'text-center pl-4 pr-0',
|
|
802
|
+
data: firstColumn.property || firstColumn.name,
|
|
803
|
+
defaultContent: '',
|
|
804
|
+
title: '',
|
|
805
|
+
sortable: false,
|
|
806
|
+
visible: !this.customSelect,
|
|
807
|
+
name: firstColumn.name,
|
|
808
|
+
};
|
|
809
|
+
// assign renderer
|
|
810
|
+
Object.assign(selectColumnDefinition, {
|
|
811
|
+
render: this._renderSelect.bind(this)
|
|
812
|
+
});
|
|
813
|
+
// and finally add column
|
|
814
|
+
tableColumns.unshift(selectColumnDefinition);
|
|
815
|
+
// user interface additions
|
|
816
|
+
// handle page change event (a trick for firing draw.dt events)
|
|
817
|
+
tableElement
|
|
818
|
+
// for each page
|
|
819
|
+
.on('page.dt', ($event) => {
|
|
820
|
+
const info = this.dataTable.page.info();
|
|
821
|
+
})
|
|
822
|
+
// handle draw events
|
|
823
|
+
.on('draw.dt', () => {
|
|
824
|
+
if (this.customSelect) {
|
|
825
|
+
// enable row selection event
|
|
826
|
+
tableElement.find('tbody').on('click', 'tr', function () {
|
|
827
|
+
// select row
|
|
828
|
+
const row = self.dataTable.row(this);
|
|
829
|
+
// add row
|
|
830
|
+
self.select.emit(row);
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
tableElement.find('input.s-row').on('click', this.onSelectionChange.bind(this));
|
|
834
|
+
// make selection on current page
|
|
835
|
+
const key = this.config.columns[0].property;
|
|
836
|
+
if (this.selected.length > 0) {
|
|
837
|
+
// enumerate selected items
|
|
838
|
+
this.selected.forEach(item => {
|
|
839
|
+
// find row identifier
|
|
840
|
+
const id = item[key];
|
|
841
|
+
// if identifier is defined
|
|
842
|
+
if (id != null) {
|
|
843
|
+
// try to find check element with this identifier in attribute data-id
|
|
844
|
+
tableElement.find(`input[data-id='${id}']`).prop('checked', true);
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
if (this.smartSelect) {
|
|
849
|
+
// select rows that are not selected
|
|
850
|
+
// and do not included in unselected collection
|
|
851
|
+
tableElement.find('input[data-id]').each((index, element) => {
|
|
852
|
+
// get data id
|
|
853
|
+
if (this.unselected.findIndex((x) => {
|
|
854
|
+
// tslint:disable-next-line:triple-equals
|
|
855
|
+
return x[key] == jQuery(element).attr('data-id');
|
|
856
|
+
}) < 0) {
|
|
857
|
+
this.selectRow(this.dataTable.row(index));
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
else if (this.selected.length === 0) {
|
|
862
|
+
tableElement.find('input[data-id]').prop('checked', false);
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
// initialize data table
|
|
867
|
+
const settings = {
|
|
868
|
+
// set length menu
|
|
869
|
+
lengthMenu: [5, 10, 50, 100, 200, 500],
|
|
870
|
+
// hide length menu
|
|
871
|
+
lengthChange: false,
|
|
872
|
+
// enable search but hide search box (css)
|
|
873
|
+
searching: true,
|
|
874
|
+
// enable data processing
|
|
875
|
+
processing: true,
|
|
876
|
+
// enable getting server side data
|
|
877
|
+
serverSide: this.serverSide,
|
|
878
|
+
// enumerate table columns
|
|
879
|
+
columns: tableColumns,
|
|
880
|
+
order: [],
|
|
881
|
+
// set scroll x
|
|
882
|
+
scrollX: this.scrollX,
|
|
883
|
+
dom: 'Bfrtip',
|
|
884
|
+
autoWidth: false,
|
|
885
|
+
buttons: [
|
|
886
|
+
{
|
|
887
|
+
extend: 'excel',
|
|
888
|
+
filename: this.config.title,
|
|
889
|
+
className: 'd-none',
|
|
890
|
+
exportOptions: {
|
|
891
|
+
columns: 'th:not(:first-child)'
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
],
|
|
895
|
+
// define server data callback
|
|
896
|
+
fnServerData: function (sSource, aoData, fnCallback, oSettings) {
|
|
897
|
+
// if method is invoked for the first time
|
|
898
|
+
if (self._firstLoad) {
|
|
899
|
+
// disabled first load flag
|
|
900
|
+
self._firstLoad = false;
|
|
901
|
+
// if auto load is disabled
|
|
902
|
+
if (self.autoLoad === false) {
|
|
903
|
+
const emptyDataSet = {
|
|
904
|
+
recordsTotal: 0,
|
|
905
|
+
recordsFiltered: 0,
|
|
906
|
+
data: []
|
|
907
|
+
};
|
|
908
|
+
// emit empty data
|
|
909
|
+
self.load.emit(emptyDataSet);
|
|
910
|
+
// return empty data
|
|
911
|
+
return fnCallback(emptyDataSet);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
// get activated route params
|
|
915
|
+
const queryParams = self._activatedRoute.snapshot.queryParams;
|
|
916
|
+
// get columns
|
|
917
|
+
const columns = aoData[1].value;
|
|
918
|
+
// get order expression
|
|
919
|
+
const order = aoData[2].value;
|
|
920
|
+
// get skip records param
|
|
921
|
+
const skip = aoData[3].value;
|
|
922
|
+
// get page size param
|
|
923
|
+
const top = aoData[4].value;
|
|
924
|
+
// get search value
|
|
925
|
+
const searchText = aoData[5].value;
|
|
926
|
+
// get data queryable
|
|
927
|
+
const q = self._query instanceof ClientDataQueryable ? self._query : self._context.model(self.config.model).asQueryable();
|
|
928
|
+
// apply paging params
|
|
929
|
+
if (top) {
|
|
930
|
+
q.take(top).skip(skip);
|
|
931
|
+
}
|
|
932
|
+
// apply order params
|
|
933
|
+
if (order && order.length) {
|
|
934
|
+
// get order query expression
|
|
935
|
+
const orderByStr = order.map(expr => {
|
|
936
|
+
return tableColumns[expr.column].name + ' ' + expr.dir || 'asc';
|
|
937
|
+
}).join(',');
|
|
938
|
+
// set order
|
|
939
|
+
q.setParam('$orderby', orderByStr);
|
|
940
|
+
}
|
|
941
|
+
// configure $select parameter
|
|
942
|
+
const select = self.config.columns.filter(column => {
|
|
943
|
+
if (column.virtual === true) {
|
|
944
|
+
return false;
|
|
945
|
+
}
|
|
946
|
+
return true;
|
|
947
|
+
}).map(column => {
|
|
948
|
+
if (column.property) {
|
|
949
|
+
return column.name + ' as ' + column.property;
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
return column.name;
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
q.select.apply(q, select);
|
|
956
|
+
// configure $filter
|
|
957
|
+
if (queryParams.$filter && queryParams.$filter.length) {
|
|
958
|
+
// set route filter
|
|
959
|
+
const qParams = q.getParams();
|
|
960
|
+
q.setParam('$filter', null);
|
|
961
|
+
const expressions = [queryParams.$filter];
|
|
962
|
+
if (qParams.$filter) {
|
|
963
|
+
expressions.push('(' + qParams.$filter + ')');
|
|
964
|
+
}
|
|
965
|
+
q.filter(expressions.join(' and '));
|
|
966
|
+
}
|
|
967
|
+
// check if query parameters contain filter
|
|
968
|
+
if (queryParams.$expand && queryParams.$expand.length) {
|
|
969
|
+
// set route $expand
|
|
970
|
+
q.setParam('$expand', queryParams.$expand);
|
|
971
|
+
}
|
|
972
|
+
if (!q.getParams()['$orderby'] && self.config && self.config.defaults && self.config.defaults.orderBy) {
|
|
973
|
+
q.setParam('$orderby', self.config.defaults.orderBy);
|
|
974
|
+
}
|
|
975
|
+
// append default filter
|
|
976
|
+
if (self.config && self.config.defaults && self.config.defaults.filter) {
|
|
977
|
+
return self._advancedFilterValueProvider.asyncBuildFilter(self.config.defaults.filter).then(defaultFilterString => {
|
|
978
|
+
if (defaultFilterString) {
|
|
979
|
+
const qParams = q.getParams();
|
|
980
|
+
q.setParam('$filter', null);
|
|
981
|
+
const expressions = [defaultFilterString];
|
|
982
|
+
if (qParams.$filter) {
|
|
983
|
+
expressions.push('(' + qParams.$filter + ')');
|
|
984
|
+
}
|
|
985
|
+
q.filter(expressions.join(' and '));
|
|
986
|
+
}
|
|
987
|
+
self.lastQueryParams = q.getParams();
|
|
988
|
+
q.getList().then(items => {
|
|
989
|
+
const dataSet = {
|
|
990
|
+
recordsTotal: items.total,
|
|
991
|
+
recordsFiltered: items.total,
|
|
992
|
+
data: items.value
|
|
993
|
+
};
|
|
994
|
+
if (items.total === 0) {
|
|
995
|
+
self.resetEmptyTable();
|
|
996
|
+
}
|
|
997
|
+
// emit load event
|
|
998
|
+
self.load.emit(dataSet);
|
|
999
|
+
// return data
|
|
1000
|
+
return fnCallback(dataSet);
|
|
1001
|
+
});
|
|
1002
|
+
}).catch((err) => {
|
|
1003
|
+
console.error('TABLES', err);
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
self.lastQueryParams = q.getParams();
|
|
1007
|
+
q.getList().then(items => {
|
|
1008
|
+
const dataSet = {
|
|
1009
|
+
recordsTotal: items.total,
|
|
1010
|
+
recordsFiltered: items.total,
|
|
1011
|
+
data: items.value
|
|
1012
|
+
};
|
|
1013
|
+
if (items.total === 0) {
|
|
1014
|
+
self.resetEmptyTable();
|
|
1015
|
+
}
|
|
1016
|
+
// emit load event
|
|
1017
|
+
self.load.emit(dataSet);
|
|
1018
|
+
// return data
|
|
1019
|
+
return fnCallback(dataSet);
|
|
1020
|
+
}).catch((err) => {
|
|
1021
|
+
console.error('TABLES', err);
|
|
1022
|
+
});
|
|
1023
|
+
},
|
|
1024
|
+
language: Object.assign({}, self.translator.transform('Tables.DataTable'), { emptyTable: ' ', processing: ' ' })
|
|
1025
|
+
};
|
|
1026
|
+
if (this.scrollable) {
|
|
1027
|
+
// set continuous scrolling
|
|
1028
|
+
Object.assign(settings, {
|
|
1029
|
+
scrollY: this.scrollY,
|
|
1030
|
+
scrollCollapse: false,
|
|
1031
|
+
scroller: {
|
|
1032
|
+
loadingIndicator: false,
|
|
1033
|
+
displayBuffer: 10
|
|
1034
|
+
},
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
Object.assign(settings, {
|
|
1039
|
+
paging: this.paging,
|
|
1040
|
+
pageLength: this.pageLength
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
if (this.selectable) ;
|
|
1044
|
+
// handle init.dt event and emit local event
|
|
1045
|
+
this.dataTable = tableElement.DataTable(settings);
|
|
1046
|
+
tableElement.on('init.dt', () => {
|
|
1047
|
+
if (this.dataTable == null) {
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
// restore default empty table html
|
|
1051
|
+
// (due to ng async binding)
|
|
1052
|
+
this.init.emit(this.dataTable);
|
|
1053
|
+
});
|
|
1054
|
+
// show or hide footer
|
|
1055
|
+
if (!this.showFooter) {
|
|
1056
|
+
this._element.nativeElement
|
|
1057
|
+
.querySelector('.dataTables_info').classList.add('d-none');
|
|
1058
|
+
}
|
|
1059
|
+
// show or hide pager
|
|
1060
|
+
if (this.showPaging) {
|
|
1061
|
+
this.showPaging = true;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
get lastQuery() {
|
|
1066
|
+
if (this.config == null) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
if (this.lastQueryParams == null) {
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
const q = this._context.model(this.config.model).asQueryable();
|
|
1073
|
+
Object.keys(this.lastQueryParams).forEach((key) => {
|
|
1074
|
+
q.setParam(key, this.lastQueryParams[key]);
|
|
1075
|
+
});
|
|
1076
|
+
// return query
|
|
1077
|
+
return q;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Fetches and updates a single row
|
|
1081
|
+
* @param find - Any object which is being converted to a query
|
|
1082
|
+
*/
|
|
1083
|
+
fetchOne(find) {
|
|
1084
|
+
if (find == null) {
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
if (this.dataTable == null) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (this.lastQueryParams == null) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
// get rows
|
|
1094
|
+
const rows = Array.from(this.dataTable.rows().data());
|
|
1095
|
+
if (rows && rows.length) {
|
|
1096
|
+
// find result
|
|
1097
|
+
const findKeys = Object.keys(find);
|
|
1098
|
+
if (findKeys.length === 0) {
|
|
1099
|
+
// do nothing
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
// find row index by searching each property
|
|
1103
|
+
const rowIndex = rows.findIndex((row) => {
|
|
1104
|
+
return findKeys.map((key) => {
|
|
1105
|
+
return row[key] === find[key];
|
|
1106
|
+
}).filter((res) => {
|
|
1107
|
+
return res === false;
|
|
1108
|
+
}).length === 0;
|
|
1109
|
+
});
|
|
1110
|
+
// if row has been found
|
|
1111
|
+
if (rowIndex >= 0) {
|
|
1112
|
+
const query = this._context.model(this.config.model).asQueryable();
|
|
1113
|
+
// select attributes
|
|
1114
|
+
query.setParam('$select', this.lastQueryParams.$select);
|
|
1115
|
+
// apply filter for row
|
|
1116
|
+
findKeys.forEach((key) => {
|
|
1117
|
+
const column = this.config.columns.find((col) => {
|
|
1118
|
+
return col.property === key || col.name === key;
|
|
1119
|
+
});
|
|
1120
|
+
if (column) {
|
|
1121
|
+
// get property or name
|
|
1122
|
+
const property = column.property || column.name;
|
|
1123
|
+
query.and(property).equal(find[key]);
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
query.and(key).equal(find[key]);
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
// and finally get item
|
|
1130
|
+
return query.getItem().then((item) => {
|
|
1131
|
+
// if item exists
|
|
1132
|
+
const row = this.dataTable.row(rowIndex);
|
|
1133
|
+
const checked = jQuery(row.node()).find(':checkbox').prop('checked');
|
|
1134
|
+
if (item) {
|
|
1135
|
+
// redraw row
|
|
1136
|
+
row.data(item);
|
|
1137
|
+
jQuery(row.node()).find(':checkbox').on('click', this.onSelectionChange.bind(this));
|
|
1138
|
+
// if row is already selected
|
|
1139
|
+
if (checked) {
|
|
1140
|
+
this.selectRow(row, true);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return Promise.resolve();
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return Promise.resolve();
|
|
1148
|
+
}
|
|
1149
|
+
ngOnDestroy() {
|
|
1150
|
+
if (this.reloadSubscription) {
|
|
1151
|
+
this.reloadSubscription.unsubscribe();
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Handle selection change
|
|
1156
|
+
* @param $event
|
|
1157
|
+
*/
|
|
1158
|
+
onSelectionChange($event) {
|
|
1159
|
+
const input = $event.target;
|
|
1160
|
+
// get row from id
|
|
1161
|
+
const rowIndex = parseInt(/-(\d+)$/g.exec(input.id)[1], 10);
|
|
1162
|
+
// get row
|
|
1163
|
+
const row = this.dataTable.row(rowIndex).data();
|
|
1164
|
+
// get row identifier
|
|
1165
|
+
if (row) {
|
|
1166
|
+
const key = this.config.columns[0].property;
|
|
1167
|
+
const id = row[key];
|
|
1168
|
+
let findItemIndex = this.selected.findIndex(item => {
|
|
1169
|
+
return item[key] === id;
|
|
1170
|
+
});
|
|
1171
|
+
if (input.checked) {
|
|
1172
|
+
if (findItemIndex < 0) {
|
|
1173
|
+
// add row to selected rows
|
|
1174
|
+
this.selected.push(row);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
else {
|
|
1178
|
+
if (findItemIndex >= 0) {
|
|
1179
|
+
// remove selected item
|
|
1180
|
+
this.selected.splice(findItemIndex, 1);
|
|
1181
|
+
if (this.smartSelect) {
|
|
1182
|
+
// add row to unselected rows
|
|
1183
|
+
findItemIndex = this.unselected.findIndex(item => {
|
|
1184
|
+
return item[key] === id;
|
|
1185
|
+
});
|
|
1186
|
+
if (findItemIndex < 0) {
|
|
1187
|
+
this.unselected.push(row);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
resetEmptyTable() {
|
|
1195
|
+
// this operation is important
|
|
1196
|
+
// due to ng data binding
|
|
1197
|
+
// empty table template has no translated text during ngOnInit()
|
|
1198
|
+
if (this.dataTable && this.dataTable.context[0]) {
|
|
1199
|
+
this.dataTable.context[0].oLanguage.sEmptyTable = this.emptyTable.nativeElement.innerHTML;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
clearEmptyTable() {
|
|
1203
|
+
if (this.dataTable && this.dataTable.context[0]) {
|
|
1204
|
+
this.dataTable.context[0].oLanguage.sEmptyTable = '';
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
export() {
|
|
1208
|
+
const element = document.querySelector('.dt-buttons .buttons-excel');
|
|
1209
|
+
element.click();
|
|
1210
|
+
}
|
|
1211
|
+
handleColumns(columnsToShow) {
|
|
1212
|
+
const table = this.dataTable;
|
|
1213
|
+
this.config.columns.slice(1).forEach((column, i) => {
|
|
1214
|
+
if (!column.hidden) {
|
|
1215
|
+
if (columnsToShow.includes(column.name)) {
|
|
1216
|
+
// noinspection TypeScriptValidateJSTypes
|
|
1217
|
+
table.column(column.name + ':name').visible(true);
|
|
1218
|
+
}
|
|
1219
|
+
else {
|
|
1220
|
+
// noinspection TypeScriptValidateJSTypes
|
|
1221
|
+
table.column(column.name + ':name').visible(false);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
getConfig() {
|
|
1227
|
+
return this.config;
|
|
1228
|
+
}
|
|
1229
|
+
ngOnChanges(changes) {
|
|
1230
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'configSrc')) {
|
|
1231
|
+
if (changes.configSrc != null) {
|
|
1232
|
+
// load table configuration on change
|
|
1233
|
+
if (changes.configSrc.currentValue && (changes.configSrc.currentValue != changes.configSrc.previousValue)) {
|
|
1234
|
+
this._http.get(changes.configSrc.currentValue).toPromise().then((config) => {
|
|
1235
|
+
// set configuration
|
|
1236
|
+
this.config = config;
|
|
1237
|
+
// fetch and redraw
|
|
1238
|
+
this.fetch();
|
|
1239
|
+
}).catch((err) => {
|
|
1240
|
+
console.error('TABLES', 'An error occurred while loading table configuration.');
|
|
1241
|
+
console.error(err);
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
AdvancedTableComponent.decorators = [
|
|
1249
|
+
{ type: Component, args: [{
|
|
1250
|
+
selector: 'app-advanced-table',
|
|
1251
|
+
template: `
|
|
1252
|
+
<table #table class="dataTable">
|
|
1253
|
+
</table>
|
|
1254
|
+
<div #emptyTable class="d-none">
|
|
1255
|
+
<div>
|
|
1256
|
+
<div class="mt-4">
|
|
1257
|
+
<div class="icon-circle bg-gray-300 border-gray-100"></div>
|
|
1258
|
+
</div>
|
|
1259
|
+
<div class="mt-4">
|
|
1260
|
+
<h4 class="font-3xl text-dark" [translate]="'Tables.EmptyTableTitle'"></h4>
|
|
1261
|
+
<p class="font-lg text-dark" [translate]="'Tables.EmptyTableMessage'"></p>
|
|
1262
|
+
</div>
|
|
1263
|
+
</div>
|
|
1264
|
+
</div>
|
|
1265
|
+
`,
|
|
1266
|
+
encapsulation: ViewEncapsulation.None,
|
|
1267
|
+
styles: ["table.dataTable thead .sorting{background-image:none}table.dataTable thead .sorting_asc{background-position:95% 60%;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAAsSAAALEgHS3X78AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAEtJREFUKJFjYBgywAOKMQALFjE/BgaGTCibjYGBYRM+kz0ZGBh2MTAw8DAwMHAxMDBsY2BgCCBGMQzg1IRNMV5NuVAJXIALqmYwAgBBkAlPRCLcLwAAAABJRU5ErkJggg==)}table.dataTable thead .sorting_desc{background-position:95% 60%;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAArzAAAK8wHvFRX1AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAFNJREFUKJHVzTEKgDAQRNGHZfBg2sSTeDMLKwXPYuVJbBKQQEyrH6bZ+cPyWWaElz4kR5cOJ1b0FXnBVRYjjmIUsGGqvX6OmnImYk+JLTkzpPyBGwrtCvfBe56nAAAAAElFTkSuQmCC)}table.dataTable thead th{color:#73818e;font-size:.75rem;font-weight:600;padding-top:16px;padding-bottom:4px;border-bottom:none;padding-left:8px}table.dataTable tbody tr:first-child td:first-child{border-radius:6px 0}table.dataTable tbody tr:first-child td:last-child{border-radius:0 6px}table.dataTable tbody tr{border-radius:6px}table.dataTable tbody tr td:first-child{border-left:1px solid #c2cfd6}table.dataTable tbody tr td:last-child{border-right:1px solid #c2cfd6}table.dataTable tbody tr td{padding-top:16px;padding-bottom:16px;border-top:1px solid #c2cfd6}table.dataTable tbody tr:last-child td:first-child{border-radius:0 0 0 6px}table.dataTable tbody tr:last-child td:last-child{border-radius:0 0 6px}table.dataTable tbody tr:last-child td{border-bottom:1px solid #c2cfd6}table.dataTable td.dataTables_empty{border:none!important}table.dataTable.no-footer{border-bottom:none}.dataTables_paginate .btn.previous{margin-right:8px}.dataTables_paginate .btn.next{margin-left:8px}.dataTables_info,.dataTables_paginate{margin-top:1rem}.dataTables_paginate span .btn{border-radius:1.5rem}.dataTables_filter{display:none}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:0;overflow:auto}.dataTables_paginate{display:none}.dataTables_wrapper .dataTables_processing{background:0 0;top:-40%;color:#73818e}.text-decoration-line-through{text-decoration:line-through!important}tr.removed .checkbox+label{visibility:hidden}"]
|
|
1268
|
+
}] }
|
|
1269
|
+
];
|
|
1270
|
+
/** @nocollapse */
|
|
1271
|
+
AdvancedTableComponent.ctorParameters = () => [
|
|
1272
|
+
{ type: AngularDataContext },
|
|
1273
|
+
{ type: undefined, decorators: [{ type: Inject, args: [COLUMN_FORMATTERS,] }] },
|
|
1274
|
+
{ type: ActivatedRoute },
|
|
1275
|
+
{ type: TranslateService },
|
|
1276
|
+
{ type: Injector },
|
|
1277
|
+
{ type: AdvancedFilterValueProvider },
|
|
1278
|
+
{ type: ElementRef },
|
|
1279
|
+
{ type: TemplatePipe },
|
|
1280
|
+
{ type: LoadingService },
|
|
1281
|
+
{ type: HttpClient }
|
|
1282
|
+
];
|
|
1283
|
+
AdvancedTableComponent.propDecorators = {
|
|
1284
|
+
table: [{ type: ViewChild, args: ['table',] }],
|
|
1285
|
+
emptyTable: [{ type: ViewChild, args: ['emptyTable',] }],
|
|
1286
|
+
loadingTable: [{ type: ViewChild, args: ['loadingTable',] }],
|
|
1287
|
+
height: [{ type: Input }],
|
|
1288
|
+
showActions: [{ type: Input }],
|
|
1289
|
+
autoLoad: [{ type: Input }],
|
|
1290
|
+
load: [{ type: Output, args: ['load',] }],
|
|
1291
|
+
init: [{ type: Output, args: ['init',] }],
|
|
1292
|
+
reload: [{ type: Input, args: ['reload',] }],
|
|
1293
|
+
select: [{ type: Output, args: ['select',] }],
|
|
1294
|
+
selectedItems: [{ type: Output, args: ['selectedItems',] }],
|
|
1295
|
+
scrollable: [{ type: Input }],
|
|
1296
|
+
serverSide: [{ type: Input }],
|
|
1297
|
+
lengthMenu: [{ type: Input }],
|
|
1298
|
+
paging: [{ type: Input }],
|
|
1299
|
+
pageLength: [{ type: Input }],
|
|
1300
|
+
scrollY: [{ type: Input }],
|
|
1301
|
+
scrollX: [{ type: Input }],
|
|
1302
|
+
selectable: [{ type: Input }],
|
|
1303
|
+
multipleSelect: [{ type: Input }],
|
|
1304
|
+
customSelect: [{ type: Input }],
|
|
1305
|
+
selected: [{ type: Input }],
|
|
1306
|
+
configSrc: [{ type: Input }],
|
|
1307
|
+
config: [{ type: Input, args: ['config',] }],
|
|
1308
|
+
showFooter: [{ type: Input, args: ['showFooter',] }],
|
|
1309
|
+
showPaging: [{ type: Input, args: ['showPaging',] }],
|
|
1310
|
+
query: [{ type: Input, args: ['query',] }]
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
class AdvancedListComponent {
|
|
1314
|
+
constructor(_context, _translateService, _activatedRoute, _errorService, _toastService, _modalService, _userActivityService) {
|
|
1315
|
+
this._context = _context;
|
|
1316
|
+
this._translateService = _translateService;
|
|
1317
|
+
this._activatedRoute = _activatedRoute;
|
|
1318
|
+
this._errorService = _errorService;
|
|
1319
|
+
this._toastService = _toastService;
|
|
1320
|
+
this._modalService = _modalService;
|
|
1321
|
+
this._userActivityService = _userActivityService;
|
|
1322
|
+
this.allowSaveOption = true;
|
|
1323
|
+
}
|
|
1324
|
+
ngOnDestroy() {
|
|
1325
|
+
if (this.dataSubscription) {
|
|
1326
|
+
this.dataSubscription.unsubscribe();
|
|
1327
|
+
}
|
|
1328
|
+
if (this.reloadSubscription) {
|
|
1329
|
+
this.reloadSubscription.unsubscribe();
|
|
1330
|
+
}
|
|
1331
|
+
if (this.subscription) {
|
|
1332
|
+
this.subscription.unsubscribe();
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
ngOnInit() {
|
|
1336
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1337
|
+
try {
|
|
1338
|
+
// get metadata
|
|
1339
|
+
const metadata = yield this._context.getMetadata();
|
|
1340
|
+
// get route data
|
|
1341
|
+
this.dataSubscription = this._activatedRoute.data.subscribe(routeData => {
|
|
1342
|
+
// get entityType
|
|
1343
|
+
const re = new RegExp(`^${routeData.model}$`, 'ig');
|
|
1344
|
+
const entitySet = metadata.EntityContainer.EntitySet.find(x => {
|
|
1345
|
+
return re.test(x.Name);
|
|
1346
|
+
});
|
|
1347
|
+
// if entity type is null
|
|
1348
|
+
if (entitySet == null) {
|
|
1349
|
+
// navigate to error
|
|
1350
|
+
return this._errorService.navigateToError(new ResponseError('EntitySet Not Found', 404));
|
|
1351
|
+
}
|
|
1352
|
+
// get entity type
|
|
1353
|
+
const entityType = metadata.EntityType.find(x => {
|
|
1354
|
+
return x.Name === entitySet.EntityType;
|
|
1355
|
+
});
|
|
1356
|
+
if (entityType == null) {
|
|
1357
|
+
return this._errorService.navigateToError(new ResponseError('EntityType Not Found', 404));
|
|
1358
|
+
}
|
|
1359
|
+
if (routeData.searchConfiguration) {
|
|
1360
|
+
if (Object.prototype.hasOwnProperty.call(routeData.searchConfiguration, 'allowSave')) {
|
|
1361
|
+
this.allowSaveOption = routeData.searchConfiguration.allowSave;
|
|
1362
|
+
}
|
|
1363
|
+
this.searchConfiguration = routeData.searchConfiguration;
|
|
1364
|
+
}
|
|
1365
|
+
if (routeData.tableConfiguration) {
|
|
1366
|
+
this.tableConfiguration = routeData.tableConfiguration;
|
|
1367
|
+
}
|
|
1368
|
+
if (routeData.description) {
|
|
1369
|
+
this.description = this._translateService.instant(routeData.description);
|
|
1370
|
+
}
|
|
1371
|
+
if (routeData.longDescription) {
|
|
1372
|
+
this.longDescription = this._translateService.instant(routeData.longDescription);
|
|
1373
|
+
}
|
|
1374
|
+
if (routeData.category) {
|
|
1375
|
+
this.category = this._translateService.instant(routeData.category);
|
|
1376
|
+
}
|
|
1377
|
+
// clone default configuration
|
|
1378
|
+
const defaultConfiguration = AdvancedTableConfiguration.cast(this.tableConfiguration, true);
|
|
1379
|
+
// correct edit button link
|
|
1380
|
+
const column = defaultConfiguration.columns.find(col => {
|
|
1381
|
+
return col.formatter === 'ButtonFormatter' && col.formatString === '(modal:edit)';
|
|
1382
|
+
});
|
|
1383
|
+
if (column) {
|
|
1384
|
+
const url = [];
|
|
1385
|
+
this._activatedRoute.snapshot.pathFromRoot.forEach(route => {
|
|
1386
|
+
url.push.apply(url, route.url);
|
|
1387
|
+
});
|
|
1388
|
+
column.formatString = `#/${url.join('/')}/(modal:\$\{id\}/edit)`;
|
|
1389
|
+
}
|
|
1390
|
+
// create list configuration by assigning list entity set
|
|
1391
|
+
this.table.config = Object.assign({
|
|
1392
|
+
model: entitySet.Name
|
|
1393
|
+
}, defaultConfiguration);
|
|
1394
|
+
// fetch data
|
|
1395
|
+
this.table.fetch(true);
|
|
1396
|
+
// do reload by using hidden fragment e.g. /classes#reload
|
|
1397
|
+
this.reloadSubscription = this._activatedRoute.fragment.subscribe(fragment => {
|
|
1398
|
+
if (fragment && fragment === 'reload') {
|
|
1399
|
+
this._toastService.show(this.description, this._translateService.instant('Settings.OperationCompleted'));
|
|
1400
|
+
this.table.fetch(true);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
// finally add user activity
|
|
1404
|
+
this._userActivityService.setItem({
|
|
1405
|
+
category: this.category,
|
|
1406
|
+
description: this.description,
|
|
1407
|
+
url: window.location.hash.substring(1),
|
|
1408
|
+
dateCreated: new Date
|
|
1409
|
+
});
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
catch (err) {
|
|
1413
|
+
console.error(err);
|
|
1414
|
+
return this._errorService.showError(err);
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Data load event handler of advanced table component
|
|
1420
|
+
* @param {AdvancedTableDataResult} data
|
|
1421
|
+
*/
|
|
1422
|
+
onDataLoad(data) {
|
|
1423
|
+
this.recordsTotal = data.recordsTotal;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Remove selected items
|
|
1427
|
+
*/
|
|
1428
|
+
remove() {
|
|
1429
|
+
if (this.table && this.table.selected && this.table.selected.length) {
|
|
1430
|
+
// get items to remove
|
|
1431
|
+
const items = this.table.selected;
|
|
1432
|
+
return this._modalService.showWarningDialog(this._translateService.instant('Tables.RemoveItemsTitle'), this._translateService.instant('Tables.RemoveItemsMessage'), DIALOG_BUTTONS.OkCancel).then(result => {
|
|
1433
|
+
if (result === 'ok') {
|
|
1434
|
+
this._context.model(this.table.config.model).remove(items).then(() => {
|
|
1435
|
+
this._toastService.show(this.description + ':' + this._translateService.instant('Tables.RemoveItemMessage.title'), this._translateService.instant((items.length === 1 ?
|
|
1436
|
+
'Tables.RemoveItemMessage.one' : 'Tables.RemoveItemMessage.many'), { value: items.length }));
|
|
1437
|
+
this.table.fetch(true);
|
|
1438
|
+
}).catch(err => {
|
|
1439
|
+
this._errorService.showError(err, {
|
|
1440
|
+
continueLink: '.'
|
|
1441
|
+
});
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
AdvancedListComponent.decorators = [
|
|
1449
|
+
{ type: Component, args: [{
|
|
1450
|
+
selector: 'app-advanced-list',
|
|
1451
|
+
template: "<div>\n <div class=\"row\" *ngIf=\"description\">\n <div class=\"col-auto\">\n <h2>{{description}}</h2>\n <p *ngIf=\"longDescription\">{{longDescription}}</p>\n </div>\n </div>\n <div class=\"row\" *ngIf=\"searchConfiguration\">\n <div class=\"col-12\" [ngVar]=\"{}\" #filter=\"ngVar\">\n <app-advanced-table-search #advancedSearch [(filter)]=\"filter.value\" [collapsed]=\"true\" [table]=\"table\" [allowSave]=\"allowSaveOption\">\n <advanced-search>\n <app-advanced-search-form [form]=\"searchConfiguration\" (filterChange)=\"advancedSearch.asyncAdvancedSearch()\" #search [(filter)]=\"filter.value\"></app-advanced-search-form>\n </advanced-search>\n </app-advanced-table-search>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-auto mt-4\">\n <div class=\"font-xl text-secondary\" *ngIf=\"recordsTotal > 1\">\n <span>{{recordsTotal}}</span><span> </span><span [translate]=\"'Tables.Items'\"></span>\n </div>\n <div class=\"font-xl text-secondary\" *ngIf=\"recordsTotal === 1\">\n <span>{{recordsTotal}}</span><span> </span><span [translate]=\"'Tables.Item'\"></span>\n </div>\n </div>\n <div class=\"pull-right ml-auto mr-3 mt-3\">\n <div class=\"font-lg btn-toolbar mt-3\">\n <a *ngIf=\"table && table.selected && table.selected.length\"\n class=\"text-danger text-decoration-none mr-2\" href=\"javascript:void(0)\" (click)=\"remove()\">\n <span>-</span><span> </span><span [translate]=\"'Tables.RemoveItem'\"></span>\n </a>\n <a class=\"text-indigo text-decoration-none\" [routerLink]=\"['.', { outlets:{ modal:'add'} }]\">\n <span>+</span><span> </span><span [translate]=\"'Tables.AddItem'\"></span>\n </a>\n </div>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-12 mb-4\">\n <app-advanced-table [selectable]=\"true\" #table\n [autoLoad]=false\n [showFooter]=\"false\"\n (load)=\"onDataLoad($event)\" [height]=\"600\"></app-advanced-table>\n </div>\n </div>\n <router-modal></router-modal>\n</div>\n"
|
|
1452
|
+
}] }
|
|
1453
|
+
];
|
|
1454
|
+
/** @nocollapse */
|
|
1455
|
+
AdvancedListComponent.ctorParameters = () => [
|
|
1456
|
+
{ type: AngularDataContext },
|
|
1457
|
+
{ type: TranslateService },
|
|
1458
|
+
{ type: ActivatedRoute },
|
|
1459
|
+
{ type: ErrorService },
|
|
1460
|
+
{ type: ToastService },
|
|
1461
|
+
{ type: ModalService },
|
|
1462
|
+
{ type: UserActivityService }
|
|
1463
|
+
];
|
|
1464
|
+
AdvancedListComponent.propDecorators = {
|
|
1465
|
+
entitySet: [{ type: Input, args: ['entitySet',] }],
|
|
1466
|
+
category: [{ type: Input, args: ['category',] }],
|
|
1467
|
+
description: [{ type: Input, args: ['description',] }],
|
|
1468
|
+
longDescription: [{ type: Input, args: ['longDescription',] }],
|
|
1469
|
+
tableConfiguration: [{ type: Input, args: ['tableConfiguration',] }],
|
|
1470
|
+
searchConfiguration: [{ type: Input, args: ['searchConfiguration',] }],
|
|
1471
|
+
table: [{ type: ViewChild, args: ['table',] }],
|
|
1472
|
+
allowSaveOption: [{ type: Input, args: ['allowSaveOption',] }]
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
class AdvancedRowActionComponent extends RouterModalOkCancel {
|
|
1476
|
+
constructor(router, activatedRoute, _translateService, _context, _errorService, _modalService, _appEvent) {
|
|
1477
|
+
super(router, activatedRoute);
|
|
1478
|
+
this._translateService = _translateService;
|
|
1479
|
+
this._context = _context;
|
|
1480
|
+
this._errorService = _errorService;
|
|
1481
|
+
this._modalService = _modalService;
|
|
1482
|
+
this._appEvent = _appEvent;
|
|
1483
|
+
this.modalIcon = 'fas fa-project-diagram';
|
|
1484
|
+
this.progressType = 'indigo';
|
|
1485
|
+
this.errorMessage = 'Tables.Actions.CompletedWithErrors';
|
|
1486
|
+
this.loading = false;
|
|
1487
|
+
this.refresh = new EventEmitter();
|
|
1488
|
+
// set modal size
|
|
1489
|
+
this.modalClass = 'modal-lg';
|
|
1490
|
+
// set modal title
|
|
1491
|
+
this.modalTitle = 'Tables.Actions.Label';
|
|
1492
|
+
// disable ok button
|
|
1493
|
+
this.okButtonDisabled = false;
|
|
1494
|
+
// set button text
|
|
1495
|
+
this.okButtonText = 'Tables.Actions.Start';
|
|
1496
|
+
}
|
|
1497
|
+
get form() {
|
|
1498
|
+
return this.formComponent;
|
|
1499
|
+
}
|
|
1500
|
+
ngOnInit() {
|
|
1501
|
+
this.refreshSubscription = this.refresh.subscribe((value) => {
|
|
1502
|
+
if (value && value.progress) {
|
|
1503
|
+
let progress = 0;
|
|
1504
|
+
if (value.progress < 0) {
|
|
1505
|
+
progress = 0;
|
|
1506
|
+
}
|
|
1507
|
+
else if (value.progress > 100) {
|
|
1508
|
+
progress = 100;
|
|
1509
|
+
}
|
|
1510
|
+
else {
|
|
1511
|
+
progress = value.progress;
|
|
1512
|
+
}
|
|
1513
|
+
this.progressComponent.value = progress;
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
ngAfterViewInit() {
|
|
1518
|
+
if (this.formComponent && this.formComponent.form) {
|
|
1519
|
+
this.okButtonDisabled = true;
|
|
1520
|
+
this.invalidSubscription = this.formComponent.form.change.subscribe((event) => {
|
|
1521
|
+
if (Object.prototype.hasOwnProperty.call(event, "isValid") && (event.changed != null)) {
|
|
1522
|
+
this.okButtonDisabled = !event.isValid;
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
ngOnDestroy() {
|
|
1528
|
+
if (this.refreshSubscription) {
|
|
1529
|
+
this.refreshSubscription.unsubscribe();
|
|
1530
|
+
}
|
|
1531
|
+
if (this.executeSubscription) {
|
|
1532
|
+
this.executeSubscription.unsubscribe();
|
|
1533
|
+
}
|
|
1534
|
+
if (this.invalidSubscription) {
|
|
1535
|
+
this.invalidSubscription.unsubscribe();
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
cancel() {
|
|
1539
|
+
if (this.loading) {
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
// close
|
|
1543
|
+
if (this._modalService.modalRef) {
|
|
1544
|
+
return this._modalService.modalRef.hide();
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
ok() {
|
|
1548
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1549
|
+
try {
|
|
1550
|
+
this.loading = true;
|
|
1551
|
+
this.lastError = null;
|
|
1552
|
+
if (this.formComponent && this.formComponent.form &&
|
|
1553
|
+
this.formComponent.form.formio && this.formComponent.form.formio.data) {
|
|
1554
|
+
// set data
|
|
1555
|
+
this.data = this.formComponent.form.formio.data;
|
|
1556
|
+
}
|
|
1557
|
+
// do action
|
|
1558
|
+
this.execute.subscribe((result) => {
|
|
1559
|
+
this.loading = false;
|
|
1560
|
+
this.refresh.emit({
|
|
1561
|
+
progress: 100
|
|
1562
|
+
});
|
|
1563
|
+
if (result && result.errors && result.errors > 0) {
|
|
1564
|
+
// show message for partial success
|
|
1565
|
+
if (result.errors === 1) {
|
|
1566
|
+
this.lastError = new Error(this._translateService.instant(`${this.errorMessage}.Description.One`));
|
|
1567
|
+
}
|
|
1568
|
+
else {
|
|
1569
|
+
this.lastError = new Error(this._translateService.instant(`${this.errorMessage}.Description.Many`, result));
|
|
1570
|
+
}
|
|
1571
|
+
this.refresh.emit({
|
|
1572
|
+
progress: 1
|
|
1573
|
+
});
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
if (this._modalService.modalRef) {
|
|
1577
|
+
this._modalService.modalRef.hide();
|
|
1578
|
+
}
|
|
1579
|
+
}, (err) => {
|
|
1580
|
+
this.loading = false;
|
|
1581
|
+
this.lastError = err;
|
|
1582
|
+
this.refresh.emit({
|
|
1583
|
+
progress: 0
|
|
1584
|
+
});
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
catch (err) {
|
|
1588
|
+
this.loading = false;
|
|
1589
|
+
this.lastError = err;
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
AdvancedRowActionComponent.decorators = [
|
|
1595
|
+
{ type: Component, args: [{
|
|
1596
|
+
selector: 'app-advanced-row-action',
|
|
1597
|
+
template: "<div>\n <div class=\"modal-header\" *ngIf=\"modalTitle\">\n <h5 class=\"modal-title\">{{modalTitle | translate}}</h5>\n <button (click)=\"cancel()\" [disabled]=\"loading\"\n type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Cancel\">\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n <div class=\"modal-body pt-0 mt-2 pb-0\">\n <div class=\"row\" *ngIf=\"lastError\">\n <div class=\"col-12\">\n <div class=\"alert alert-warning\" role=\"alert\">\n <span>{{ 'Tables.Actions.CompletedWithErrors.Title' | translate }}</span><br>\n <small *ngIf=\"lastError.status >= 0\" [translate]=\"'E' + lastError.status + '.message'\"></small>\n <small *ngIf=\"lastError.status == null\">{{lastError.message}}</small>\n </div>\n </div>\n </div>\n <div class=\"row\" *ngIf=\"description\">\n <div class=\"col-12 mb-3\">\n <div class=\"text-justify\">\n {{description | translate}}\n </div>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-12\">\n <div *ngIf=\"items && items.length > 1\" [translate]=\"'Tables.Actions.ActionMessage.Many'\" [translateParams]=\"items\"></div>\n <div *ngIf=\"items && items.length === 1\" [translate]=\"'Tables.Actions.ActionMessage.One'\"></div>\n <div class=\"font-weight-bold text-danger\" *ngIf=\"items && items.length <= 0\" [translate]=\"'Tables.Actions.ActionMessage.None'\"></div>\n <div class=\"pt-2 font-weight-bold text-danger\" *ngIf=\"additionalMessage && items && items.length <= 0\">{{additionalMessage}}</div>\n </div>\n </div>\n <div class=\"row pt-3\" *ngIf=\"formTemplate\">\n <div class=\"col-12 mb-3\">\n <div>\n <advanced-form-edit #formComponent [formName]=\"formTemplate\" [(data)]=\"data\"></advanced-form-edit>\n </div>\n </div>\n </div>\n\n <div class=\"row mt-3\">\n <div class=\"col-12\">\n <progressbar #progress [type]=\"progressType\" [max]=\"100\">\n </progressbar>\n </div>\n </div>\n </div>\n\n <div class=\"modal-footer justify-content-end\">\n <button [disabled]=\"okButtonDisabled || loading || (items && items.length === 0)\" (click)=\"ok()\"\n type=\"button\" [ngClass]=\"okButtonClass\">\n {{okButtonText | translate}}\n </button>\n <button [disabled]=\"cancelButtonDisabled || loading\"\n (click)=\"cancel()\" type=\"button\" [ngClass]=\"cancelButtonClass\" data-dismiss=\"modal\">\n {{cancelButtonText | translate}}\n </button>\n </div>\n</div>\n",
|
|
1598
|
+
encapsulation: ViewEncapsulation.None,
|
|
1599
|
+
styles: [`
|
|
1600
|
+
.fa-1_5x {
|
|
1601
|
+
font-size: 1.5rem;
|
|
1602
|
+
}
|
|
1603
|
+
.form-control:disabled, .form-control[readonly] {
|
|
1604
|
+
background-color: inherit;
|
|
1605
|
+
}
|
|
1606
|
+
.form-check.checkbox {
|
|
1607
|
+
position: relative
|
|
1608
|
+
}
|
|
1609
|
+
`]
|
|
1610
|
+
}] }
|
|
1611
|
+
];
|
|
1612
|
+
/** @nocollapse */
|
|
1613
|
+
AdvancedRowActionComponent.ctorParameters = () => [
|
|
1614
|
+
{ type: Router },
|
|
1615
|
+
{ type: ActivatedRoute },
|
|
1616
|
+
{ type: TranslateService },
|
|
1617
|
+
{ type: AngularDataContext },
|
|
1618
|
+
{ type: ErrorService },
|
|
1619
|
+
{ type: ModalService },
|
|
1620
|
+
{ type: AppEventService }
|
|
1621
|
+
];
|
|
1622
|
+
AdvancedRowActionComponent.propDecorators = {
|
|
1623
|
+
items: [{ type: Input }],
|
|
1624
|
+
modalIcon: [{ type: Input }],
|
|
1625
|
+
description: [{ type: Input }],
|
|
1626
|
+
formTemplate: [{ type: Input }],
|
|
1627
|
+
progressType: [{ type: Input }],
|
|
1628
|
+
data: [{ type: Output }],
|
|
1629
|
+
additionalMessage: [{ type: Input }],
|
|
1630
|
+
errorMessage: [{ type: Input }],
|
|
1631
|
+
progressComponent: [{ type: ViewChild, args: ['progress',] }],
|
|
1632
|
+
formComponent: [{ type: ViewChild, args: ['formComponent',] }],
|
|
1633
|
+
refresh: [{ type: Input }],
|
|
1634
|
+
execute: [{ type: Input }]
|
|
1635
|
+
};
|
|
1636
|
+
|
|
1637
|
+
class AdvancedTableSearchBaseComponent {
|
|
1638
|
+
constructor() {
|
|
1639
|
+
this.filterChange = new EventEmitter();
|
|
1640
|
+
}
|
|
1641
|
+
get filter() {
|
|
1642
|
+
return this._filter;
|
|
1643
|
+
}
|
|
1644
|
+
set filter(value) {
|
|
1645
|
+
this._filter = value;
|
|
1646
|
+
}
|
|
1647
|
+
onKeyEnter(event) {
|
|
1648
|
+
if (event.code === 'Enter') {
|
|
1649
|
+
this.filterChange.emit(this._filter);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
AdvancedTableSearchBaseComponent.propDecorators = {
|
|
1654
|
+
filterChange: [{ type: Output }],
|
|
1655
|
+
filter: [{ type: Input }]
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
class AdvancedSearchFormComponent extends AdvancedTableSearchBaseComponent {
|
|
1659
|
+
constructor(_context, _translateService, _activatedRoute, _http) {
|
|
1660
|
+
super();
|
|
1661
|
+
this._context = _context;
|
|
1662
|
+
this._translateService = _translateService;
|
|
1663
|
+
this._activatedRoute = _activatedRoute;
|
|
1664
|
+
this._http = _http;
|
|
1665
|
+
this.form = {};
|
|
1666
|
+
this.refreshForm = new EventEmitter();
|
|
1667
|
+
this.submission = { data: {} };
|
|
1668
|
+
this.loading = true;
|
|
1669
|
+
}
|
|
1670
|
+
ngOnChanges(changes) {
|
|
1671
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'form')) {
|
|
1672
|
+
if (changes.form != null) {
|
|
1673
|
+
if (changes.form.currentValue &&
|
|
1674
|
+
changes.form.currentValue != changes.form.previousValue) {
|
|
1675
|
+
this.ngOnInit();
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'formSrc')) {
|
|
1680
|
+
if (changes.formSrc != null) {
|
|
1681
|
+
if (changes.formSrc.currentValue &&
|
|
1682
|
+
changes.formSrc.currentValue != changes.formSrc.previousValue) {
|
|
1683
|
+
this._http.get(changes.formSrc.currentValue).toPromise().then((form) => {
|
|
1684
|
+
// set form
|
|
1685
|
+
this.form = form;
|
|
1686
|
+
// and init
|
|
1687
|
+
this.ngOnInit();
|
|
1688
|
+
}).catch((err) => {
|
|
1689
|
+
console.error('An error occurred while loading search configuration');
|
|
1690
|
+
console.error(err);
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'filter')) {
|
|
1696
|
+
if (changes.filter != null) {
|
|
1697
|
+
this.submission.data = changes.filter.currentValue || {};
|
|
1698
|
+
this.refreshForm.emit({
|
|
1699
|
+
submission: this.submission
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
ngOnInit() {
|
|
1705
|
+
if (this.form) {
|
|
1706
|
+
new ServiceUrlPreProcessor(this._context).parse(this.form);
|
|
1707
|
+
new TranslatePreProcessor(this._translateService).parse(this.form);
|
|
1708
|
+
this.submission = {
|
|
1709
|
+
data: this.filter
|
|
1710
|
+
};
|
|
1711
|
+
this.refreshForm.emit({
|
|
1712
|
+
form: this.form
|
|
1713
|
+
});
|
|
1714
|
+
this.loading = false;
|
|
1715
|
+
this.filterSubscription = this.filterChange.subscribe(filter => {
|
|
1716
|
+
this.submission.data = filter;
|
|
1717
|
+
this.refreshForm.emit({
|
|
1718
|
+
submission: this.submission
|
|
1719
|
+
});
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
onChange(event) {
|
|
1724
|
+
Object.keys(this.formComponent.submission.data).forEach(key => {
|
|
1725
|
+
if (Object.prototype.hasOwnProperty.call(this.formComponent.submission.data, key)) {
|
|
1726
|
+
const value = this.formComponent.submission.data[key];
|
|
1727
|
+
// the filter with type is a string and is not empty
|
|
1728
|
+
const isEmptyString = typeof value === 'string' && value.length === 0;
|
|
1729
|
+
if (!isEmptyString) {
|
|
1730
|
+
Object.defineProperty(this.filter, key, {
|
|
1731
|
+
configurable: true,
|
|
1732
|
+
enumerable: true,
|
|
1733
|
+
value: value
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
else {
|
|
1737
|
+
delete this.filter[key];
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
onFormLoad(form) {
|
|
1743
|
+
const currentLang = this._translateService.currentLang;
|
|
1744
|
+
this.formComponent.formio.i18next.options.resources[currentLang] = { translation: this._translateService.instant(`Forms`) };
|
|
1745
|
+
this.formComponent.formio.language = currentLang;
|
|
1746
|
+
}
|
|
1747
|
+
ngAfterViewInit() {
|
|
1748
|
+
//
|
|
1749
|
+
}
|
|
1750
|
+
ngOnDestroy() {
|
|
1751
|
+
if (this.filterSubscription) {
|
|
1752
|
+
this.filterSubscription.unsubscribe();
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
AdvancedSearchFormComponent.decorators = [
|
|
1757
|
+
{ type: Component, args: [{
|
|
1758
|
+
selector: 'app-advanced-search-form',
|
|
1759
|
+
template: `
|
|
1760
|
+
<div class="p-4">
|
|
1761
|
+
<formio #formioComponent class="formio-search" (keydown)="onKeyEnter($event)" (change)="onChange($event)" (formLoad)="onFormLoad($event)"
|
|
1762
|
+
[refresh]="refreshForm" [submission]="submission" [form]="form"></formio>
|
|
1763
|
+
</div>
|
|
1764
|
+
`,
|
|
1765
|
+
encapsulation: ViewEncapsulation.None,
|
|
1766
|
+
styles: ["formio.formio-search .form-group{margin-bottom:0}formio.formio-search .form-group .col-form-label{font-size:90%;padding-top:4px;padding-bottom:4px}.choices__list.choices__list--single .choices__item:first-child.choices__item--selectable{width:85%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}"]
|
|
1767
|
+
}] }
|
|
1768
|
+
];
|
|
1769
|
+
/** @nocollapse */
|
|
1770
|
+
AdvancedSearchFormComponent.ctorParameters = () => [
|
|
1771
|
+
{ type: AngularDataContext },
|
|
1772
|
+
{ type: TranslateService },
|
|
1773
|
+
{ type: ActivatedRoute },
|
|
1774
|
+
{ type: HttpClient }
|
|
1775
|
+
];
|
|
1776
|
+
AdvancedSearchFormComponent.propDecorators = {
|
|
1777
|
+
formComponent: [{ type: ViewChild, args: ['formioComponent',] }],
|
|
1778
|
+
form: [{ type: Input }],
|
|
1779
|
+
formSrc: [{ type: Input }],
|
|
1780
|
+
refreshForm: [{ type: Output }]
|
|
1781
|
+
};
|
|
1782
|
+
|
|
1783
|
+
class AdvancedTableConfigurationResolver {
|
|
1784
|
+
constructor(_http) {
|
|
1785
|
+
this._http = _http;
|
|
1786
|
+
}
|
|
1787
|
+
resolve(route, state) {
|
|
1788
|
+
// get list
|
|
1789
|
+
const list = route.data.list || route.params.list;
|
|
1790
|
+
// get model
|
|
1791
|
+
const model = route.data.model || route.params.model;
|
|
1792
|
+
// get configuration
|
|
1793
|
+
return this._http.get(`assets/lists/${model}/${list}.json`).toPromise();
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
AdvancedTableConfigurationResolver.decorators = [
|
|
1797
|
+
{ type: Injectable }
|
|
1798
|
+
];
|
|
1799
|
+
/** @nocollapse */
|
|
1800
|
+
AdvancedTableConfigurationResolver.ctorParameters = () => [
|
|
1801
|
+
{ type: HttpClient }
|
|
1802
|
+
];
|
|
1803
|
+
class AdvancedSearchConfigurationResolver {
|
|
1804
|
+
constructor(_http) {
|
|
1805
|
+
this._http = _http;
|
|
1806
|
+
}
|
|
1807
|
+
resolve(route, state) {
|
|
1808
|
+
// get list
|
|
1809
|
+
const list = route.data.list || route.params.list;
|
|
1810
|
+
// get model
|
|
1811
|
+
const model = route.data.model || route.params.model;
|
|
1812
|
+
// get configuration
|
|
1813
|
+
return this._http.get(`assets/lists/${model}/search.${list}.json`).toPromise();
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
AdvancedSearchConfigurationResolver.decorators = [
|
|
1817
|
+
{ type: Injectable }
|
|
1818
|
+
];
|
|
1819
|
+
/** @nocollapse */
|
|
1820
|
+
AdvancedSearchConfigurationResolver.ctorParameters = () => [
|
|
1821
|
+
{ type: HttpClient }
|
|
1822
|
+
];
|
|
1823
|
+
|
|
1824
|
+
class AdvancedTableSearchComponent extends AdvancedTableSearchBaseComponent {
|
|
1825
|
+
constructor(_context, _activatedRoute, datePipe, _modalService, _userStorage, _translate, _advancedSearchValueProvider, _template, _router, _modal) {
|
|
1826
|
+
super();
|
|
1827
|
+
this._context = _context;
|
|
1828
|
+
this._activatedRoute = _activatedRoute;
|
|
1829
|
+
this.datePipe = datePipe;
|
|
1830
|
+
this._modalService = _modalService;
|
|
1831
|
+
this._userStorage = _userStorage;
|
|
1832
|
+
this._translate = _translate;
|
|
1833
|
+
this._advancedSearchValueProvider = _advancedSearchValueProvider;
|
|
1834
|
+
this._template = _template;
|
|
1835
|
+
this._router = _router;
|
|
1836
|
+
this._modal = _modal;
|
|
1837
|
+
this.collapsed = true;
|
|
1838
|
+
this.allowSave = true;
|
|
1839
|
+
this.showMore = true;
|
|
1840
|
+
this.mergeQueryParams = true;
|
|
1841
|
+
this.viewChange = new EventEmitter();
|
|
1842
|
+
this.userSearchList = [];
|
|
1843
|
+
this.selectedList = null;
|
|
1844
|
+
}
|
|
1845
|
+
set text(value) {
|
|
1846
|
+
if (this.searchText) {
|
|
1847
|
+
this.searchText.nativeElement.value = value;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
get text() {
|
|
1851
|
+
if (this.searchText) {
|
|
1852
|
+
return this.searchText.nativeElement.value;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
ngOnInit() {
|
|
1856
|
+
this.configChangesSubscription = this.table.configChanges.subscribe((config) => {
|
|
1857
|
+
if (config == null) {
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
// load user searches
|
|
1861
|
+
this.loadUserSearchList().then(() => {
|
|
1862
|
+
// if search component merges query params
|
|
1863
|
+
// (uses queryParams to apply filter)
|
|
1864
|
+
if (this.mergeQueryParams) {
|
|
1865
|
+
this.queryParamSubscription = this._activatedRoute.queryParams.subscribe((queryParams) => {
|
|
1866
|
+
this.applyQueryParams(queryParams);
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Applies query given by the queryParams of the activated route
|
|
1874
|
+
* @param queryParams
|
|
1875
|
+
*/
|
|
1876
|
+
applyQueryParams(queryParams) {
|
|
1877
|
+
this.filter = {};
|
|
1878
|
+
if (Object.prototype.hasOwnProperty.call(queryParams, 'q')) {
|
|
1879
|
+
// set collapsed
|
|
1880
|
+
this.collapsed = true;
|
|
1881
|
+
// search text
|
|
1882
|
+
this.text = queryParams.q;
|
|
1883
|
+
return this.table.search(this.text);
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
this.text = '';
|
|
1887
|
+
}
|
|
1888
|
+
let filterParams = Object.assign({}, queryParams);
|
|
1889
|
+
if (Object.prototype.hasOwnProperty.call(queryParams, 's')) {
|
|
1890
|
+
// set collapsed
|
|
1891
|
+
this.collapsed = false;
|
|
1892
|
+
// find user search by name
|
|
1893
|
+
if (this.userSearchList) {
|
|
1894
|
+
this.selectedList = this.userSearchList.find((item) => {
|
|
1895
|
+
return item.name === queryParams.s;
|
|
1896
|
+
});
|
|
1897
|
+
if (this.selectedList) {
|
|
1898
|
+
filterParams = this.selectedList.filter;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
else {
|
|
1903
|
+
this.selectedList = null;
|
|
1904
|
+
}
|
|
1905
|
+
Object.keys(filterParams).filter((key) => {
|
|
1906
|
+
// search criteria
|
|
1907
|
+
if (this.table && this.table.config && this.table.config.criteria) {
|
|
1908
|
+
return this.table.config.criteria.find((item) => {
|
|
1909
|
+
return item.name === key;
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
}).forEach((key) => {
|
|
1913
|
+
let value = filterParams[key];
|
|
1914
|
+
if (TextUtils.isNumber(value)) {
|
|
1915
|
+
value = parseFloat(filterParams[key]);
|
|
1916
|
+
}
|
|
1917
|
+
if (this.filter[key] !== value) {
|
|
1918
|
+
// set value
|
|
1919
|
+
Object.defineProperty(this.filter, key, {
|
|
1920
|
+
configurable: true,
|
|
1921
|
+
enumerable: true,
|
|
1922
|
+
writable: true,
|
|
1923
|
+
value: value
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
if (this.filter[key] != null) {
|
|
1927
|
+
this.collapsed = false;
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
// call filterChange event to apply change to advanced search children
|
|
1931
|
+
this.filterChange.emit(this.filter);
|
|
1932
|
+
// apply query
|
|
1933
|
+
this.getQuery().then((query) => {
|
|
1934
|
+
// if table query is null
|
|
1935
|
+
if (this.table.query == null) {
|
|
1936
|
+
// set query
|
|
1937
|
+
this.table.query = query;
|
|
1938
|
+
}
|
|
1939
|
+
else {
|
|
1940
|
+
if (query != null) {
|
|
1941
|
+
const params = query.getParams();
|
|
1942
|
+
// otherwise set filter params
|
|
1943
|
+
this.table.query.setParam('$filter', params.$filter);
|
|
1944
|
+
}
|
|
1945
|
+
else {
|
|
1946
|
+
this.table.query.setParam('$filter', null);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
// and finally fetch data
|
|
1950
|
+
this.table.fetch(true);
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
ngOnDestroy() {
|
|
1954
|
+
if (this.queryParamSubscription) {
|
|
1955
|
+
this.queryParamSubscription.unsubscribe();
|
|
1956
|
+
}
|
|
1957
|
+
if (this.configChangesSubscription) {
|
|
1958
|
+
this.configChangesSubscription.unsubscribe();
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
ngAfterViewInit() {
|
|
1962
|
+
}
|
|
1963
|
+
onSearchKeyDown(event) {
|
|
1964
|
+
if (this.table && event.keyCode === 13) {
|
|
1965
|
+
// get search text
|
|
1966
|
+
const searchText = event.target.value;
|
|
1967
|
+
// merge query params ?
|
|
1968
|
+
if (this.mergeQueryParams) {
|
|
1969
|
+
const queryParams = {
|
|
1970
|
+
q: searchText
|
|
1971
|
+
};
|
|
1972
|
+
if (this.table.config && this.table.config.criteria) {
|
|
1973
|
+
this.table.config.criteria.forEach((item) => {
|
|
1974
|
+
Object.defineProperty(queryParams, item.name, {
|
|
1975
|
+
configurable: true,
|
|
1976
|
+
enumerable: true,
|
|
1977
|
+
writable: true,
|
|
1978
|
+
value: null
|
|
1979
|
+
});
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
// try to apply filter by appending query params
|
|
1983
|
+
return this._router.navigate([], {
|
|
1984
|
+
relativeTo: this._activatedRoute,
|
|
1985
|
+
queryParams: queryParams,
|
|
1986
|
+
queryParamsHandling: 'merge'
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
// otherwise do search
|
|
1990
|
+
this.table.search(searchText);
|
|
1991
|
+
return false;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
transformDate(date) {
|
|
1995
|
+
date = this.datePipe.transform(date, 'MM/dd/yyyy'); // whatever format you need.
|
|
1996
|
+
return date;
|
|
1997
|
+
}
|
|
1998
|
+
convertToString(nameFilter) {
|
|
1999
|
+
let name = '';
|
|
2000
|
+
if (nameFilter != null) {
|
|
2001
|
+
name = nameFilter.toString();
|
|
2002
|
+
}
|
|
2003
|
+
return name;
|
|
2004
|
+
}
|
|
2005
|
+
// Load saved criteria searches user has stored before
|
|
2006
|
+
loadUserSearchList() {
|
|
2007
|
+
this.storageKey = 'tables/' + this.table.config.model + '/searches';
|
|
2008
|
+
return this._userStorage.getItem(this.storageKey).then((data) => {
|
|
2009
|
+
const list = data.value || [];
|
|
2010
|
+
// if table configuration has pre-defined searches
|
|
2011
|
+
if (this.table && this.table.config.searches && this.table.config.searches.length) {
|
|
2012
|
+
// add pre-defined lists
|
|
2013
|
+
list.push.apply(list, this.table.config.searches.map(x => {
|
|
2014
|
+
x.readOnly = true; // as readonly
|
|
2015
|
+
x.name = this._translate.instant(x.name);
|
|
2016
|
+
return x;
|
|
2017
|
+
}));
|
|
2018
|
+
}
|
|
2019
|
+
// set user search list
|
|
2020
|
+
this.userSearchList = list;
|
|
2021
|
+
return Promise.resolve();
|
|
2022
|
+
}).catch((err) => {
|
|
2023
|
+
// DO NOT THROW ERROR
|
|
2024
|
+
// todo: notify component for this error in order to disable some actions
|
|
2025
|
+
console.error('An error occurred while getting user search list');
|
|
2026
|
+
console.error(err);
|
|
2027
|
+
return Promise.resolve();
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
onSelectUserSearch(event) {
|
|
2031
|
+
if (this.selectedList) {
|
|
2032
|
+
const listFound = this.searchInListByName(this.selectedList.name);
|
|
2033
|
+
if (listFound) {
|
|
2034
|
+
if (this.mergeQueryParams) {
|
|
2035
|
+
const queryParams = { q: null, s: this.selectedList.name };
|
|
2036
|
+
// clear criteria if any
|
|
2037
|
+
this.table.config.criteria.forEach((item) => {
|
|
2038
|
+
if (Object.prototype.hasOwnProperty.call(queryParams, item.name) === false) {
|
|
2039
|
+
// set null value
|
|
2040
|
+
Object.defineProperty(queryParams, item.name, {
|
|
2041
|
+
configurable: true,
|
|
2042
|
+
enumerable: true,
|
|
2043
|
+
writable: true,
|
|
2044
|
+
value: null
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
return this._router.navigate([], {
|
|
2049
|
+
relativeTo: this._activatedRoute,
|
|
2050
|
+
queryParams: queryParams,
|
|
2051
|
+
queryParamsHandling: 'merge'
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
this.selectedList = listFound;
|
|
2055
|
+
this.filter = this.selectedList.filter;
|
|
2056
|
+
this.filterChange.emit(this.filter);
|
|
2057
|
+
return true;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
else {
|
|
2061
|
+
return this._router.navigate([], {
|
|
2062
|
+
relativeTo: this._activatedRoute,
|
|
2063
|
+
queryParams: { s: null },
|
|
2064
|
+
queryParamsHandling: 'merge'
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
searchInListByName(name) {
|
|
2070
|
+
if (Array.isArray(this.userSearchList)) {
|
|
2071
|
+
const listFound = this.userSearchList.find(x => x.name === name);
|
|
2072
|
+
if (listFound) {
|
|
2073
|
+
return listFound;
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
return false;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
// Update list item contents with given content (Search by object name property)
|
|
2081
|
+
updateSavedSearchListItemByName(name, newItem) {
|
|
2082
|
+
const itemInList = this.userSearchList.find(x => x.name === name);
|
|
2083
|
+
const index = this.userSearchList.indexOf(itemInList);
|
|
2084
|
+
this.userSearchList[index] = newItem;
|
|
2085
|
+
this.selectedList = this.userSearchList[index];
|
|
2086
|
+
}
|
|
2087
|
+
// Update list item contents with given content (Search by object)
|
|
2088
|
+
updateSavedSearchListItemByObject(itemInList, newItem) {
|
|
2089
|
+
const index = this.userSearchList.indexOf(itemInList);
|
|
2090
|
+
this.userSearchList[index] = newItem;
|
|
2091
|
+
this.selectedList = this.userSearchList[index];
|
|
2092
|
+
}
|
|
2093
|
+
// Delete list item from list
|
|
2094
|
+
deleteItemFromSavedSearchListByObject(itemInList) {
|
|
2095
|
+
this.userSearchList = this.userSearchList.filter(item => item !== itemInList);
|
|
2096
|
+
this.selectedList = null;
|
|
2097
|
+
this.filter = {};
|
|
2098
|
+
this.advancedSearch(true);
|
|
2099
|
+
this.uploadListOnUserStorage();
|
|
2100
|
+
}
|
|
2101
|
+
// User chose to save its advanced form search criteria
|
|
2102
|
+
saveSearch(templateToLoad, newList = true) {
|
|
2103
|
+
if (this.table.config.model) {
|
|
2104
|
+
// Set the corresponding key to its list and show a confirmation modal window
|
|
2105
|
+
this.storageKey = `tables/${this.table.config.model}/searches`;
|
|
2106
|
+
if (this.filter && Object.keys(this.filter).length) {
|
|
2107
|
+
// User wants to save an existing list from the dropdown
|
|
2108
|
+
if (!newList && this.selectedList) {
|
|
2109
|
+
const newItem = { 'name': this.selectedList.name, 'filter': Object.assign({}, this.filter) };
|
|
2110
|
+
this.updateSavedSearchListItemByObject(this.selectedList, newItem);
|
|
2111
|
+
this.uploadListOnUserStorage();
|
|
2112
|
+
// User wants to save a new list
|
|
2113
|
+
}
|
|
2114
|
+
else {
|
|
2115
|
+
this.modalRef = this._modalService.show(templateToLoad);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
// User did not define any criteria
|
|
2120
|
+
this._modal.showWarningDialog(this._translate.instant('AdvancedSearch.Modal.Title'), this._translate.instant('AdvancedSearch.Modal.EmptyCriteriaList'), DIALOG_BUTTONS.Ok);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
else {
|
|
2124
|
+
this._modal.showWarningDialog(this._translate.instant('AdvancedSearch.Modal.Title'), this._translate.instant('AdvancedSearch.Modal.NoListDefined'), DIALOG_BUTTONS.Ok);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
// User Accepted => Save list and Close modal
|
|
2128
|
+
confirmModal(saveFormName) {
|
|
2129
|
+
// name given and list name is unique
|
|
2130
|
+
if (saveFormName && !(this.searchInListByName(saveFormName))) {
|
|
2131
|
+
const newInsertedValue = { 'name': saveFormName, 'filter': Object.assign({}, this.filter) };
|
|
2132
|
+
if (this.userSearchList && this.userSearchList.length !== 0) {
|
|
2133
|
+
this.userSearchList.push(newInsertedValue);
|
|
2134
|
+
this.storageValue = this.userSearchList;
|
|
2135
|
+
this.selectedList = newInsertedValue;
|
|
2136
|
+
}
|
|
2137
|
+
else {
|
|
2138
|
+
this.storageValue = [newInsertedValue];
|
|
2139
|
+
this.userSearchList = this.storageValue;
|
|
2140
|
+
this.selectedList = newInsertedValue;
|
|
2141
|
+
}
|
|
2142
|
+
this.uploadListOnUserStorage();
|
|
2143
|
+
this.modalRef.hide();
|
|
2144
|
+
}
|
|
2145
|
+
else if (saveFormName && (this.searchInListByName(saveFormName))) {
|
|
2146
|
+
// User should insert a different name cause this one is already in use
|
|
2147
|
+
alert(this._translate.instant('AdvancedSearch.Modal.TitleOnListAlreadyCreated'));
|
|
2148
|
+
}
|
|
2149
|
+
else {
|
|
2150
|
+
// User did not insert any text
|
|
2151
|
+
alert(this._translate.instant('AdvancedSearch.Modal.TitleOnUserError'));
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
// User declined => Close modal
|
|
2155
|
+
closeModal() {
|
|
2156
|
+
this.modalRef.hide();
|
|
2157
|
+
}
|
|
2158
|
+
// Upload user's criteria on User Storage
|
|
2159
|
+
uploadListOnUserStorage() {
|
|
2160
|
+
this._userStorage.setItem(this.storageKey, this.userSearchList.filter(x => {
|
|
2161
|
+
return !x.readOnly;
|
|
2162
|
+
}));
|
|
2163
|
+
}
|
|
2164
|
+
/**
|
|
2165
|
+
* @deprecated This method is going to be deprecated. Use search() method instead.
|
|
2166
|
+
* @param {*} event
|
|
2167
|
+
*/
|
|
2168
|
+
asyncAdvancedSearch(event) {
|
|
2169
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2170
|
+
if (this.table && this.table.config && Array.isArray(this.table.config.criteria)) {
|
|
2171
|
+
const query = yield this.getQuery();
|
|
2172
|
+
if (this.mergeQueryParams) {
|
|
2173
|
+
this.selectedList = null;
|
|
2174
|
+
const queryParams = Object.assign({ q: null, s: null }, this.filter);
|
|
2175
|
+
// enumerate criteria
|
|
2176
|
+
this.table.config.criteria.forEach((item) => {
|
|
2177
|
+
if (Object.prototype.hasOwnProperty.call(queryParams, item.name) === false) {
|
|
2178
|
+
// set null value
|
|
2179
|
+
Object.defineProperty(queryParams, item.name, {
|
|
2180
|
+
configurable: true,
|
|
2181
|
+
enumerable: true,
|
|
2182
|
+
writable: true,
|
|
2183
|
+
value: null
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
return this._router.navigate([], {
|
|
2188
|
+
relativeTo: this._activatedRoute,
|
|
2189
|
+
queryParams: queryParams,
|
|
2190
|
+
queryParamsHandling: 'merge'
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
if (this.table.query) {
|
|
2194
|
+
this.table.query.setParam('$filter', query.getParams()['$filter']);
|
|
2195
|
+
}
|
|
2196
|
+
else {
|
|
2197
|
+
this.table.query = query;
|
|
2198
|
+
}
|
|
2199
|
+
this.table.fetch();
|
|
2200
|
+
}
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
search() {
|
|
2204
|
+
return this.asyncAdvancedSearch().then(() => {
|
|
2205
|
+
// do nothing
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
getQuery() {
|
|
2209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2210
|
+
// on simple search return null
|
|
2211
|
+
if (this.collapsed) {
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
if (this.table && this.table.config && Array.isArray(this.table.config.criteria)) {
|
|
2215
|
+
const expressions = [];
|
|
2216
|
+
const values = yield this._advancedSearchValueProvider.getValues();
|
|
2217
|
+
this.table.config.criteria.forEach((x) => {
|
|
2218
|
+
if (Object.prototype.hasOwnProperty.call(this.filter, x.name)) {
|
|
2219
|
+
const nameFilter = this.convertToString(this.filter[x.name]);
|
|
2220
|
+
if (nameFilter.includes('GMT')) {
|
|
2221
|
+
const newDate = this.transformDate(this.filter[x.name]);
|
|
2222
|
+
expressions.push(this._template.transform(x.filter, {
|
|
2223
|
+
value: newDate
|
|
2224
|
+
}));
|
|
2225
|
+
}
|
|
2226
|
+
else if (this.filter[x.name] !== 'undefined') {
|
|
2227
|
+
expressions.push(this._template.transform(x.filter, Object.assign({
|
|
2228
|
+
value: this.filter[x.name]
|
|
2229
|
+
}, values)));
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
});
|
|
2233
|
+
let query;
|
|
2234
|
+
if (expressions || expressions.length) {
|
|
2235
|
+
// create client query
|
|
2236
|
+
query = this._context.model(this.table.config.model).asQueryable();
|
|
2237
|
+
query.setParam('filter', expressions.join(' and '));
|
|
2238
|
+
}
|
|
2239
|
+
else {
|
|
2240
|
+
query = this._context.model(this.table.config.model).asQueryable();
|
|
2241
|
+
}
|
|
2242
|
+
return query;
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
advancedSearch(event) {
|
|
2247
|
+
return this.asyncAdvancedSearch(event);
|
|
2248
|
+
}
|
|
2249
|
+
clear() {
|
|
2250
|
+
if (this.mergeQueryParams) {
|
|
2251
|
+
this.selectedList = null;
|
|
2252
|
+
const queryParams = { q: null, s: null };
|
|
2253
|
+
// enumerate criteria
|
|
2254
|
+
this.table.config.criteria.forEach((item) => {
|
|
2255
|
+
if (Object.prototype.hasOwnProperty.call(queryParams, item.name) === false) {
|
|
2256
|
+
// set null value
|
|
2257
|
+
Object.defineProperty(queryParams, item.name, {
|
|
2258
|
+
configurable: true,
|
|
2259
|
+
enumerable: true,
|
|
2260
|
+
writable: true,
|
|
2261
|
+
value: null
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
return this._router.navigate([], {
|
|
2266
|
+
relativeTo: this._activatedRoute,
|
|
2267
|
+
queryParams: queryParams,
|
|
2268
|
+
queryParamsHandling: 'merge'
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
else {
|
|
2272
|
+
// clear filter
|
|
2273
|
+
this.filter = {};
|
|
2274
|
+
// clear list
|
|
2275
|
+
this.selectedList = null;
|
|
2276
|
+
// raise filter change event
|
|
2277
|
+
this.filterChange.emit(this.filter);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Gets the view state of search control
|
|
2282
|
+
*/
|
|
2283
|
+
get viewState() {
|
|
2284
|
+
return {
|
|
2285
|
+
advancedView: !this.collapsed
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Switches view between simple and advanced search
|
|
2290
|
+
*/
|
|
2291
|
+
toggleView() {
|
|
2292
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2293
|
+
this.collapsed = !this.collapsed;
|
|
2294
|
+
// emit event for view status change
|
|
2295
|
+
this.viewChange.emit(this.viewState);
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
AdvancedTableSearchComponent.decorators = [
|
|
2300
|
+
{ type: Component, args: [{
|
|
2301
|
+
selector: 'app-advanced-table-search',
|
|
2302
|
+
template: "<div class=\"s--list-navbar d-none d-sm-block\">\n\n <div id=\"collapse-header\" [ngClass]=\"{'show':collapsed}\" class=\"collapse multi-collapse\">\n <form class=\"form-inline p-3 col\">\n <div class=\"form-group col-7\" [ngClass]= \"{ 'col-12': !showMore }\">\n <label class=\"pr-3\" for=\"searchText\" [translate]=\"'Tables.Search'\"></label>\n <input (keydown)=\"onSearchKeyDown($event)\" spellcheck=\"false\" type=\"text\" id=\"searchText\" name=\"searchText\"\n class=\"form-control col text-left\" placeholder=\"{{ 'Tables.SearchPlaceholder' | translate }}\" #searchText>\n </div>\n <div *ngIf=\"showMore\" class=\"col-5 text-right text-secondary\" id=\"heading3\">\n <button class=\"d-inline-block btn btn-link collapsed float-right text-secondary text-decoration-none\"\n type=\"button\" (click)=\"toggleView();\">\n <span class=\"d-none d-sm-inline\" [translate]=\"'AdvancedSearch.More'\"></span>\n <i class=\"ml-2 pt-1 fa fa-chevron-down\"></i>\n </button>\n </div>\n </form>\n </div>\n\n <div id=\"collapse3\" [ngClass]=\"{'show':!collapsed}\" class=\"collapse multi-collapse\" aria-labelledby=\"heading3\">\n <div class=\"row\">\n <div class=\"col-sm-12\">\n <div id=\"advanced-form\">\n <ng-content select=\"advanced-search\"></ng-content>\n <hr class=\"mt-0 mb-0\" width=\"97%\">\n <div class=\"row\">\n <div class=\"col-sm-12\">\n <div class=\"form-inline pb-3 pt-3 pr-1 pl-1 col\">\n\n <div class=\"form-group col-4\">\n <div class=\"d-flex flex-row w-100\" *ngIf=\"allowSave\">\n <label class=\"pr-3 d-none\" [translate]=\"'AdvancedSearch.SavedSearches'\"></label>\n <select *ngIf=\"userSearchList\" class=\"custom-select text-secondary flex col-12\" (change)=\"onSelectUserSearch($event)\" [(ngModel)]=\"selectedList\">\n <option [ngValue]=\"null\" [translate]=\"'AdvancedSearch.Choose'\"></option>\n <option *ngFor=\"let item of userSearchList\" [ngValue]=\"item\">{{ item.name }}</option>\n </select>\n <select *ngIf=\"!userSearchList\" class=\"custom-select flex col-6\">\n <option [value]=\"null\" [translate]=\"'AdvancedSearch.Choose'\"></option>\n </select>\n </div>\n </div>\n\n <div class=\"col-3 d-flex flex-row w-100\">\n <div class=\"btn-group\" *ngIf=\"allowSave\" dropdown>\n <button type=\"button\" class=\"btn btn-sm btn-primary\" (click)=\"saveSearch(templateToUse, ( (selectedList && selectedList.readOnly) ? true : false ) )\" [translate]=\"'AdvancedSearch.SaveCurrentSearch'\"></button>\n <button type=\"button\" dropdownToggle class=\"btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n <span class=\"sr-only\" [translate]=\"'AdvancedSearch.ToggleDropdown'\"></span>\n </button>\n\n <div class=\"dropdown-menu\" *dropdownMenu>\n <a [ngClass]=\"{'disabled': selectedList && selectedList.readOnly}\" class=\"dropdown-item\" (click)=\"saveSearch(templateToUse, false)\" [translate]=\"'AdvancedSearch.Save'\" href=\"javascript:void(0)\"></a>\n <a class=\"dropdown-item\" (click)=\"saveSearch(templateToUse)\" [translate]=\"'AdvancedSearch.SaveAs'\" href=\"javascript:void(0)\"></a>\n <div class=\"dropdown-divider\"></div>\n <a [ngClass]=\"{'disabled': selectedList && selectedList.readOnly}\" class=\"dropdown-item\" (click)=\"deleteItemFromSavedSearchListByObject(selectedList)\" [translate]=\"'AdvancedSearch.Delete'\" href=\"javascript:void(0)\"></a>\n </div>\n </div>\n </div>\n\n <div class=\"col-5 text-right text-secondary\">\n <button type=\"button\" class=\"btn btn-light text-secondary pl-2 pr-2 mr-2\" (click)=\"clear()\">\n <i class=\"fa fa-eraser\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-success pl-4 pr-4 mr-2\" (click)=\"advancedSearch($event);\"\n [translate]=\"'Tables.Search'\"></button>\n <button class=\"d-inline-block btn btn-link collapsed float-right text-secondary text-decoration-none\"\n type=\"button\" (click)=\"toggleView();\">\n <span class=\"d-none d-sm-inline\" [translate]=\"'AdvancedSearch.Less'\"></span>\n <i class=\"ml-2 pt-1 fa fa-chevron-up\"></i>\n </button>\n </div>\n\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <ng-template #templateToUse>\n <div class=\"modal-header\">\n <h4 class=\"modal-title pull-left\" [translate]=\"'AdvancedSearch.Modal.Title'\"></h4>\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"modalRef.hide()\">\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n <div class=\"modal-body mt-0 mb-0 text-center\">\n <div>\n <p class=\"text-secondary text-left\" [translate]=\"'AdvancedSearch.Modal.Help'\"></p>\n </div>\n <form class=\"p-3 text-center\" #newListForm=\"ngForm\" >\n <div [ngVar]=\"{ name: ( selectedList?.name ? ( selectedList?.name + ' (1)' ) : null ) }\" #model=\"ngVar\">\n <input [(ngModel)]=\"model.value.name\" [required]=\"true\" id=\"newListName\" name=\"newListName\" type=\"text\" class=\"form-control\" placeholder=\"{{ 'AdvancedSearch.Modal.SaveNamePlaceholder' | translate }}\"/>\n <div class=\"btn-group mt-4\">\n <button [disabled]=\"newListForm.invalid\" type=\"button\" class=\"btn btn-primary\" (click)=\"confirmModal(model.value.name)\" [translate]=\"'AdvancedSearch.Modal.Accept'\"></button>\n <button type=\"button\" class=\"btn btn-gray-300 text-white\" (click)=\"closeModal()\" [translate]=\"'AdvancedSearch.Modal.Close'\"></button>\n </div>\n </div>\n </form>\n </div>\n </ng-template>\n\n</div>\n",
|
|
2303
|
+
encapsulation: ViewEncapsulation.None
|
|
2304
|
+
}] }
|
|
2305
|
+
];
|
|
2306
|
+
/** @nocollapse */
|
|
2307
|
+
AdvancedTableSearchComponent.ctorParameters = () => [
|
|
2308
|
+
{ type: AngularDataContext },
|
|
2309
|
+
{ type: ActivatedRoute },
|
|
2310
|
+
{ type: DatePipe },
|
|
2311
|
+
{ type: BsModalService },
|
|
2312
|
+
{ type: UserStorageService },
|
|
2313
|
+
{ type: TranslateService },
|
|
2314
|
+
{ type: AdvancedFilterValueProvider },
|
|
2315
|
+
{ type: TemplatePipe },
|
|
2316
|
+
{ type: Router },
|
|
2317
|
+
{ type: ModalService }
|
|
2318
|
+
];
|
|
2319
|
+
AdvancedTableSearchComponent.propDecorators = {
|
|
2320
|
+
table: [{ type: Input }],
|
|
2321
|
+
collapsed: [{ type: Input }],
|
|
2322
|
+
allowSave: [{ type: Input }],
|
|
2323
|
+
showMore: [{ type: Input }],
|
|
2324
|
+
mergeQueryParams: [{ type: Input }],
|
|
2325
|
+
searchText: [{ type: ViewChild, args: ['searchText',] }],
|
|
2326
|
+
viewChange: [{ type: Output, args: ['viewChange',] }]
|
|
2327
|
+
};
|
|
2328
|
+
|
|
2329
|
+
const FORMATTERS = {
|
|
2330
|
+
ActionLinkFormatter: new ActionLinkFormatter(),
|
|
2331
|
+
ButtonFormatter: new ButtonFormatter(),
|
|
2332
|
+
DateTimeFormatter: new DateTimeFormatter(),
|
|
2333
|
+
LanguageFormatter: new LanguageFormatter(),
|
|
2334
|
+
LinkFormatter: new LinkFormatter(),
|
|
2335
|
+
NestedPropertyFormatter: new NestedPropertyFormatter(),
|
|
2336
|
+
NgClassFormatter: new NgClassFormatter(),
|
|
2337
|
+
SelectRowFormatter: new SelectRowFormatter(),
|
|
2338
|
+
TemplateFormatter: new TemplateFormatter(),
|
|
2339
|
+
TranslationFormatter: new TranslationFormatter(),
|
|
2340
|
+
TrueFalseFormatter: new TrueFalseFormatter()
|
|
2341
|
+
};
|
|
2342
|
+
|
|
2343
|
+
// important: export this const due to AOT compilation errors
|
|
2344
|
+
// see more https://angular.io/guide/aot-metadata-errors#only-initialized-variables
|
|
2345
|
+
const AdvancedTableModalBaseTemplate = `
|
|
2346
|
+
<div>
|
|
2347
|
+
<!-- search -->
|
|
2348
|
+
<div class="row mt-3">
|
|
2349
|
+
<div class="col-12 col-lg-3">
|
|
2350
|
+
<div class="input-group mb-3">
|
|
2351
|
+
<input spellcheck="false" type="text"
|
|
2352
|
+
(keyup)="onKeyUp($event)" [(ngModel)]="searchText"
|
|
2353
|
+
class="form-control bg-gray-100 border-0"
|
|
2354
|
+
placeholder="{{'Tables.SearchPlaceholder' | translate}}" aria-label="Search" aria-describedby="basic-addon2">
|
|
2355
|
+
<div class="input-group-append">
|
|
2356
|
+
<button class="btn btn-default bg-gray-100 border-0" type="button">
|
|
2357
|
+
<i class="fa fa-search text-secondary"></i>
|
|
2358
|
+
</button>
|
|
2359
|
+
</div>
|
|
2360
|
+
</div>
|
|
2361
|
+
</div>
|
|
2362
|
+
<div #emptyTable class="d-none">
|
|
2363
|
+
<div>
|
|
2364
|
+
<div class="mt-4">
|
|
2365
|
+
<div class="icon-circle bg-gray-300 border-gray-100"></div>
|
|
2366
|
+
</div>
|
|
2367
|
+
<div class="mt-4">
|
|
2368
|
+
<h4 class="font-3xl text-dark" [translate]="'Tables.EmptyTableTitle'"></h4>
|
|
2369
|
+
<p class="font-lg text-dark" [translate]="'Tables.EmptyTableMessage'"></p>
|
|
2370
|
+
</div>
|
|
2371
|
+
|
|
2372
|
+
</div>
|
|
2373
|
+
</div>
|
|
2374
|
+
<div class="col-12 col-lg-5">
|
|
2375
|
+
<div class="btn-toolbar">
|
|
2376
|
+
<button class="btn btn-gray-100">
|
|
2377
|
+
<i class="fa fa-sliders-h pr-2 text-secondary" aria-hidden="true"></i>{{'Tables.Filters' | translate}}
|
|
2378
|
+
</button>
|
|
2379
|
+
<div #dropdownFilter class="dropdown ml-3" dropdown>
|
|
2380
|
+
<button class="btn btn-gray-100 dropdown-toggle"
|
|
2381
|
+
type="button" id="dropdownDefaultFilters"
|
|
2382
|
+
data-toggle="dropdown" dropdownToggle>
|
|
2383
|
+
{{ this.defaultFilter.name | translate }}
|
|
2384
|
+
</button>
|
|
2385
|
+
<div class="dropdown-menu" aria-labelledby="dropdownDefaultFilters" *dropdownMenu>
|
|
2386
|
+
<a class="dropdown-item" *ngFor="let filter of defaultFilters" (click)="advancedSearch(filter.filter ? filter : '')">
|
|
2387
|
+
{{ filter.name | translate }}
|
|
2388
|
+
</a>
|
|
2389
|
+
</div>
|
|
2390
|
+
</div>
|
|
2391
|
+
</div>
|
|
2392
|
+
</div>
|
|
2393
|
+
<div class="col text-lg-right mt-3 mt-lg-0">
|
|
2394
|
+
<button [disabled]="true" class="btn rounded-pill btn-outline-theme">
|
|
2395
|
+
<span class="pr-2 font-lg">+</span><span [translate]="'Tables.AddItem'"></span>
|
|
2396
|
+
</button>
|
|
2397
|
+
</div>
|
|
2398
|
+
</div>
|
|
2399
|
+
<div class="row">
|
|
2400
|
+
<div class="col-12" [ngClass]="{ 'component-loading': loading}">
|
|
2401
|
+
<app-advanced-table #advancedTable (init)="onTableInit($event)"
|
|
2402
|
+
(load)="onDataLoad($event)"
|
|
2403
|
+
[selectable]="true"
|
|
2404
|
+
[multipleSelect]="true"
|
|
2405
|
+
[scrollX]="false" [scrollY]="320"
|
|
2406
|
+
[pageLength]="pageLength"
|
|
2407
|
+
[scrollable]="false" [showFooter]="false"
|
|
2408
|
+
[showActions]="false" [lengthMenu]="lengthMenu"
|
|
2409
|
+
[config]="tableConfig" [configSrc]="tableConfigSrc"></app-advanced-table>
|
|
2410
|
+
</div>
|
|
2411
|
+
</div>
|
|
2412
|
+
<div class="row mt-3 table-custom-pager">
|
|
2413
|
+
<div class="col-12 d-flex" *ngIf="recordsTotal">
|
|
2414
|
+
<div class="mt-2 ml-auto mr-3 text-secondary" [translate]="'Tables.RecordsPerPage'"></div>
|
|
2415
|
+
<div #dropdownLength class="dropdown mr-3" dropdown>
|
|
2416
|
+
<button class="btn btn-gray-100 dropdown-toggle"
|
|
2417
|
+
type="button" id="dropdownPageSize" data-toggle="dropdown"
|
|
2418
|
+
dropdownToggle>
|
|
2419
|
+
{{pageLength}}
|
|
2420
|
+
</button>
|
|
2421
|
+
<div class="dropdown-menu" aria-labelledby="dropdownPageSize" *dropdownMenu>
|
|
2422
|
+
<button (click)="setLength(item)"
|
|
2423
|
+
class="dropdown-item dropdown-item-theme"
|
|
2424
|
+
type="button" *ngFor="let item of lengthMenu">{{item}}</button>
|
|
2425
|
+
</div>
|
|
2426
|
+
</div>
|
|
2427
|
+
<div class="mt-2 mr-2" [translate]="'Tables.PageInfo'" [translateParams]="pageInfo"></div>
|
|
2428
|
+
<button [disabled]="pageInfo && pageInfo.page === 0" class="btn text-nowrap" (click)="firstPage()">
|
|
2429
|
+
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
2430
|
+
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
2431
|
+
</button>
|
|
2432
|
+
<button [disabled]="pageInfo && pageInfo.page===0" class="btn" (click)="previousPage()">
|
|
2433
|
+
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
2434
|
+
</button>
|
|
2435
|
+
<button [disabled]="pageInfo && pageInfo.page===pageInfo.pages-1" class="btn" (click)="nextPage()">
|
|
2436
|
+
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
2437
|
+
</button>
|
|
2438
|
+
<button [disabled]="pageInfo && pageInfo.page===pageInfo.pages-1" class="btn text-nowrap" (click)="lastPage()">
|
|
2439
|
+
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
2440
|
+
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
2441
|
+
</button>
|
|
2442
|
+
</div>
|
|
2443
|
+
</div>
|
|
2444
|
+
</div>
|
|
2445
|
+
`;
|
|
2446
|
+
class AdvancedTableModalBaseComponent extends RouterModalOkCancel {
|
|
2447
|
+
constructor(_router, _activatedRoute, _context, advancedFilterValueProvider, datePipe) {
|
|
2448
|
+
super(_router, _activatedRoute);
|
|
2449
|
+
this._context = _context;
|
|
2450
|
+
this.advancedFilterValueProvider = advancedFilterValueProvider;
|
|
2451
|
+
this.datePipe = datePipe;
|
|
2452
|
+
this.pageLength = 25;
|
|
2453
|
+
this.lengthMenu = [5, 10, 25, 50, 100];
|
|
2454
|
+
this.recordsTotal = 0;
|
|
2455
|
+
this.loading = false;
|
|
2456
|
+
this.recordsFiltered = 0;
|
|
2457
|
+
this.defaultFilters = [AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER];
|
|
2458
|
+
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
2459
|
+
this.pageInfo = {
|
|
2460
|
+
serverSide: true
|
|
2461
|
+
};
|
|
2462
|
+
this.modalClass = 'modal-xl modal-table';
|
|
2463
|
+
this._template = new TemplatePipe();
|
|
2464
|
+
}
|
|
2465
|
+
ngAfterViewInit() {
|
|
2466
|
+
//
|
|
2467
|
+
}
|
|
2468
|
+
_getInputs(componentType) {
|
|
2469
|
+
const props = componentType.__prop__metadata__;
|
|
2470
|
+
const inputs = [];
|
|
2471
|
+
if (props == null) {
|
|
2472
|
+
return inputs;
|
|
2473
|
+
}
|
|
2474
|
+
Object.keys(props).forEach(prop => {
|
|
2475
|
+
const member = props[prop][0];
|
|
2476
|
+
if (member.ngMetadataName === 'Input') {
|
|
2477
|
+
inputs.push(prop);
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
return inputs.sort();
|
|
2481
|
+
}
|
|
2482
|
+
hasInputs() {
|
|
2483
|
+
return [];
|
|
2484
|
+
}
|
|
2485
|
+
ngOnInit() {
|
|
2486
|
+
this.okButtonText = 'Tables.Apply';
|
|
2487
|
+
this.cancelButtonText = 'Tables.Cancel';
|
|
2488
|
+
// get route data
|
|
2489
|
+
this.subscription = this.activatedRoute.data.subscribe(data => {
|
|
2490
|
+
// set modal title if any
|
|
2491
|
+
if (data.title) {
|
|
2492
|
+
this.modalTitle = data.title;
|
|
2493
|
+
}
|
|
2494
|
+
if (data.config) {
|
|
2495
|
+
this.tableConfig = data.config;
|
|
2496
|
+
}
|
|
2497
|
+
if (this.tableConfig) {
|
|
2498
|
+
this.defaultFilters.push.apply(this.defaultFilters, this.tableConfig.searches);
|
|
2499
|
+
}
|
|
2500
|
+
// important: use hasInputs due to AOT compilation. Metadata properties cannot be used (e.g. @Input())
|
|
2501
|
+
// because there are empty during runtime
|
|
2502
|
+
// so we need to define manually input properties that are going to be bind through this process
|
|
2503
|
+
// bind all inputs
|
|
2504
|
+
const inputs = this.hasInputs();
|
|
2505
|
+
inputs.forEach(input => {
|
|
2506
|
+
if (Object.prototype.hasOwnProperty.call(data, input)) {
|
|
2507
|
+
this[input] = data[input];
|
|
2508
|
+
}
|
|
2509
|
+
});
|
|
2510
|
+
this.configChangesSubscription = this.advancedTable.configChanges.subscribe((config) => {
|
|
2511
|
+
if (this.tableConfig) {
|
|
2512
|
+
// reset default filter
|
|
2513
|
+
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
2514
|
+
// init filter
|
|
2515
|
+
this.defaultFilters = [AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER];
|
|
2516
|
+
// add extra filters
|
|
2517
|
+
this.defaultFilters.push.apply(this.defaultFilters, this.tableConfig.searches);
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
cancel() {
|
|
2523
|
+
return this.close();
|
|
2524
|
+
}
|
|
2525
|
+
ok() {
|
|
2526
|
+
return this.close();
|
|
2527
|
+
}
|
|
2528
|
+
ngOnDestroy() {
|
|
2529
|
+
if (this.subscription) {
|
|
2530
|
+
this.subscription.unsubscribe();
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
onDataLoad(data) {
|
|
2534
|
+
this.recordsTotal = data.recordsTotal;
|
|
2535
|
+
this.recordsFiltered = data.recordsFiltered;
|
|
2536
|
+
// get first page info (because page is not going to be fired for first page)
|
|
2537
|
+
if (data.recordsTotal > 0 && this.pageInfo.recordsTotal === 0) {
|
|
2538
|
+
this.pageInfo.recordsTotal = data.recordsTotal;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
previousPage() {
|
|
2542
|
+
// go to next page
|
|
2543
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2544
|
+
this.advancedTable.dataTable.page('previous').draw('page');
|
|
2545
|
+
}
|
|
2546
|
+
nextPage() {
|
|
2547
|
+
// go to next page
|
|
2548
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2549
|
+
this.advancedTable.dataTable.page('next').draw('page');
|
|
2550
|
+
}
|
|
2551
|
+
onTableInit(dataTable) {
|
|
2552
|
+
const self = this;
|
|
2553
|
+
// get initial page info
|
|
2554
|
+
const initialPageInfo = dataTable.page.info();
|
|
2555
|
+
initialPageInfo.start += 1;
|
|
2556
|
+
self.pageInfo = initialPageInfo;
|
|
2557
|
+
// change emptyTableMessage
|
|
2558
|
+
dataTable.context[0].oLanguage.sEmptyTable = this.emptyTable.nativeElement.innerHTML;
|
|
2559
|
+
// bind page change event
|
|
2560
|
+
dataTable.on('page.dt', function () {
|
|
2561
|
+
const pageInfo = dataTable.page.info();
|
|
2562
|
+
pageInfo.start += 1;
|
|
2563
|
+
self.pageInfo = pageInfo;
|
|
2564
|
+
});
|
|
2565
|
+
// bind draw change event
|
|
2566
|
+
dataTable.on('draw.dt', function () {
|
|
2567
|
+
// for future use
|
|
2568
|
+
self.loading = false;
|
|
2569
|
+
const pageInfo = dataTable.page.info();
|
|
2570
|
+
pageInfo.start += 1;
|
|
2571
|
+
self.pageInfo = pageInfo;
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
firstPage() {
|
|
2575
|
+
// go to first page
|
|
2576
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2577
|
+
this.advancedTable.dataTable.page('first').draw('page');
|
|
2578
|
+
}
|
|
2579
|
+
lastPage() {
|
|
2580
|
+
// go to last page
|
|
2581
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2582
|
+
this.advancedTable.dataTable.page('last').draw('page');
|
|
2583
|
+
}
|
|
2584
|
+
setLength(value) {
|
|
2585
|
+
this.pageLength = value;
|
|
2586
|
+
this.advancedTable.dataTable.page.len(value).draw();
|
|
2587
|
+
}
|
|
2588
|
+
onKeyUp(event) {
|
|
2589
|
+
if (event.code === 'Enter') {
|
|
2590
|
+
// loading
|
|
2591
|
+
this.loading = true;
|
|
2592
|
+
this.advancedTable.search(event.target.value);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
*
|
|
2597
|
+
* Reads the modal table and returns a list of filters to use in the table
|
|
2598
|
+
* query.
|
|
2599
|
+
*
|
|
2600
|
+
* @param tableConfig {object} The table configuration from json file
|
|
2601
|
+
* @param defaultFilterValues {object} A list of default filters
|
|
2602
|
+
* @param {object} searchItem The searchItem to use
|
|
2603
|
+
*
|
|
2604
|
+
*/
|
|
2605
|
+
getFilters(tableConfig, defaultFilterValues = {}, searchItem) {
|
|
2606
|
+
// Do nothing in absence of configuration.
|
|
2607
|
+
if (!tableConfig || !Array.isArray(tableConfig.criteria)) {
|
|
2608
|
+
console.warn('No table configuration available', this.tableConfig);
|
|
2609
|
+
return [];
|
|
2610
|
+
}
|
|
2611
|
+
const matchedCriterion = tableConfig.criteria.find(criterion => Object.prototype.hasOwnProperty.call(searchItem.filter, criterion.name));
|
|
2612
|
+
if (!matchedCriterion) {
|
|
2613
|
+
return [];
|
|
2614
|
+
}
|
|
2615
|
+
const criterionSafeName = matchedCriterion.name !== null
|
|
2616
|
+
? matchedCriterion.name.toString()
|
|
2617
|
+
: '';
|
|
2618
|
+
let applicableFilter;
|
|
2619
|
+
// Raw dates should be transformed to a specific format
|
|
2620
|
+
if (criterionSafeName.includes('GMT')) {
|
|
2621
|
+
const newDate = this.datePipe.transform(criterionSafeName, 'MM/dd/yyyy');
|
|
2622
|
+
applicableFilter = this._template.transform(matchedCriterion.filter, {
|
|
2623
|
+
value: newDate
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
else if (criterionSafeName !== 'undefined') {
|
|
2627
|
+
applicableFilter = (this._template.transform(matchedCriterion.filter, Object.assign({
|
|
2628
|
+
value: matchedCriterion.filter
|
|
2629
|
+
}, defaultFilterValues)));
|
|
2630
|
+
}
|
|
2631
|
+
else {
|
|
2632
|
+
applicableFilter = '';
|
|
2633
|
+
}
|
|
2634
|
+
return [applicableFilter];
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
*
|
|
2638
|
+
* Runs a filter against the modal table
|
|
2639
|
+
*
|
|
2640
|
+
* @param {object} filter The filter to use in the search
|
|
2641
|
+
*
|
|
2642
|
+
*/
|
|
2643
|
+
advancedSearch(filter) {
|
|
2644
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2645
|
+
// Do nothing in absence of configuration.
|
|
2646
|
+
if (!this.tableConfig && !Array.isArray(this.tableConfig.criteria)) {
|
|
2647
|
+
console.warn('No table configuration available', this.tableConfig);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
// An advanced table has some filters by default
|
|
2651
|
+
let defaultFilterValues;
|
|
2652
|
+
try {
|
|
2653
|
+
defaultFilterValues = yield this.advancedFilterValueProvider.getValues();
|
|
2654
|
+
}
|
|
2655
|
+
catch (err) {
|
|
2656
|
+
defaultFilterValues = {};
|
|
2657
|
+
}
|
|
2658
|
+
const expressions = [];
|
|
2659
|
+
let predefinedCriteria;
|
|
2660
|
+
if (filter) {
|
|
2661
|
+
this.defaultFilter = filter;
|
|
2662
|
+
predefinedCriteria = this.getFilters(this.tableConfig, defaultFilterValues, filter);
|
|
2663
|
+
}
|
|
2664
|
+
else {
|
|
2665
|
+
// Empty a falsy filter resets the results
|
|
2666
|
+
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
2667
|
+
}
|
|
2668
|
+
// search filter is applied separately, there is no need to add here.
|
|
2669
|
+
expressions.push(predefinedCriteria);
|
|
2670
|
+
const query = this._context.model(this.advancedTable.config.model).asQueryable();
|
|
2671
|
+
query.setParam('filter', expressions.join(' and ')).prepare();
|
|
2672
|
+
this.advancedTable.query = query;
|
|
2673
|
+
this.advancedTable.fetch();
|
|
2674
|
+
});
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER = {
|
|
2678
|
+
name: 'Tables.All',
|
|
2679
|
+
filter: null
|
|
2680
|
+
};
|
|
2681
|
+
AdvancedTableModalBaseComponent.decorators = [
|
|
2682
|
+
{ type: Component, args: [{
|
|
2683
|
+
selector: 'app-advanced-table-modal',
|
|
2684
|
+
template: AdvancedTableModalBaseTemplate,
|
|
2685
|
+
styles: [`
|
|
2686
|
+
|
|
2687
|
+
`]
|
|
2688
|
+
}] }
|
|
2689
|
+
];
|
|
2690
|
+
/** @nocollapse */
|
|
2691
|
+
AdvancedTableModalBaseComponent.ctorParameters = () => [
|
|
2692
|
+
{ type: Router },
|
|
2693
|
+
{ type: ActivatedRoute },
|
|
2694
|
+
{ type: AngularDataContext },
|
|
2695
|
+
{ type: AdvancedFilterValueProvider },
|
|
2696
|
+
{ type: DatePipe }
|
|
2697
|
+
];
|
|
2698
|
+
AdvancedTableModalBaseComponent.propDecorators = {
|
|
2699
|
+
advancedTable: [{ type: ViewChild, args: ['advancedTable',] }],
|
|
2700
|
+
emptyTable: [{ type: ViewChild, args: ['emptyTable',] }],
|
|
2701
|
+
dropdownFilter: [{ type: ViewChild, args: ['dropdownFilter',] }],
|
|
2702
|
+
dropdownLength: [{ type: ViewChild, args: ['dropdownLength',] }],
|
|
2703
|
+
tableConfig: [{ type: Input, args: ['tableConfig',] }],
|
|
2704
|
+
tableConfigSrc: [{ type: Input, args: ['tableConfigSrc',] }],
|
|
2705
|
+
pageLength: [{ type: Input, args: ['pageLength',] }],
|
|
2706
|
+
searchText: [{ type: Input, args: ['searchText',] }],
|
|
2707
|
+
lengthMenu: [{ type: Input, args: ['lengthMenu',] }],
|
|
2708
|
+
defaultFilter: [{ type: Input, args: ['defaultFilter',] }]
|
|
2709
|
+
};
|
|
2710
|
+
|
|
2711
|
+
class AdvancedSelectComponent extends AdvancedTableModalBaseComponent {
|
|
2712
|
+
constructor(router, activatedRoute, context, advancedFilterValueProvider, datePipe, _modalRef) {
|
|
2713
|
+
super(router, activatedRoute, context, advancedFilterValueProvider, datePipe);
|
|
2714
|
+
this._modalRef = _modalRef;
|
|
2715
|
+
this.dismiss = new EventEmitter();
|
|
2716
|
+
}
|
|
2717
|
+
ok() {
|
|
2718
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2719
|
+
this._modalRef.hide();
|
|
2720
|
+
this.dismiss.emit('ok');
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
cancel() {
|
|
2724
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2725
|
+
this._modalRef.hide();
|
|
2726
|
+
this.dismiss.emit('cancel');
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
AdvancedSelectComponent.decorators = [
|
|
2731
|
+
{ type: Component, args: [{
|
|
2732
|
+
selector: 'app-advanced-select',
|
|
2733
|
+
template: "<div class=\"modal-header\" *ngIf=\"modalTitle\">\n <h5 class=\"modal-title\">{{modalTitle | translate}}</h5>\n <button [disabled]=\"cancelButtonDisabled\" (click)=\"cancel()\" type=\"button\" class=\"close\" data-dismiss=\"modal\"\n aria-label=\"Cancel\">\n <span aria-hidden=\"true\">×</span>\n </button>\n</div>\n<div class=\"modal-body\">\n <div>\n <!-- search -->\n <div class=\"row mt-3\">\n <div class=\"col-12 col-lg-3\">\n <div class=\"input-group mb-3\">\n <input spellcheck=\"false\" type=\"text\" (keyup)=\"onKeyUp($event)\" [(ngModel)]=\"searchText\"\n class=\"form-control bg-gray-100 border-0\" placeholder=\"{{'Tables.SearchPlaceholder' | translate}}\"\n aria-label=\"Search\" aria-describedby=\"basic-addon2\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-default bg-gray-100 border-0\" type=\"button\">\n <i class=\"fa fa-search text-secondary\"></i>\n </button>\n </div>\n </div>\n </div>\n <div #emptyTable class=\"d-none\">\n <div>\n <div class=\"mt-4\">\n <div class=\"icon-circle bg-gray-300 border-gray-100\"></div>\n </div>\n <div class=\"mt-4\">\n <h4 class=\"font-3xl text-dark\" [translate]=\"'Tables.EmptyTableTitle'\"></h4>\n <p class=\"font-lg text-dark\" [translate]=\"'Tables.EmptyTableMessage'\"></p>\n </div>\n\n </div>\n </div>\n <div class=\"col-12 col-lg-5\">\n <div class=\"btn-toolbar\">\n <button class=\"btn btn-gray-100\">\n <i class=\"fa fa-sliders-h pr-2 text-secondary\" aria-hidden=\"true\"></i>{{'Tables.Filters' | translate}}\n </button>\n <div class=\"dropdown ml-3\" dropdown>\n <button dropdownToggle class=\"btn btn-gray-100 dropdown-toggle\" type=\"button\" id=\"dropdownDefaultFilters\"\n data-toggle=\"dropdown\">\n {{ this.defaultFilter.name | translate }}\n </button>\n <div class=\"dropdown-menu\" aria-labelledby=\"dropdownDefaultFilters\" *dropdownMenu>\n <a class=\"dropdown-item\" *ngFor=\"let filter of defaultFilters\"\n (click)=\"advancedSearch(filter.filter ? filter : '')\">\n {{ filter.name | translate }}\n </a>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col text-lg-right mt-3 mt-lg-0\">\n <button [disabled]=\"true\" class=\"btn rounded-pill btn-outline-theme\">\n <span class=\"pr-2 font-lg\">+</span><span [translate]=\"'Tables.AddItem'\"></span>\n </button>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-12\" [ngClass]=\"{ 'component-loading': loading}\">\n <app-advanced-table #advancedTable (init)=\"onTableInit($event)\" (load)=\"onDataLoad($event)\"\n [selectable]=\"true\" [multipleSelect]=\"true\" [scrollX]=\"false\" [scrollY]=\"320\" [pageLength]=\"pageLength\"\n [scrollable]=\"false\" [showFooter]=\"false\" [showActions]=\"false\" [lengthMenu]=\"lengthMenu\"\n [config]=\"tableConfig\" [configSrc]=\"tableConfigSrc\"></app-advanced-table>\n </div>\n </div>\n <div class=\"row mt-3 table-custom-pager\">\n <div class=\"col-12 d-flex\" *ngIf=\"recordsTotal\">\n <div class=\"mt-2 ml-auto mr-3 text-secondary\" [translate]=\"'Tables.RecordsPerPage'\"></div>\n <div class=\"dropdown mr-3\" dropdown>\n <button dropdownToggle class=\"btn btn-gray-100 dropdown-toggle\" type=\"button\" id=\"dropdownPageSize\"\n data-toggle=\"dropdown\">\n {{pageLength}}\n </button>\n <div class=\"dropdown-menu\" aria-labelledby=\"dropdownPageSize\" *dropdownMenu>\n <button (click)=\"setLength(item)\" class=\"dropdown-item dropdown-item-theme\" type=\"button\"\n *ngFor=\"let item of lengthMenu\">{{item}}</button>\n </div>\n </div>\n <div class=\"mt-2 mr-2\" [translate]=\"'Tables.PageInfo'\" [translateParams]=\"pageInfo\"></div>\n <button [disabled]=\"pageInfo && pageInfo.page === 0\" class=\"btn text-nowrap\" (click)=\"firstPage()\">\n <i class=\"fa fa-chevron-left text-color-theme\"></i>\n <i class=\"fa fa-chevron-left text-color-theme\"></i>\n </button>\n <button [disabled]=\"pageInfo && pageInfo.page===0\" class=\"btn\" (click)=\"previousPage()\">\n <i class=\"fa fa-chevron-left text-color-theme\"></i>\n </button>\n <button [disabled]=\"pageInfo && pageInfo.page===pageInfo.pages-1\" class=\"btn\" (click)=\"nextPage()\">\n <i class=\"fa fa-chevron-right text-color-theme\"></i>\n </button>\n <button [disabled]=\"pageInfo && pageInfo.page===pageInfo.pages-1\" class=\"btn text-nowrap\"\n (click)=\"lastPage()\">\n <i class=\"fa fa-chevron-right text-color-theme\"></i>\n <i class=\"fa fa-chevron-right text-color-theme\"></i>\n </button>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-footer justify-content-end\">\n <button [disabled]=\"okButtonDisabled\" (click)=\"ok()\" type=\"button\" [ngClass]=\"okButtonClass\">\n {{okButtonText | translate}}\n </button>\n <button [disabled]=\"cancelButtonDisabled\" (click)=\"cancel()\" type=\"button\" [ngClass]=\"cancelButtonClass\"\n data-dismiss=\"modal\">\n {{ cancelButtonText | translate}}\n </button>\n</div>\n",
|
|
2734
|
+
encapsulation: ViewEncapsulation.None,
|
|
2735
|
+
styles: [`
|
|
2736
|
+
.modal-table .dataTables_wrapper {
|
|
2737
|
+
height: 50vh;
|
|
2738
|
+
overflow-y: auto;
|
|
2739
|
+
}
|
|
2740
|
+
`]
|
|
2741
|
+
}] }
|
|
2742
|
+
];
|
|
2743
|
+
/** @nocollapse */
|
|
2744
|
+
AdvancedSelectComponent.ctorParameters = () => [
|
|
2745
|
+
{ type: Router },
|
|
2746
|
+
{ type: ActivatedRoute },
|
|
2747
|
+
{ type: AngularDataContext },
|
|
2748
|
+
{ type: AdvancedFilterValueProvider },
|
|
2749
|
+
{ type: DatePipe },
|
|
2750
|
+
{ type: BsModalRef }
|
|
2751
|
+
];
|
|
2752
|
+
|
|
2753
|
+
class AdvancedSelectService {
|
|
2754
|
+
constructor(_modal) {
|
|
2755
|
+
this._modal = _modal;
|
|
2756
|
+
//
|
|
2757
|
+
}
|
|
2758
|
+
select(initialState) {
|
|
2759
|
+
return new Promise((resolve, reject) => {
|
|
2760
|
+
try {
|
|
2761
|
+
const modalRef = this._modal.openModalComponent(AdvancedSelectComponent, {
|
|
2762
|
+
class: 'modal-xl modal-table',
|
|
2763
|
+
ignoreBackdropClick: true,
|
|
2764
|
+
animated: true,
|
|
2765
|
+
initialState: initialState
|
|
2766
|
+
});
|
|
2767
|
+
const componentRef = modalRef.content;
|
|
2768
|
+
componentRef.dismiss.subscribe((result) => {
|
|
2769
|
+
return resolve({
|
|
2770
|
+
result: result,
|
|
2771
|
+
items: componentRef.advancedTable.selected
|
|
2772
|
+
});
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
catch (err) {
|
|
2776
|
+
return reject(err);
|
|
2777
|
+
}
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
AdvancedSelectService.decorators = [
|
|
2782
|
+
{ type: Injectable }
|
|
2783
|
+
];
|
|
2784
|
+
/** @nocollapse */
|
|
2785
|
+
AdvancedSelectService.ctorParameters = () => [
|
|
2786
|
+
{ type: ModalService }
|
|
2787
|
+
];
|
|
2788
|
+
|
|
2789
|
+
class ActivatedTableService {
|
|
2790
|
+
constructor(_router, _activatedRoute) {
|
|
2791
|
+
this._router = _router;
|
|
2792
|
+
this._activatedRoute = _activatedRoute;
|
|
2793
|
+
_router.events.forEach((event) => {
|
|
2794
|
+
if (event instanceof NavigationStart) {
|
|
2795
|
+
if (this._activeTable) {
|
|
2796
|
+
this._activeTable = null;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
});
|
|
2800
|
+
}
|
|
2801
|
+
get activeTable() {
|
|
2802
|
+
return this._activeTable;
|
|
2803
|
+
}
|
|
2804
|
+
set activeTable(tableComponent) {
|
|
2805
|
+
this._activeTable = tableComponent;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
ActivatedTableService.decorators = [
|
|
2809
|
+
{ type: Injectable, args: [{
|
|
2810
|
+
providedIn: 'root'
|
|
2811
|
+
},] }
|
|
2812
|
+
];
|
|
2813
|
+
/** @nocollapse */
|
|
2814
|
+
ActivatedTableService.ctorParameters = () => [
|
|
2815
|
+
{ type: Router },
|
|
2816
|
+
{ type: ActivatedRoute }
|
|
2817
|
+
];
|
|
2818
|
+
ActivatedTableService.ngInjectableDef = defineInjectable({ factory: function ActivatedTableService_Factory() { return new ActivatedTableService(inject(Router), inject(ActivatedRoute)); }, token: ActivatedTableService, providedIn: "root" });
|
|
2819
|
+
|
|
2820
|
+
class BtnExpDirective {
|
|
2821
|
+
constructor(_activatedTable, _context, _config, _translate, _loadingService, _errorService) {
|
|
2822
|
+
this._activatedTable = _activatedTable;
|
|
2823
|
+
this._context = _context;
|
|
2824
|
+
this._config = _config;
|
|
2825
|
+
this._translate = _translate;
|
|
2826
|
+
this._loadingService = _loadingService;
|
|
2827
|
+
this._errorService = _errorService;
|
|
2828
|
+
}
|
|
2829
|
+
clickEvent(event) {
|
|
2830
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2831
|
+
event.preventDefault();
|
|
2832
|
+
event.stopPropagation();
|
|
2833
|
+
if (this._activatedTable.activeTable) {
|
|
2834
|
+
const lastQuery = this._activatedTable.activeTable.lastQuery;
|
|
2835
|
+
const tableConfig = this._activatedTable.activeTable.config;
|
|
2836
|
+
const serviceHeaders = this._context.getService().getHeaders();
|
|
2837
|
+
const queryModel = lastQuery.getModel();
|
|
2838
|
+
let query = this._context.model(queryModel).asQueryable();
|
|
2839
|
+
const queryParams = lastQuery.getParams();
|
|
2840
|
+
query = this.constructQuery(query, queryParams, tableConfig, serviceHeaders);
|
|
2841
|
+
const maxExportItems = 1000;
|
|
2842
|
+
{
|
|
2843
|
+
query.skip(0).take(maxExportItems);
|
|
2844
|
+
}
|
|
2845
|
+
// This constructs the URI based on the way @themost does
|
|
2846
|
+
const url = this.constructURL(query);
|
|
2847
|
+
this.downloadFile(url, query.getService().getHeaders(), query.getModel());
|
|
2848
|
+
}
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Sets the parameters to the query based on
|
|
2853
|
+
* the active filters of the activated table
|
|
2854
|
+
* @param {ClientDataQueryable} query
|
|
2855
|
+
* @param params: The filters of the last query of the activated table
|
|
2856
|
+
* @return ClientDataQueryable
|
|
2857
|
+
*/
|
|
2858
|
+
setParams(query, params) {
|
|
2859
|
+
if (params && typeof params === 'object') {
|
|
2860
|
+
Object.keys(params).forEach((key) => {
|
|
2861
|
+
query.setParam(key, params[key]);
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
return query;
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Sets the headers to the query based on
|
|
2868
|
+
* the default headers of the DataContext
|
|
2869
|
+
* and appending the accept header to get
|
|
2870
|
+
* an xlsx file from the API
|
|
2871
|
+
* @param {ClientDataQueryable} query
|
|
2872
|
+
* @param {Headers} headers: The headers to append to the query
|
|
2873
|
+
* @return ClientDataQueryable
|
|
2874
|
+
*/
|
|
2875
|
+
setHeaders(query, headers) {
|
|
2876
|
+
Object.keys(headers).forEach((key) => {
|
|
2877
|
+
if (headers.hasOwnProperty(key)) {
|
|
2878
|
+
query.getService().setHeader(key, headers[key]);
|
|
2879
|
+
}
|
|
2880
|
+
});
|
|
2881
|
+
// sets a header to accept xlsx files
|
|
2882
|
+
query.getService().setHeader('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
2883
|
+
return query;
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Constructs the select parameter of the
|
|
2887
|
+
* ClientDataQuery by replacing the target name
|
|
2888
|
+
* of the field based on the localized translation
|
|
2889
|
+
* of that property on the advanced tabled
|
|
2890
|
+
* @param {ClientDataQueryable} query
|
|
2891
|
+
* @param tableConfig: The configuration of the activated table
|
|
2892
|
+
* @return ClientDataQueryable
|
|
2893
|
+
*/
|
|
2894
|
+
constructQueryParam(query, tableConfig) {
|
|
2895
|
+
let selectQuery = query.getParams().$select;
|
|
2896
|
+
let selectQueryArray = selectQuery.split(',');
|
|
2897
|
+
selectQueryArray = selectQueryArray.map((select) => {
|
|
2898
|
+
const words = select.split(' ');
|
|
2899
|
+
if (words.length === 3) {
|
|
2900
|
+
if (tableConfig && tableConfig.columns) {
|
|
2901
|
+
for (const column of tableConfig.columns) {
|
|
2902
|
+
if (column.property === words[2] && column.title) {
|
|
2903
|
+
words[2] = `"${this._translate.instant(column.title)}"`;
|
|
2904
|
+
}
|
|
2905
|
+
else if (column.property === words[2]) {
|
|
2906
|
+
words[2] = `"${this._translate.instant(words[2])}"`;
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
if (words.length === 1) {
|
|
2912
|
+
if (tableConfig && tableConfig.columns) {
|
|
2913
|
+
for (const column of tableConfig.columns) {
|
|
2914
|
+
if (column.name === words[0] && column.title) {
|
|
2915
|
+
words[0] = `${words[0]} as "${this._translate.instant(column.title)}"`;
|
|
2916
|
+
break;
|
|
2917
|
+
}
|
|
2918
|
+
else if (column.name === words[0] && column.property) {
|
|
2919
|
+
words[0] = `${words[0]} as "${this._translate.instant(column.property)}"`;
|
|
2920
|
+
break;
|
|
2921
|
+
}
|
|
2922
|
+
else if (words[0] === column.name && words[0] === "id" && column.hidden) {
|
|
2923
|
+
break;
|
|
2924
|
+
}
|
|
2925
|
+
else if (words[0] == column.name || words[0] === column.property) {
|
|
2926
|
+
words[0] = `${words[0]} as "${this._translate.instant(words[0])}"`;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
return words[0];
|
|
2931
|
+
}
|
|
2932
|
+
return words.join(' ');
|
|
2933
|
+
});
|
|
2934
|
+
selectQuery = selectQueryArray.join(',');
|
|
2935
|
+
query.setParam('$select', selectQuery);
|
|
2936
|
+
return query;
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* Constructs the fetch URL based the query parameters
|
|
2940
|
+
* @param {ClientDataQueryable} query
|
|
2941
|
+
* @return String
|
|
2942
|
+
*/
|
|
2943
|
+
constructURL(query) {
|
|
2944
|
+
const baseURL = query.getService().getBase();
|
|
2945
|
+
let url = baseURL.endsWith('/') ? (baseURL + query.getModel() + '?') : (baseURL + '/' + query.getModel() + '?');
|
|
2946
|
+
Object.keys(query.getParams()).forEach((key, index) => {
|
|
2947
|
+
if (index === 0) {
|
|
2948
|
+
url += key + '=' + query.getParams()[key];
|
|
2949
|
+
}
|
|
2950
|
+
else {
|
|
2951
|
+
url += '&' + key + '=' + query.getParams()[key];
|
|
2952
|
+
}
|
|
2953
|
+
});
|
|
2954
|
+
return url;
|
|
2955
|
+
}
|
|
2956
|
+
/**
|
|
2957
|
+
* Constructs the query by setting the parameters of the
|
|
2958
|
+
* advanced table's active filters, sets the necessary headers
|
|
2959
|
+
* and build the select parameter of the query
|
|
2960
|
+
* @param {ClientDataQueryable} query
|
|
2961
|
+
* @param params: The filters of the last query of the activated table
|
|
2962
|
+
* @param tableConfig: The configuration of the activated table
|
|
2963
|
+
* @param {Headers} headers: The headers to append to the query
|
|
2964
|
+
* @return ClientDataQueryable
|
|
2965
|
+
*/
|
|
2966
|
+
constructQuery(query, params, tableConfig, headers) {
|
|
2967
|
+
let _query = this.setParams(query, params);
|
|
2968
|
+
_query = this.setHeaders(_query, headers);
|
|
2969
|
+
_query = this.constructQueryParam(_query, tableConfig);
|
|
2970
|
+
return _query;
|
|
2971
|
+
}
|
|
2972
|
+
/**
|
|
2973
|
+
* Fetches and saves the xlsx file
|
|
2974
|
+
* @param {String} url
|
|
2975
|
+
* @param {Headers} serviceHeaders: The headers to append to the query
|
|
2976
|
+
* @param modelName: The model name of the advanced table
|
|
2977
|
+
*/
|
|
2978
|
+
downloadFile(url, serviceHeaders, modelName) {
|
|
2979
|
+
this._loadingService.showLoading();
|
|
2980
|
+
fetch(encodeURI(url), {
|
|
2981
|
+
headers: serviceHeaders,
|
|
2982
|
+
credentials: 'include'
|
|
2983
|
+
}).then(response => {
|
|
2984
|
+
if (response && !response.ok) {
|
|
2985
|
+
throw new ResponseError(response.statusText, response.status);
|
|
2986
|
+
}
|
|
2987
|
+
return response.blob();
|
|
2988
|
+
}).then(blob => {
|
|
2989
|
+
const objectUrl = window.URL.createObjectURL(blob);
|
|
2990
|
+
const a = document.createElement('a');
|
|
2991
|
+
document.body.appendChild(a);
|
|
2992
|
+
a.setAttribute('style', 'display: none');
|
|
2993
|
+
a.href = objectUrl;
|
|
2994
|
+
const downloadName = `${modelName}.xlsx`;
|
|
2995
|
+
a.download = downloadName;
|
|
2996
|
+
// this adds support for IE and MS Edge (up to version 44.19041)
|
|
2997
|
+
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
|
2998
|
+
window.navigator.msSaveOrOpenBlob(blob, downloadName);
|
|
2999
|
+
}
|
|
3000
|
+
else {
|
|
3001
|
+
// for all other browsers
|
|
3002
|
+
a.click();
|
|
3003
|
+
}
|
|
3004
|
+
window.URL.revokeObjectURL(objectUrl);
|
|
3005
|
+
a.remove();
|
|
3006
|
+
this._loadingService.hideLoading();
|
|
3007
|
+
}).catch(err => {
|
|
3008
|
+
this._loadingService.hideLoading();
|
|
3009
|
+
this._errorService.showError(err, {
|
|
3010
|
+
continueLink: '.'
|
|
3011
|
+
});
|
|
3012
|
+
});
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
BtnExpDirective.decorators = [
|
|
3016
|
+
{ type: Directive, args: [{
|
|
3017
|
+
selector: '.universis-btn-export'
|
|
3018
|
+
},] }
|
|
3019
|
+
];
|
|
3020
|
+
/** @nocollapse */
|
|
3021
|
+
BtnExpDirective.ctorParameters = () => [
|
|
3022
|
+
{ type: ActivatedTableService },
|
|
3023
|
+
{ type: AngularDataContext },
|
|
3024
|
+
{ type: ConfigurationService },
|
|
3025
|
+
{ type: TranslateService },
|
|
3026
|
+
{ type: LoadingService },
|
|
3027
|
+
{ type: ErrorService }
|
|
3028
|
+
];
|
|
3029
|
+
BtnExpDirective.propDecorators = {
|
|
3030
|
+
clickEvent: [{ type: HostListener, args: ['click', ['$event'],] }]
|
|
3031
|
+
};
|
|
3032
|
+
|
|
3033
|
+
class AdvancedTableSettingsComponent {
|
|
3034
|
+
constructor(_context, _activatedRoute, elRef, modalService, userStorage, formBuilder, _activatedTable) {
|
|
3035
|
+
this._context = _context;
|
|
3036
|
+
this._activatedRoute = _activatedRoute;
|
|
3037
|
+
this.elRef = elRef;
|
|
3038
|
+
this.modalService = modalService;
|
|
3039
|
+
this.userStorage = userStorage;
|
|
3040
|
+
this.formBuilder = formBuilder;
|
|
3041
|
+
this._activatedTable = _activatedTable;
|
|
3042
|
+
this.params = this._activatedRoute.params;
|
|
3043
|
+
this.columns = [];
|
|
3044
|
+
}
|
|
3045
|
+
ngOnInit() {
|
|
3046
|
+
}
|
|
3047
|
+
ngAfterViewInit() {
|
|
3048
|
+
this.activeTableConfig = this._activatedTable.activeTable.getConfig();
|
|
3049
|
+
this.storageKey = 'registrar/settings/' + this.activeTableConfig.title + '/table/columns';
|
|
3050
|
+
this.form = this.formBuilder.group({ columns: new FormArray([], this.minSelectedColumns(1)) });
|
|
3051
|
+
this.userStorage.getItem(this.storageKey).then((data) => {
|
|
3052
|
+
if (data) {
|
|
3053
|
+
this.savedTableSettings = data.value;
|
|
3054
|
+
if (this.savedTableSettings) {
|
|
3055
|
+
if (this._activatedTable.activeTable) {
|
|
3056
|
+
this._activatedTable.activeTable.handleColumns(this.savedTableSettings);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
this.addCheckboxes();
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
addCheckboxes() {
|
|
3064
|
+
this.tableColumns = this.activeTableConfig.columns.slice(1);
|
|
3065
|
+
this.tableColumns.forEach((column, i) => {
|
|
3066
|
+
if (!column.hidden) {
|
|
3067
|
+
const control = {
|
|
3068
|
+
id: column.name,
|
|
3069
|
+
name: column.title
|
|
3070
|
+
};
|
|
3071
|
+
this.columns.push(control);
|
|
3072
|
+
}
|
|
3073
|
+
});
|
|
3074
|
+
if (this.savedTableSettings) {
|
|
3075
|
+
this.columns.forEach((column, i) => {
|
|
3076
|
+
column.check = false;
|
|
3077
|
+
column.check = this.savedTableSettings.includes(column.id);
|
|
3078
|
+
});
|
|
3079
|
+
this.columns.map((column, i) => {
|
|
3080
|
+
const control = new FormControl(column.check);
|
|
3081
|
+
this.form.controls.columns.push(control);
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
openTableSettings(table_settings_modal) {
|
|
3086
|
+
this.modalRef = this.modalService.show(table_settings_modal);
|
|
3087
|
+
}
|
|
3088
|
+
saveTableSettings() {
|
|
3089
|
+
const selectedColumnsIds = this.form.value.columns
|
|
3090
|
+
.map((v, i) => v ? this.columns[i].id : null)
|
|
3091
|
+
.filter(v => v !== null);
|
|
3092
|
+
this.userStorage.setItem(this.storageKey, selectedColumnsIds);
|
|
3093
|
+
if (this._activatedTable.activeTable) {
|
|
3094
|
+
this._activatedTable.activeTable.handleColumns(selectedColumnsIds);
|
|
3095
|
+
}
|
|
3096
|
+
this.modalRef.hide();
|
|
3097
|
+
}
|
|
3098
|
+
dismiss() {
|
|
3099
|
+
this.modalRef.hide();
|
|
3100
|
+
}
|
|
3101
|
+
minSelectedColumns(min = 1) {
|
|
3102
|
+
const validator = (formArray) => {
|
|
3103
|
+
const totalSelected = formArray.controls
|
|
3104
|
+
.map(control => control.value)
|
|
3105
|
+
.reduce((prev, next) => next ? prev + next : prev, 0);
|
|
3106
|
+
return totalSelected >= min ? null : { required: true };
|
|
3107
|
+
};
|
|
3108
|
+
return validator;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
AdvancedTableSettingsComponent.decorators = [
|
|
3112
|
+
{ type: Component, args: [{
|
|
3113
|
+
selector: 'app-advanced-table-settings',
|
|
3114
|
+
template: "<i class=\"fa fa-edit fa-lg advanced-table-settings\" (click)=\"openTableSettings(modal_settings_template)\"></i>\r\n<ng-template #modal_settings_template>\r\n <div class=\"modal-header\">\r\n <h3 class=\"modal-title\" [translate]=\"'Tables.ColumnDisplay'\"></h3>\r\n <button type=\"button\" class=\"close pull-right\" aria-label=\"Close\" (click)=\"dismiss()\">\r\n <span aria-hidden=\"true\">×</span>\r\n </button>\r\n </div>\r\n\r\n <form class=\"form-group p-3 text-left\" [formGroup]=\"form\" (ngSubmit)=\"saveTableSettings()\">\r\n <div class=\"modal-body\">\r\n <p [translate]=\"'Tables.ColumnDisplayInfo'\"></p>\r\n <!-- <div *ngFor=\"let column of form.controls.columns.controls; let i = index\">\r\n <div formArrayName=\"columns\">\r\n <input class=\"checkbox checkbox-primary\" type=\"checkbox\" [formControlName]=\"i\" id=\"checkbox{{i}}\">\r\n <label for=\"checkbox{{i}}\">\r\n {{columns[i].name | translate}}\r\n </label>\r\n </div>\r\n </div> -->\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-light\" (click)=\"dismiss()\" [translate]=\"'Tables.Cancel'\"> </button>\r\n <button type=\"submit\" class=\"btn btn-success\" [translate]=\"'Tables.Save'\"></button>\r\n </div>\r\n </form>\r\n</ng-template>\r\n",
|
|
3115
|
+
encapsulation: ViewEncapsulation.None
|
|
3116
|
+
}] }
|
|
3117
|
+
];
|
|
3118
|
+
/** @nocollapse */
|
|
3119
|
+
AdvancedTableSettingsComponent.ctorParameters = () => [
|
|
3120
|
+
{ type: AngularDataContext },
|
|
3121
|
+
{ type: ActivatedRoute },
|
|
3122
|
+
{ type: ElementRef },
|
|
3123
|
+
{ type: BsModalService },
|
|
3124
|
+
{ type: UserStorageService },
|
|
3125
|
+
{ type: FormBuilder },
|
|
3126
|
+
{ type: ActivatedTableService }
|
|
3127
|
+
];
|
|
3128
|
+
AdvancedTableSettingsComponent.propDecorators = {
|
|
3129
|
+
table: [{ type: Input }],
|
|
3130
|
+
modal_settings_template: [{ type: ViewChild, args: ['modal_settings_template',] }]
|
|
3131
|
+
};
|
|
3132
|
+
|
|
3133
|
+
const TABLES_EL = {
|
|
3134
|
+
"Tables": {
|
|
3135
|
+
"Export": "Εξαγωγή",
|
|
3136
|
+
"More": "Περισσότερα",
|
|
3137
|
+
"Search": "Αναζήτηση",
|
|
3138
|
+
"SearchPlaceholder": "Αναζήτηση...",
|
|
3139
|
+
"GroupBy": "Ομαδοποίηση ανά",
|
|
3140
|
+
"DataTable": {
|
|
3141
|
+
"paginate": {
|
|
3142
|
+
"first": "Πρώτη σελίδα",
|
|
3143
|
+
"last": "Τελεταία σελίδα",
|
|
3144
|
+
"next": "Επόμενη",
|
|
3145
|
+
"previous": "Προηγούμενη"
|
|
3146
|
+
},
|
|
3147
|
+
"info": "Εμφάνιση _START_ έως _END_ από _TOTAL_ εγγραφές",
|
|
3148
|
+
"infoEmpty": "Δεν βρέθηκαν εγγραφές",
|
|
3149
|
+
"loading": "Εμφάνιση δεδομένων...",
|
|
3150
|
+
"processing": "Παραλαβή δεδομένων...",
|
|
3151
|
+
"emptyTable": "Δεν βρέθηκαν αποτελέσματα."
|
|
3152
|
+
},
|
|
3153
|
+
"ColumnDisplay": "Επιλογή Εμφάνισης στηλών",
|
|
3154
|
+
"ColumnDisplayInfo": "Επιλέξτε τις στήλες που θέλετε να εμφανίζονται.",
|
|
3155
|
+
"Save": "Εφαρμογή",
|
|
3156
|
+
"Apply": "Εφαρμογή",
|
|
3157
|
+
"Cancel": "Ακύρωση",
|
|
3158
|
+
"RecordsPerPage": "Εγγραφές ανά σελίδα",
|
|
3159
|
+
"PageInfo": "{{start}}-{{end}} από {{recordsTotal}}",
|
|
3160
|
+
"SearchListPlaceholder": "Αναζητήστε στη λίστα...",
|
|
3161
|
+
"Filters": "Φίλτρα",
|
|
3162
|
+
"All": "Όλα",
|
|
3163
|
+
"AddItem": "Προσθήκη στοιχείου",
|
|
3164
|
+
"EmptyTableTitle": "Δεν βρέθηκαν αποτελέσματα.",
|
|
3165
|
+
"EmptyTableMessage": "Προσπαθήστε να χρησιμοποιήσετε άλλες λέξεις κλειδιά.",
|
|
3166
|
+
"RemoveItem": "Διαγραφή στοιχείου",
|
|
3167
|
+
"Items": "στοιχεία",
|
|
3168
|
+
"Item": "στοιχείο",
|
|
3169
|
+
"AddItemMessage": {
|
|
3170
|
+
"title": "Προσθήκη στοιχείων",
|
|
3171
|
+
"one": "Ένα νέο στοιχείο προστέθηκε με επιτυχία.",
|
|
3172
|
+
"many": "{{value}} νέα στοιχεία προστέθηκαν με επιτυχία."
|
|
3173
|
+
},
|
|
3174
|
+
"RemoveItemMessage": {
|
|
3175
|
+
"title": "Αφαίρεση στοιχείων",
|
|
3176
|
+
"one": "Ένα στοιχείο αφαιρέθηκε με επιτυχία.",
|
|
3177
|
+
"many": "{{value}} στοιχεία αφαιρέθηκαν με επιτυχία."
|
|
3178
|
+
},
|
|
3179
|
+
"RemoveItemsTitle": "Διαγραφή στοιχείου",
|
|
3180
|
+
"RemoveItemsMessage": "Πρόκειται να διαγράψετε ένα ή περισσότερα στοιχεία. Θέλετε να προχωρήσετε?",
|
|
3181
|
+
"SelectAll": "Επιλογή όλων",
|
|
3182
|
+
"ClearSelection": "Αναίρεση επιλογής",
|
|
3183
|
+
"Actions": {
|
|
3184
|
+
"Label": "Επεξεργασία",
|
|
3185
|
+
"Start": "Έναρξη",
|
|
3186
|
+
"ActionMessage": {
|
|
3187
|
+
"None": "Δεν έχετε επιλέξει κανένα στοιχείο που να ακολουθεί τις προϋποθέσεις της ενέργειας.",
|
|
3188
|
+
"One": "Έχετε επιλέξει ένα στοιχείο που ακολουθεί τις προϋποθέσεις της ενέργειας. Πατήστε το πλήκτρο Έναρξη για να ξεκινήσει η διαδικασία.",
|
|
3189
|
+
"Many": "Έχετε επιλέξει {{length}} στοιχεία που ακολουθούν τις προϋποθέσεις της ενέργειας. Πατήστε το πλήκτρο Έναρξη για να ξεκινήσει η διαδικασία."
|
|
3190
|
+
},
|
|
3191
|
+
"CompletedWithErrors": {
|
|
3192
|
+
"Title": "Η διαδικασία ολοκληρώθηκε με σφάλματα.",
|
|
3193
|
+
"Description": {
|
|
3194
|
+
"One": "Ένα στοιχείο δεν ανανεώθηκε λόγω ενός σφάλματος που συνέβη κατά την εκτέλεση της διαδικασίας.",
|
|
3195
|
+
"Many": "{{errors}} στοιχεία δεν ανανεώθηκαν λόγω ενός σφάλματος που συνέβη κατά την εκτέλεση της διαδικασίας."
|
|
3196
|
+
}
|
|
3197
|
+
},
|
|
3198
|
+
"CompletedWithSuccess": {
|
|
3199
|
+
"Title": "Η διαδικασία ολοκληρώθηκε με επιτυχία."
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
},
|
|
3203
|
+
"TrueFalse": {
|
|
3204
|
+
"long": {
|
|
3205
|
+
"Yes": "Ναι",
|
|
3206
|
+
"No": "Όχι"
|
|
3207
|
+
}
|
|
3208
|
+
},
|
|
3209
|
+
"AdvancedSearch": {
|
|
3210
|
+
"Choose": "<Επιλέξτε μια αναζήτηση>",
|
|
3211
|
+
"ChooseDate": "Επιλογή ημερομηνίας",
|
|
3212
|
+
"SavedSearches": "Αποθηκευμένες αναζητήσεις",
|
|
3213
|
+
"ToggleDropdown": "Εμφάνιση λίστας",
|
|
3214
|
+
"SaveCurrentSearch": "Αποθήκευση αναζήτησης",
|
|
3215
|
+
"SaveAs": "Αποθήκευση ως",
|
|
3216
|
+
"Save": "Αποθήκευση",
|
|
3217
|
+
"Delete": "Διαγραφή",
|
|
3218
|
+
"More": "Περισσότερα",
|
|
3219
|
+
"Less": "Λιγότερα",
|
|
3220
|
+
"Modal": {
|
|
3221
|
+
"Title": "Αποθήκευση κριτηρίων αναζήτησης",
|
|
3222
|
+
"Help": "Ορίστε έναν τίτλο για τα επιλεγμένα κριτήρια αναζήτησης. Αυτός ο τίτλος θα σας βοηθήσει να τα χρησιμοποιήσετε ξανά στο μέλλον.",
|
|
3223
|
+
"TitleOnUserError": "Παρακαλώ εισάγετε έναν τίτλο",
|
|
3224
|
+
"TitleOnListAlreadyCreated": "Το όνομα αυτό χρησιμοποιείται ήδη. Διαλέξτε ένα διαφορετικό.",
|
|
3225
|
+
"NoListDefined": "Δεν έχει οριστεί το σημείο αποθήκευσης της λίστας",
|
|
3226
|
+
"EmptyCriteriaList": "Δεν έχετε καθορίσει κανένα κριτήριο για αποθήκευση",
|
|
3227
|
+
"SaveNameLabel": "Ονομασία λίστας",
|
|
3228
|
+
"SaveNamePlaceholder": "Εισάγετε έναν τίτλο",
|
|
3229
|
+
"Accept": "Αποθήκευση",
|
|
3230
|
+
"Decline": "Απόρριψη",
|
|
3231
|
+
"Close": "Κλείσιμο"
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
|
|
3236
|
+
const TABLES_EN = {
|
|
3237
|
+
"Tables": {
|
|
3238
|
+
"Export": "Export",
|
|
3239
|
+
"More": "More",
|
|
3240
|
+
"Search": "Search",
|
|
3241
|
+
"SearchPlaceholder": "Search...",
|
|
3242
|
+
"GroupBy": "Group by",
|
|
3243
|
+
"DataTable": {
|
|
3244
|
+
"paginate": {
|
|
3245
|
+
"first": "First",
|
|
3246
|
+
"last": "Last",
|
|
3247
|
+
"next": "Next",
|
|
3248
|
+
"previous": "Previous"
|
|
3249
|
+
},
|
|
3250
|
+
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
|
3251
|
+
"infoEmpty": "Showing 0 to 0 of 0 entries",
|
|
3252
|
+
"processing": "Processing data...",
|
|
3253
|
+
"emptyTable": "There are no matching records."
|
|
3254
|
+
},
|
|
3255
|
+
"ColumnDisplay": "Column Display",
|
|
3256
|
+
"ColumnDisplayInfo": "Επιλέξτε τις στήλες που θέλετε να εμφανίζονται.",
|
|
3257
|
+
"Save": "Save",
|
|
3258
|
+
"Apply": "Apply",
|
|
3259
|
+
"Cancel": "Cancel",
|
|
3260
|
+
"RecordsPerPage": "Items per page",
|
|
3261
|
+
"PageInfo": "{{start}}-{{end}} of {{recordsTotal}}",
|
|
3262
|
+
"SearchListPlaceHolder": "Search in list...",
|
|
3263
|
+
"Filters": "Filters",
|
|
3264
|
+
"All": "All",
|
|
3265
|
+
"AddItem": "Add item",
|
|
3266
|
+
"EmptyTableTitle": "There are no matching records.",
|
|
3267
|
+
"EmptyTableMessage": "Please try to use other keywords or search parameters.",
|
|
3268
|
+
"RemoveItem": "Remove item",
|
|
3269
|
+
"Items": "items",
|
|
3270
|
+
"Item": "item",
|
|
3271
|
+
"AddItemMessage": {
|
|
3272
|
+
"title": "Add item",
|
|
3273
|
+
"one": "A new item has been added successfully.",
|
|
3274
|
+
"many": "{{value}} new items have been added successfully."
|
|
3275
|
+
},
|
|
3276
|
+
"RemoveItemMessage": {
|
|
3277
|
+
"title": "Remove items",
|
|
3278
|
+
"one": "An item has been successfully removed.",
|
|
3279
|
+
"many": "{{value}} items have been successfully removed."
|
|
3280
|
+
},
|
|
3281
|
+
"RemoveItemsTitle": "Remove items",
|
|
3282
|
+
"RemoveItemsMessage": "You are going to remove one or more instructor courses. Do you want to proceed?",
|
|
3283
|
+
"SelectAll": "Select all",
|
|
3284
|
+
"ClearSelection": "Clear selection",
|
|
3285
|
+
"Actions": {
|
|
3286
|
+
"Label": "Edit",
|
|
3287
|
+
"Start": "Start",
|
|
3288
|
+
"ActionMessage": {
|
|
3289
|
+
"None": "There is no selected item that meets action requirements.",
|
|
3290
|
+
"One": "You have selected one item that meets action requirements.",
|
|
3291
|
+
"Many": "You have selected {{length}} items that meet action requirements."
|
|
3292
|
+
},
|
|
3293
|
+
"CompletedWithErrors": {
|
|
3294
|
+
"Title": "The operation was completed with errors.",
|
|
3295
|
+
"Description": {
|
|
3296
|
+
"One": "One item has not been updated due to an error occurred while executing process.",
|
|
3297
|
+
"Many": "{{errors}} items have not been updated due to an error occurred while executing process."
|
|
3298
|
+
}
|
|
3299
|
+
},
|
|
3300
|
+
"CompletedWithSuccess": {
|
|
3301
|
+
"Title": "The operation was completed successfully."
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
},
|
|
3305
|
+
"TrueFalse": {
|
|
3306
|
+
"long": {
|
|
3307
|
+
"Yes": "Yes",
|
|
3308
|
+
"No": "No"
|
|
3309
|
+
}
|
|
3310
|
+
},
|
|
3311
|
+
"AdvancedSearch": {
|
|
3312
|
+
"Choose": "<Select a saved search>",
|
|
3313
|
+
"ChooseDate": "Choose date",
|
|
3314
|
+
"SavedSearches": "Saved searches",
|
|
3315
|
+
"ToggleDropdown": "Toggle Dropdown",
|
|
3316
|
+
"SaveCurrentSearch": "Save search",
|
|
3317
|
+
"SaveAs": "Save as...",
|
|
3318
|
+
"Save": "Save",
|
|
3319
|
+
"Delete": "Remove",
|
|
3320
|
+
"More": "More",
|
|
3321
|
+
"Less": "Less",
|
|
3322
|
+
"Modal": {
|
|
3323
|
+
"Title": "Save current search criteria",
|
|
3324
|
+
"Help": "Please add a title for the given search criteria. This title will help you to use those criteria again in the future.",
|
|
3325
|
+
"TitleOnUserError": "Please use a valid name for your list",
|
|
3326
|
+
"TitleOnListAlreadyCreated": "Name already in use. Please choose a different one",
|
|
3327
|
+
"NoListDefined": "Cannot define user storage key.",
|
|
3328
|
+
"EmptyCriteriaList": "Please define search criteria before save.",
|
|
3329
|
+
"SaveNameLabel": "List name",
|
|
3330
|
+
"SaveNamePlaceholder": "Type a title here...",
|
|
3331
|
+
"Accept": "Save",
|
|
3332
|
+
"Decline": "Decline",
|
|
3333
|
+
"Close": "Close"
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
};
|
|
3337
|
+
|
|
3338
|
+
class SimpleHeaderToolsComponent {
|
|
3339
|
+
}
|
|
3340
|
+
SimpleHeaderToolsComponent.decorators = [
|
|
3341
|
+
{ type: Component, args: [{
|
|
3342
|
+
selector: 'table-simple-header-tools',
|
|
3343
|
+
template: `
|
|
3344
|
+
<ng-content></ng-content>
|
|
3345
|
+
`
|
|
3346
|
+
}] }
|
|
3347
|
+
];
|
|
3348
|
+
class TableSimpleHeaderComponent {
|
|
3349
|
+
constructor() { }
|
|
3350
|
+
ngOnChanges(changes) {
|
|
3351
|
+
if (changes.table) {
|
|
3352
|
+
if (changes.table.currentValue == null) {
|
|
3353
|
+
if (this.dataLoadSubscription) {
|
|
3354
|
+
this.dataLoadSubscription.unsubscribe();
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
if (changes.table.currentValue != changes.table.previousValue) {
|
|
3359
|
+
this.dataLoadSubscription = changes.table.currentValue.load.subscribe((data) => {
|
|
3360
|
+
this.recordsTotal = data.recordsTotal;
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
ngOnDestroy() {
|
|
3366
|
+
if (this.dataLoadSubscription) {
|
|
3367
|
+
this.dataLoadSubscription.unsubscribe();
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
ngOnInit() {
|
|
3371
|
+
//
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
TableSimpleHeaderComponent.decorators = [
|
|
3375
|
+
{ type: Component, args: [{
|
|
3376
|
+
selector: 'app-table-simple-header',
|
|
3377
|
+
template: "<div class=\"row\">\n <div class=\"col-auto mt-4\">\n <div class=\"font-xl text-secondary\" *ngIf=\"recordsTotal === 0\">\n <span> </span>\n </div>\n <div class=\"font-xl text-secondary\" *ngIf=\"recordsTotal > 1\">\n <span>{{recordsTotal}}</span><span> </span><span [translate]=\"'Tables.Items'\"></span>\n </div>\n <div class=\"font-xl text-secondary\" *ngIf=\"recordsTotal === 1\">\n <span>{{recordsTotal}}</span><span> </span><span [translate]=\"'Tables.Item'\"></span>\n </div>\n </div>\n <div class=\"pull-right ml-auto mr-3 mt-3\">\n <div class=\"font-lg btn-toolbar mt-2\">\n <ng-content select=\"table-simple-header-tools\"></ng-content>\n </div>\n </div>\n</div>\n\n"
|
|
3378
|
+
}] }
|
|
3379
|
+
];
|
|
3380
|
+
/** @nocollapse */
|
|
3381
|
+
TableSimpleHeaderComponent.ctorParameters = () => [];
|
|
3382
|
+
TableSimpleHeaderComponent.propDecorators = {
|
|
3383
|
+
table: [{ type: Input }]
|
|
3384
|
+
};
|
|
3385
|
+
|
|
3386
|
+
class AdvancedTableNotNullError extends Error {
|
|
3387
|
+
constructor() {
|
|
3388
|
+
super('Advanced table component cannot be empty at this context.');
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
class AdvancedTableEditorDirective {
|
|
3392
|
+
constructor(table) {
|
|
3393
|
+
this.table = table;
|
|
3394
|
+
this.dirty = false;
|
|
3395
|
+
this.dirtyChange = new EventEmitter(false);
|
|
3396
|
+
//
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* Add rows to table and mark them as added rows
|
|
3400
|
+
*/
|
|
3401
|
+
add(...addItems) {
|
|
3402
|
+
if (this.table == null) {
|
|
3403
|
+
throw new AdvancedTableNotNullError();
|
|
3404
|
+
}
|
|
3405
|
+
// decorate items
|
|
3406
|
+
const key = this.table.config.columns[0].property || this.table.config.columns[0].name;
|
|
3407
|
+
for (let index = 0; index < addItems.length; index++) {
|
|
3408
|
+
const element = addItems[index];
|
|
3409
|
+
let tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3410
|
+
if (tr.hasClass('removed')) {
|
|
3411
|
+
tr.toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3412
|
+
}
|
|
3413
|
+
else {
|
|
3414
|
+
this.table.dataTable.rows.add([element]);
|
|
3415
|
+
tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3416
|
+
tr.toggleClass('added font-weight-bold text-success');
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
this.table.dataTable.draw();
|
|
3420
|
+
// find removed
|
|
3421
|
+
const removedLength = this.table.dataTable.rows('.removed')[0].length;
|
|
3422
|
+
// find added
|
|
3423
|
+
const addedLength = this.table.dataTable.rows('.added')[0].length;
|
|
3424
|
+
// and emit event
|
|
3425
|
+
const items = this.table.dataTable.rows().data();
|
|
3426
|
+
this.table.load.emit({
|
|
3427
|
+
data: items,
|
|
3428
|
+
recordsFiltered: items.length - removedLength,
|
|
3429
|
+
recordsTotal: items.length - removedLength
|
|
3430
|
+
});
|
|
3431
|
+
this.dirty = (removedLength > 0 || addedLength > 0);
|
|
3432
|
+
this.dirtyChange.emit(this.dirty);
|
|
3433
|
+
}
|
|
3434
|
+
/**
|
|
3435
|
+
* Marks rows as removed rows
|
|
3436
|
+
*/
|
|
3437
|
+
remove(...removeItems) {
|
|
3438
|
+
if (this.table == null) {
|
|
3439
|
+
throw new AdvancedTableNotNullError();
|
|
3440
|
+
}
|
|
3441
|
+
// get key
|
|
3442
|
+
const key = this.table.config.columns[0].property || this.table.config.columns[0].name;
|
|
3443
|
+
// loop items
|
|
3444
|
+
for (let index = 0; index < removeItems.length; index++) {
|
|
3445
|
+
const element = removeItems[index];
|
|
3446
|
+
const tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3447
|
+
// if row is marked as added
|
|
3448
|
+
if (tr.hasClass('added')) {
|
|
3449
|
+
// remove this dirty row
|
|
3450
|
+
this.table.dataTable.rows(tr).remove();
|
|
3451
|
+
}
|
|
3452
|
+
else {
|
|
3453
|
+
// otherwise unmarked row
|
|
3454
|
+
tr.toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3455
|
+
}
|
|
3456
|
+
// check if item is selected
|
|
3457
|
+
const selectedIndex = this.table.selected.findIndex((item) => {
|
|
3458
|
+
return item[key] === element[key];
|
|
3459
|
+
});
|
|
3460
|
+
// and remove it from selected collection
|
|
3461
|
+
if (selectedIndex >= 0) {
|
|
3462
|
+
this.table.selected.splice(selectedIndex, 1);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
// re-draw table
|
|
3466
|
+
this.table.dataTable.draw();
|
|
3467
|
+
// get number of table items
|
|
3468
|
+
const items = this.table.dataTable.rows().data();
|
|
3469
|
+
// get number of removed items
|
|
3470
|
+
const removedLength = this.table.dataTable.rows('.removed')[0].length;
|
|
3471
|
+
// get number of added items
|
|
3472
|
+
const addedLength = this.table.dataTable.rows('.added')[0].length;
|
|
3473
|
+
// refresh
|
|
3474
|
+
this.table.load.emit({
|
|
3475
|
+
data: items,
|
|
3476
|
+
recordsFiltered: items.length - removedLength,
|
|
3477
|
+
recordsTotal: items.length - removedLength
|
|
3478
|
+
});
|
|
3479
|
+
this.dirty = (removedLength > 0 || addedLength > 0);
|
|
3480
|
+
this.dirtyChange.emit(this.dirty);
|
|
3481
|
+
}
|
|
3482
|
+
set(items) {
|
|
3483
|
+
if (this.table == null) {
|
|
3484
|
+
throw new AdvancedTableNotNullError();
|
|
3485
|
+
}
|
|
3486
|
+
// clear rows
|
|
3487
|
+
this.table.selected = [];
|
|
3488
|
+
this.table.dataTable.clear();
|
|
3489
|
+
// set items
|
|
3490
|
+
this.table.dataTable.rows.add(items).draw();
|
|
3491
|
+
this.table.load.emit({
|
|
3492
|
+
data: items,
|
|
3493
|
+
recordsFiltered: items.length,
|
|
3494
|
+
recordsTotal: items.length
|
|
3495
|
+
});
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Undo changes
|
|
3499
|
+
*/
|
|
3500
|
+
undo() {
|
|
3501
|
+
if (this.table == null) {
|
|
3502
|
+
throw new AdvancedTableNotNullError();
|
|
3503
|
+
}
|
|
3504
|
+
// reset removed rows
|
|
3505
|
+
this.table.dataTable.rows('.removed').nodes().to$().toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3506
|
+
// remove added rows
|
|
3507
|
+
this.table.dataTable.rows('.added').remove();
|
|
3508
|
+
// clear selected items
|
|
3509
|
+
this.table.selected = [];
|
|
3510
|
+
const items = this.table.dataTable.rows().data();
|
|
3511
|
+
this.table.dataTable.draw();
|
|
3512
|
+
this.table.load.emit({
|
|
3513
|
+
data: items,
|
|
3514
|
+
recordsFiltered: items.length,
|
|
3515
|
+
recordsTotal: items.length
|
|
3516
|
+
});
|
|
3517
|
+
this.dirty = false;
|
|
3518
|
+
this.dirtyChange.emit(this.dirty);
|
|
3519
|
+
}
|
|
3520
|
+
rows() {
|
|
3521
|
+
if (this.table == null) {
|
|
3522
|
+
throw new AdvancedTableNotNullError();
|
|
3523
|
+
}
|
|
3524
|
+
return Array.from(this.table.dataTable.rows(':not(.removed)').data());
|
|
3525
|
+
}
|
|
3526
|
+
}
|
|
3527
|
+
AdvancedTableEditorDirective.decorators = [
|
|
3528
|
+
{ type: Directive, args: [{
|
|
3529
|
+
selector: '[tableEditor]'
|
|
3530
|
+
},] }
|
|
3531
|
+
];
|
|
3532
|
+
/** @nocollapse */
|
|
3533
|
+
AdvancedTableEditorDirective.ctorParameters = () => [
|
|
3534
|
+
{ type: AdvancedTableComponent }
|
|
3535
|
+
];
|
|
3536
|
+
AdvancedTableEditorDirective.propDecorators = {
|
|
3537
|
+
dirtyChange: [{ type: Output }]
|
|
3538
|
+
};
|
|
3539
|
+
|
|
3540
|
+
class AdvancedAggregatorService {
|
|
3541
|
+
constructor(_context, _template) {
|
|
3542
|
+
this._context = _context;
|
|
3543
|
+
this._template = _template;
|
|
3544
|
+
}
|
|
3545
|
+
/**
|
|
3546
|
+
*
|
|
3547
|
+
* Formats and executes the data query
|
|
3548
|
+
*
|
|
3549
|
+
* @param {string} model The target model
|
|
3550
|
+
* @param {object} fixedParams Pairs of parameters to always be applied
|
|
3551
|
+
* @param {string} queryStatement The default query to use for fetching the data (e.g. expands etc)
|
|
3552
|
+
* @param {Array<string>} filters A list of filter expressions to append at the query
|
|
3553
|
+
* @param {string} searchTemplate The template to use for searching expressions
|
|
3554
|
+
* @param {string} searchValue The value of the user search
|
|
3555
|
+
*
|
|
3556
|
+
* @returns {Promise<any>} The response of the query execution
|
|
3557
|
+
*/
|
|
3558
|
+
fetchData(model, fixedParams, queryStatement = '', filters, searchTemplate, searchValue) {
|
|
3559
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3560
|
+
const query = this._context.model(model).asQueryable();
|
|
3561
|
+
if (!fixedParams || typeof fixedParams !== 'object') {
|
|
3562
|
+
fixedParams = {};
|
|
3563
|
+
}
|
|
3564
|
+
let filterExpressions = [];
|
|
3565
|
+
if (fixedParams) {
|
|
3566
|
+
for (let param of Object.keys(fixedParams)) {
|
|
3567
|
+
filterExpressions.push(`${param} eq ${fixedParams[param]}`);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
if (filters && Array.isArray(filters)) {
|
|
3571
|
+
filterExpressions = [
|
|
3572
|
+
...filterExpressions,
|
|
3573
|
+
...filters
|
|
3574
|
+
];
|
|
3575
|
+
}
|
|
3576
|
+
if (searchTemplate && searchValue) {
|
|
3577
|
+
let evaluatedSearch;
|
|
3578
|
+
if (/^"(.*?)"$/.test(searchValue)) { // validate search text in double quotes
|
|
3579
|
+
evaluatedSearch = this._template.transform(searchTemplate, Object.assign({}, fixedParams, { text: searchValue.replace(/^"|"$/g, '') }));
|
|
3580
|
+
}
|
|
3581
|
+
else {
|
|
3582
|
+
// try to split words
|
|
3583
|
+
const words = searchValue.split(' ');
|
|
3584
|
+
// join words to a single filter
|
|
3585
|
+
evaluatedSearch = words.map(word => {
|
|
3586
|
+
return this._template.transform(searchTemplate, Object.assign({}, fixedParams, { text: word }));
|
|
3587
|
+
}).join(' and ');
|
|
3588
|
+
}
|
|
3589
|
+
filterExpressions.push(evaluatedSearch);
|
|
3590
|
+
}
|
|
3591
|
+
query.setParam('$filter', filterExpressions.join(' and '));
|
|
3592
|
+
return query.expand(queryStatement).take(-1).getItems();
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
/**
|
|
3596
|
+
*
|
|
3597
|
+
* Formats the received data to use flat keys
|
|
3598
|
+
*
|
|
3599
|
+
* @param config The applied configuration
|
|
3600
|
+
* @param data The raw data
|
|
3601
|
+
*
|
|
3602
|
+
* @returns {Array<any>} The flat-key object list
|
|
3603
|
+
*
|
|
3604
|
+
*/
|
|
3605
|
+
formatData(config, data) {
|
|
3606
|
+
try {
|
|
3607
|
+
return data.map((entry) => {
|
|
3608
|
+
let entryObject = {};
|
|
3609
|
+
for (let i = 0; i < config.columns.length; i++) {
|
|
3610
|
+
const keys = config.columns[i].name.split('/');
|
|
3611
|
+
let helperObject = entry[keys[0]];
|
|
3612
|
+
if (helperObject && keys.length > 1) {
|
|
3613
|
+
for (let i = 1; i < keys.length; i++) {
|
|
3614
|
+
if (helperObject === undefined || helperObject === null) {
|
|
3615
|
+
break;
|
|
3616
|
+
}
|
|
3617
|
+
else {
|
|
3618
|
+
helperObject = helperObject[keys[i]] !== undefined && helperObject[keys[i]] !== null
|
|
3619
|
+
? helperObject[keys[i]]
|
|
3620
|
+
: undefined;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
entryObject[config.columns[i].name] = helperObject;
|
|
3625
|
+
}
|
|
3626
|
+
if (entry.subitems) {
|
|
3627
|
+
entryObject.subitems = entry.subitems;
|
|
3628
|
+
}
|
|
3629
|
+
return entryObject;
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3632
|
+
catch (err) {
|
|
3633
|
+
console.error(err);
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
AdvancedAggregatorService.decorators = [
|
|
3638
|
+
{ type: Injectable, args: [{
|
|
3639
|
+
providedIn: 'root'
|
|
3640
|
+
},] }
|
|
3641
|
+
];
|
|
3642
|
+
/** @nocollapse */
|
|
3643
|
+
AdvancedAggregatorService.ctorParameters = () => [
|
|
3644
|
+
{ type: AngularDataContext },
|
|
3645
|
+
{ type: TemplatePipe }
|
|
3646
|
+
];
|
|
3647
|
+
AdvancedAggregatorService.ngInjectableDef = defineInjectable({ factory: function AdvancedAggregatorService_Factory() { return new AdvancedAggregatorService(inject(AngularDataContext), inject(TemplatePipe)); }, token: AdvancedAggregatorService, providedIn: "root" });
|
|
3648
|
+
|
|
3649
|
+
/**
|
|
3650
|
+
*
|
|
3651
|
+
* @class AdvancedAggregatorComponent
|
|
3652
|
+
* @classdesc An advanced list with the capability of aggregate data based on configurable filters
|
|
3653
|
+
*
|
|
3654
|
+
*/
|
|
3655
|
+
class AdvancedAggregatorComponent {
|
|
3656
|
+
constructor(_changeDetector, _advancedAggregatorService, _loadingService) {
|
|
3657
|
+
this._changeDetector = _changeDetector;
|
|
3658
|
+
this._advancedAggregatorService = _advancedAggregatorService;
|
|
3659
|
+
this._loadingService = _loadingService;
|
|
3660
|
+
this.groups = [];
|
|
3661
|
+
this.eventHandler = new EventEmitter();
|
|
3662
|
+
}
|
|
3663
|
+
set config(value) {
|
|
3664
|
+
this._config = value;
|
|
3665
|
+
}
|
|
3666
|
+
set aggregators(value) {
|
|
3667
|
+
this._aggregators = value;
|
|
3668
|
+
}
|
|
3669
|
+
set postProcessors(value) {
|
|
3670
|
+
this._postProcessors = value;
|
|
3671
|
+
}
|
|
3672
|
+
get config() {
|
|
3673
|
+
return this._config;
|
|
3674
|
+
}
|
|
3675
|
+
get aggregators() {
|
|
3676
|
+
return this._aggregators;
|
|
3677
|
+
}
|
|
3678
|
+
get postProcessors() {
|
|
3679
|
+
return this._postProcessors;
|
|
3680
|
+
}
|
|
3681
|
+
ngOnInit() {
|
|
3682
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3683
|
+
this.selectedGroups$.subscribe((data) => {
|
|
3684
|
+
this.groups = data;
|
|
3685
|
+
this._changeDetector.markForCheck();
|
|
3686
|
+
});
|
|
3687
|
+
this.filters$.subscribe((filters) => __awaiter(this, void 0, void 0, function* () {
|
|
3688
|
+
try {
|
|
3689
|
+
yield this.fetch(filters);
|
|
3690
|
+
}
|
|
3691
|
+
catch (err) {
|
|
3692
|
+
console.error(err);
|
|
3693
|
+
}
|
|
3694
|
+
}));
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
3697
|
+
ngOnDestroy() {
|
|
3698
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3699
|
+
this.selectedGroups$.unsubscribe();
|
|
3700
|
+
this.filters$.unsubscribe();
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
/**
|
|
3704
|
+
*
|
|
3705
|
+
* Retrieves the data
|
|
3706
|
+
*
|
|
3707
|
+
*/
|
|
3708
|
+
fetch(filters) {
|
|
3709
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3710
|
+
try {
|
|
3711
|
+
this._loadingService.showLoading();
|
|
3712
|
+
let data = yield this._advancedAggregatorService.fetchData(this.config.model, this.params, this.config.query, filters.filters, this.searchExpression, filters.text);
|
|
3713
|
+
if (this.postProcessors && Object.keys(this.postProcessors).length > 0 && this.config.postProcessors && this.config.postProcessors.length > 0) {
|
|
3714
|
+
for (let i = 0; i < this.config.postProcessors.length; i++) {
|
|
3715
|
+
data = yield this.postProcessors[this.config.postProcessors[i].name](data);
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
this.body = this._advancedAggregatorService.formatData(this.config, data);
|
|
3719
|
+
this._changeDetector.markForCheck();
|
|
3720
|
+
}
|
|
3721
|
+
catch (err) {
|
|
3722
|
+
console.error(err);
|
|
3723
|
+
}
|
|
3724
|
+
finally {
|
|
3725
|
+
this._loadingService.hideLoading();
|
|
3726
|
+
}
|
|
3727
|
+
});
|
|
3728
|
+
}
|
|
3729
|
+
/**
|
|
3730
|
+
*
|
|
3731
|
+
* Triggers the event handler event
|
|
3732
|
+
*
|
|
3733
|
+
* @param {*} event The event data
|
|
3734
|
+
*
|
|
3735
|
+
*/
|
|
3736
|
+
onActionEvent(event) {
|
|
3737
|
+
this.eventHandler.emit(event);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
AdvancedAggregatorComponent.decorators = [
|
|
3741
|
+
{ type: Component, args: [{
|
|
3742
|
+
selector: 'app-advanced-aggregator',
|
|
3743
|
+
template: "<ng-container *ngIf=\"config && body\">\n <div class=\"w-100 text-center\" *ngIf=\"body && body.length === 0\">\n <div>\n <div class=\"mt-4\">\n <div class=\"icon-circle bg-gray-300 border-gray-100\"></div>\n </div>\n <div class=\"mt-4\">\n <h4 class=\"font-3xl text-dark\" [translate]=\"'Tables.EmptyTableTitle'\"></h4>\n <p class=\"font-lg text-dark\" [translate]=\"'Tables.EmptyTableMessage'\"></p>\n </div>\n </div>\n </div>\n <ng-container *ngIf=\"groups.length === 0\">\n <div class=\"row mt-4\">\n <app-advanced-aggregator-list [body]=\"body\" [config]=\"config\" (clickEvent)=\"onActionEvent($event)\" class=\"col-12\">\n </app-advanced-aggregator-list>\n <div *ngIf=\"config.aggregators && config.aggregators.length > 0\"\n class=\"footer-text text-dark font-weight-normal text-right font-sm mt-3 mr-2 w-100\">\n <div *ngFor=\"let aggregator of config.aggregators\">\n <span *ngIf=\"aggregator.label\"> {{ aggregator.label | translate }} </span>:\n <span class=\"font-weight-bold\"> {{ body | callAggregator: aggregators: aggregator.name }}\n </span>\n </div>\n </div>\n </div>\n </ng-container>\n <ng-container *ngIf=\"groups.length === 1\">\n <ng-container *ngFor=\"let group of body | groupBy: groups[0].name | keyvalue\">\n <div class=\"row mt-4\">\n <h4 class=\"col text-secondary\"> {{ groups[0].title | aggregatorListTitle: group.key }} </h4>\n <app-advanced-aggregator-list [body]=\"group.value\" [config]=\"config\" (clickEvent)=\"onActionEvent($event)\"\n class=\"col-12\">\n </app-advanced-aggregator-list>\n </div>\n\n <!-- Footer for the group -->\n <div *ngIf=\"config.aggregators && config.aggregators.length > 0\"\n class=\"footer-text text-dark font-weight-normal text-right font-sm mt-3 mr-2 w-100\">\n <div *ngFor=\"let aggregator of config.aggregators\">\n <span *ngIf=\"aggregator.label\"> {{ aggregator.label | translate }} </span>:\n <span class=\"font-weight-bold\"> {{ group.value | callAggregator: aggregators: aggregator.name }} </span>\n </div>\n </div>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"groups.length > 1\">\n <ng-container *ngFor=\"let group of body | groupBy: groups[0].name | keyvalue\">\n <div class=\"row mt-4\">\n <h4 class=\"col text-secondary\"> {{ groups[0].title | aggregatorListTitle: group.key }} </h4>\n <div *ngFor=\"let subgroup of group.value | groupBy: groups[1].name | keyvalue\" class=\"col-12 ml-4 pl-4\">\n <div class=\"row mt-4\">\n <h6 class=\"col-12 text-secondary\"> {{ groups[1].title | aggregatorListTitle: subgroup.key }} </h6>\n <app-advanced-aggregator-list [body]=\"subgroup.value\" [config]=\"config\" (clickEvent)=\"onActionEvent($event)\"\n class=\"col-12\">\n </app-advanced-aggregator-list>\n </div>\n\n <!-- Footer for each subgroup -->\n <div *ngIf=\"config.aggregators && config.aggregators.length > 0\"\n class=\"footer-text text-dark font-weight-normal text-right font-sm mt-3 mr-2 w-100\">\n <div *ngFor=\"let aggregator of config.aggregators\">\n <span *ngIf=\"aggregator.label\"> {{ aggregator.label | translate }} </span>:\n <span class=\"font-weight-bold\"> {{ subgroup.value | callAggregator: aggregators: aggregator.name }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n </ng-container>\n</ng-container>",
|
|
3744
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
3745
|
+
}] }
|
|
3746
|
+
];
|
|
3747
|
+
/** @nocollapse */
|
|
3748
|
+
AdvancedAggregatorComponent.ctorParameters = () => [
|
|
3749
|
+
{ type: ChangeDetectorRef },
|
|
3750
|
+
{ type: AdvancedAggregatorService },
|
|
3751
|
+
{ type: LoadingService }
|
|
3752
|
+
];
|
|
3753
|
+
AdvancedAggregatorComponent.propDecorators = {
|
|
3754
|
+
config: [{ type: Input, args: ['config',] }],
|
|
3755
|
+
aggregators: [{ type: Input, args: ['aggregators',] }],
|
|
3756
|
+
postProcessors: [{ type: Input, args: ['postProcessors',] }],
|
|
3757
|
+
params: [{ type: Input, args: ['params',] }],
|
|
3758
|
+
selectedGroups$: [{ type: Input, args: ['selectedGroups',] }],
|
|
3759
|
+
filters$: [{ type: Input, args: ['filters',] }],
|
|
3760
|
+
searchExpression: [{ type: Input, args: ['searchExpression',] }],
|
|
3761
|
+
eventHandler: [{ type: Output }]
|
|
3762
|
+
};
|
|
3763
|
+
|
|
3764
|
+
var Lengths;
|
|
3765
|
+
(function (Lengths) {
|
|
3766
|
+
Lengths["XL"] = "xl";
|
|
3767
|
+
Lengths["LG"] = "lg";
|
|
3768
|
+
Lengths["MD"] = "md";
|
|
3769
|
+
Lengths["SM"] = "sm";
|
|
3770
|
+
Lengths["XS"] = "xs";
|
|
3771
|
+
})(Lengths || (Lengths = {}));
|
|
3772
|
+
class AdvancedAggregatorListComponent {
|
|
3773
|
+
constructor(_template) {
|
|
3774
|
+
this._template = _template;
|
|
3775
|
+
this.clickEvent = new EventEmitter();
|
|
3776
|
+
}
|
|
3777
|
+
ngOnInit() {
|
|
3778
|
+
this.config.columns.forEach(x => {
|
|
3779
|
+
x.length = Object.values(Lengths).includes(x.length) ? this._template.transform(`sis--list-group-item_${x.length}_column`, x) :
|
|
3780
|
+
!x.length ? 'sis--list-group-item_sm_column' : x.length;
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
/**
|
|
3784
|
+
*
|
|
3785
|
+
* Triggers the output event emitter
|
|
3786
|
+
*
|
|
3787
|
+
* @param type The type of the event as it's declared in the configuration
|
|
3788
|
+
* @param item The value of the current item
|
|
3789
|
+
* }
|
|
3790
|
+
*/
|
|
3791
|
+
callCallback(type, data) {
|
|
3792
|
+
this.clickEvent.emit({
|
|
3793
|
+
type,
|
|
3794
|
+
data
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
AdvancedAggregatorListComponent.decorators = [
|
|
3799
|
+
{ type: Component, args: [{
|
|
3800
|
+
selector: 'app-advanced-aggregator-list',
|
|
3801
|
+
template: "<div id=\"group-list\" class=\"flex-column\" *ngIf=\"config && config.columns\">\n <ul class=\"sis--list-group sis--list-group-header\">\n <li class=\"sis--list-group-item text-wrap\">\n <div class=\"d-flex justify-content-center align-items-center\" *ngIf=\"body && body.length > 0\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column\"></div>\n <div *ngFor=\"let column of config.columns; let i = index\" class=\"text-left\"\n [class]=\"config.columns[i].length\"\n [translate]=\"config.columns[i].type === 'action' ? '' : column.title\" [ngClass]=\"{\n 'd-none': config.columns[i].hidden\n }\"></div>\n </div>\n </li>\n </ul>\n\n <!-- The list of entries -->\n <ul class=\"sis--list-group sis--list-group-body\" *ngIf=\"body && body.length > 0\">\n <li class=\"sis--list-group-item text-wrap\" *ngFor=\"let entry of body | orderBy: config.orderBy\">\n <div class=\"d-flex justify-content-center align-items-center list-color\">\n <!-- The list of actions -->\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, entry)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i [class]=\"'fas text-indigo ml-2 ' + 'fa-' + action.icon\"></i>\n </button>\n </div>\n <!-- The list of data -->\n <div *ngFor=\"let column of config.columns; let i=index\" [class]=\"config.columns[i].length\" class=\"text-left\"\n [ngClass]=\"{\n 'text-truncate': config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate,\n 'd-none': config.columns[i].hidden,\n 'd-md-block': !config.columns[i].hidden\n }\"\n [title]=\"config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate ? entry[column.name] : ''\">\n <span class=\"text-secondary\"\n *ngIf=\"!config.columns[i].hasOwnProperty('type') || (config.columns[i].hasOwnProperty('type') && config.columns[i].type === 'text')\"\n [ngClass]=\"{\n 'font-xs': config.columns[i].variant !== 'primary',\n 'text-body': config.columns[i].variant === 'primary',\n 'text-success': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && entry[config.columns[i].condition],\n 'text-danger': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && !entry[config.columns[i].condition],\n 'font-lg': config.columns[i].variant === 'redGreen' && entry[column.name] && entry[column.name].length > 3,\n 'font-2xl': config.columns[i].variant === 'redGreen' && entry[column.name] && entry[column.name].length <= 3\n }\">\n {{entry[column.name] || '-'}}\n </span>\n\n <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': entry[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': entry[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"entry[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant === 'checkOrDash'\"\n class=\"text-secondary font-xs\">-</span>\n </span>\n </div>\n </div>\n <ul *ngIf=\"entry && entry.subitems\">\n <li *ngFor=\"let subitem of entry.subitems\" class=\"d-flex justify-content-center align-items-center list-color\">\n <div class=\"text-left\"></div>\n <div *ngFor=\"let column of config.columns; let i=index\" [class]=\"config.column[i].length\" class=\"text-left\"\n [ngClass]=\"{\n 'text-truncate': config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate,\n 'd-none': config.columns[i].hidden,\n 'd-md-block': !config.columns[i].hidden\n }\"\n [title]=\"config.columns[i].hasOwnProperty('truncate') && config.columns[i].truncate ? entry[column.name] : ''\">\n <span class=\"text-secondary\"\n *ngIf=\"!config.columns[i].hasOwnProperty('type') || (config.columns[i].hasOwnProperty('type') && config.columns[i].type === 'text')\"\n [ngClass]=\"{\n 'font-xs': config.columns[i].variant !== 'primary',\n 'text-body': config.columns[i].variant === 'primary',\n 'text-success': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && subitem[config.columns[i].condition],\n 'text-danger': config.columns[i].variant === 'redGreen' && config.columns[i].hasOwnProperty('condition') && !subitem[config.columns[i].condition],\n 'font-lg': config.columns[i].variant === 'redGreen' && subitem[column.name] && subitem[column.name].length > 3,\n 'font-2xl': config.columns[i].variant === 'redGreen' && subitem[column.name] && subitem[column.name].length <= 3\n }\">\n {{subitem[column.name] || '-'}}\n </span>\n </div>\n </li>\n </ul>\n </li>\n </ul>\n</div>\n"
|
|
3802
|
+
}] }
|
|
3803
|
+
];
|
|
3804
|
+
/** @nocollapse */
|
|
3805
|
+
AdvancedAggregatorListComponent.ctorParameters = () => [
|
|
3806
|
+
{ type: TemplatePipe }
|
|
3807
|
+
];
|
|
3808
|
+
AdvancedAggregatorListComponent.propDecorators = {
|
|
3809
|
+
config: [{ type: Input }],
|
|
3810
|
+
body: [{ type: Input }],
|
|
3811
|
+
clickEvent: [{ type: Output }]
|
|
3812
|
+
};
|
|
3813
|
+
|
|
3814
|
+
/**
|
|
3815
|
+
*
|
|
3816
|
+
* @class AdvancedAggregatorPipe
|
|
3817
|
+
* @classdesc Given a list of items runs a user defined function against that list
|
|
3818
|
+
*
|
|
3819
|
+
*/
|
|
3820
|
+
class AdvancedAggregatorPipe {
|
|
3821
|
+
/**
|
|
3822
|
+
*
|
|
3823
|
+
* @override
|
|
3824
|
+
*
|
|
3825
|
+
* @param items The list of items to run the aggregator against
|
|
3826
|
+
* @param functionPool The functions that are available from the list caller
|
|
3827
|
+
* @param functionName The name of the function to be called
|
|
3828
|
+
*
|
|
3829
|
+
* @returns {string} The value to show at the UI
|
|
3830
|
+
*
|
|
3831
|
+
*/
|
|
3832
|
+
transform(items, functionPool, functionName) {
|
|
3833
|
+
if (Array.isArray(items) && functionName && functionName in functionPool) {
|
|
3834
|
+
return functionPool[functionName](items);
|
|
3835
|
+
}
|
|
3836
|
+
else {
|
|
3837
|
+
return '-';
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
AdvancedAggregatorPipe.decorators = [
|
|
3842
|
+
{ type: Pipe, args: [{
|
|
3843
|
+
name: 'callAggregator'
|
|
3844
|
+
},] }
|
|
3845
|
+
];
|
|
3846
|
+
|
|
3847
|
+
/**
|
|
3848
|
+
*
|
|
3849
|
+
* @class ParseTitlePipe
|
|
3850
|
+
* @classdesc Given a title of an aggregator list, translates and evaluates the parameter if any
|
|
3851
|
+
*
|
|
3852
|
+
*/
|
|
3853
|
+
class ParseTitlePipe {
|
|
3854
|
+
constructor(_translateService) {
|
|
3855
|
+
this._translateService = _translateService;
|
|
3856
|
+
}
|
|
3857
|
+
/**
|
|
3858
|
+
*
|
|
3859
|
+
* @override
|
|
3860
|
+
*
|
|
3861
|
+
* @param {string} title The title of the list
|
|
3862
|
+
* @param {string} key The current list key that is being used
|
|
3863
|
+
*
|
|
3864
|
+
*/
|
|
3865
|
+
transform(title, key) {
|
|
3866
|
+
if (!title) {
|
|
3867
|
+
return '';
|
|
3868
|
+
}
|
|
3869
|
+
else {
|
|
3870
|
+
if (key === undefined || key === null || key === 'undefined' || key === 'null') {
|
|
3871
|
+
key = this._translateService.instant('Tables.NotDefined');
|
|
3872
|
+
}
|
|
3873
|
+
return title.split(' ')
|
|
3874
|
+
.map((part) => this._translateService.instant(part))
|
|
3875
|
+
.map((part) => part.replace('${param}', key))
|
|
3876
|
+
.join(' ');
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
ParseTitlePipe.decorators = [
|
|
3881
|
+
{ type: Pipe, args: [{
|
|
3882
|
+
name: 'aggregatorListTitle'
|
|
3883
|
+
},] }
|
|
3884
|
+
];
|
|
3885
|
+
/** @nocollapse */
|
|
3886
|
+
ParseTitlePipe.ctorParameters = () => [
|
|
3887
|
+
{ type: TranslateService }
|
|
3888
|
+
];
|
|
3889
|
+
|
|
3890
|
+
class AddItemsComponent extends RouterModalOkCancel {
|
|
3891
|
+
constructor(router, activatedRoute, _errorService, _modalService, _appEvent, _toastService, _loadingService, _translateService, _context) {
|
|
3892
|
+
super(router, activatedRoute);
|
|
3893
|
+
this._errorService = _errorService;
|
|
3894
|
+
this._modalService = _modalService;
|
|
3895
|
+
this._appEvent = _appEvent;
|
|
3896
|
+
this._toastService = _toastService;
|
|
3897
|
+
this._loadingService = _loadingService;
|
|
3898
|
+
this._translateService = _translateService;
|
|
3899
|
+
this._context = _context;
|
|
3900
|
+
this.loading = false;
|
|
3901
|
+
this.items = [];
|
|
3902
|
+
this.messages = [];
|
|
3903
|
+
// set modal size
|
|
3904
|
+
this.modalClass = 'modal-lg';
|
|
3905
|
+
this.okButtonDisabled = true;
|
|
3906
|
+
}
|
|
3907
|
+
ngOnInit() {
|
|
3908
|
+
this.selectedSubscription = this.selectComponent.advancedTable.selectedItems.subscribe((items) => {
|
|
3909
|
+
if (this.formName == null) {
|
|
3910
|
+
this.okButtonDisabled = items.length === 0;
|
|
3911
|
+
}
|
|
3912
|
+
});
|
|
3913
|
+
if (this.formName != null) {
|
|
3914
|
+
this.formChangeSubscription = this.formComponent.form.change.subscribe((event) => {
|
|
3915
|
+
if (Object.prototype.hasOwnProperty.call(event, 'isValid')) {
|
|
3916
|
+
// enable or disable button based on form status
|
|
3917
|
+
this.okButtonDisabled = (!event.isValid);
|
|
3918
|
+
}
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
if (this.target) {
|
|
3922
|
+
const target = this.target;
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
cancel() {
|
|
3926
|
+
this.clearMessages();
|
|
3927
|
+
if (this.loading) {
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
// close
|
|
3931
|
+
if (this._modalService.modalRef) {
|
|
3932
|
+
return this._modalService.modalRef.hide();
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
ngOnDestroy() {
|
|
3936
|
+
if (this.formLoadSubscription) {
|
|
3937
|
+
this.formLoadSubscription.unsubscribe();
|
|
3938
|
+
}
|
|
3939
|
+
if (this.formChangeSubscription) {
|
|
3940
|
+
this.formChangeSubscription.unsubscribe();
|
|
3941
|
+
}
|
|
3942
|
+
if (this.selectedSubscription) {
|
|
3943
|
+
this.selectedSubscription.unsubscribe();
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
get formData() {
|
|
3947
|
+
if (this.formComponent === null) {
|
|
3948
|
+
return null;
|
|
3949
|
+
}
|
|
3950
|
+
return this.formComponent.form.formio.data;
|
|
3951
|
+
}
|
|
3952
|
+
ok() {
|
|
3953
|
+
try {
|
|
3954
|
+
this.clearMessages();
|
|
3955
|
+
return new Promise((resolve, reject) => {
|
|
3956
|
+
this.loading = false;
|
|
3957
|
+
this.lastError = null;
|
|
3958
|
+
// set courses
|
|
3959
|
+
this.items = this.selectComponent.advancedTable.selected;
|
|
3960
|
+
// execute add
|
|
3961
|
+
this.executeSubscription = this.execute.subscribe((result) => {
|
|
3962
|
+
this.loading = false;
|
|
3963
|
+
if (this.executeSubscription) {
|
|
3964
|
+
this.executeSubscription.unsubscribe();
|
|
3965
|
+
}
|
|
3966
|
+
// close modal
|
|
3967
|
+
if (this._modalService.modalRef) {
|
|
3968
|
+
this._modalService.modalRef.hide();
|
|
3969
|
+
}
|
|
3970
|
+
return resolve();
|
|
3971
|
+
}, (err) => {
|
|
3972
|
+
this.loading = false;
|
|
3973
|
+
// ensure that loading is hidden
|
|
3974
|
+
this._loadingService.hideLoading();
|
|
3975
|
+
// set last error
|
|
3976
|
+
this.lastError = err;
|
|
3977
|
+
return resolve();
|
|
3978
|
+
});
|
|
3979
|
+
});
|
|
3980
|
+
}
|
|
3981
|
+
catch (err) {
|
|
3982
|
+
this.loading = false;
|
|
3983
|
+
this._loadingService.hideLoading();
|
|
3984
|
+
this.lastError = err;
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
onChange($event) { }
|
|
3988
|
+
clearMessages() {
|
|
3989
|
+
this.messages = [];
|
|
3990
|
+
}
|
|
3991
|
+
onFilterChange(event) { }
|
|
3992
|
+
}
|
|
3993
|
+
AddItemsComponent.decorators = [
|
|
3994
|
+
{ type: Component, args: [{
|
|
3995
|
+
selector: 'app-add-items',
|
|
3996
|
+
template: "<div>\n <div class=\"modal-header\" *ngIf=\"modalTitle\">\n <h5 class=\"modal-title\">{{modalTitle | translate}}</h5>\n <button (click)=\"cancel()\" [disabled]=\"loading\"\n type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Cancel\">\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n <div class=\"modal-body pt-0\">\n <div class=\"row\" *ngIf=\"lastError\">\n <div class=\"alert alert-danger col-12\" role=\"alert\">\n <span [translate]=\"'E' + (lastError.statusCode || 500) + '.title'\"></span><br>\n <small *ngIf=\"lastError.statusCode >= 0\" [translate]=\"'E' + (lastError.statusCode || 500) + '.message'\"></small>\n <small *ngIf=\"lastError.statusCode == null\">{{lastError.message}}</small>\n </div>\n </div>\n <div class=\"row d-none\" *ngIf=\"items\">\n <div class=\"col-12\">\n <p [translate]=\"'StudyPrograms.AddCoursesHelp'\" [translateParams]=\"items\" ></p>\n </div>\n </div>\n <div class=\"row mt-3\">\n <div class=\"col-12\">\n <ng-container>\n <ul style=\"margin-left: -24px\" class=\"nav nav-tabs mb-1\" id=\"myTab\" role=\"tablist\">\n <li class=\"nav-item\" *ngIf=\"tableConfiguration\">\n <a class=\"nav-link active\" id=\"courses-tab\" data-toggle=\"tab\" href=\"#courses\" role=\"tab\" aria-controls=\"home\" aria-selected=\"true\" [translate]=\"'AddItems.SelectCourses'\" ></a>\n </li>\n <li class=\"nav-item\" *ngIf=\"formName\">\n <a class=\"nav-link\" [ngClass]=\"{ 'disabled': !(selectComponent.advancedTable.selected && selectComponent.advancedTable.selected.length) }\" id=\"attributes-tab\" data-toggle=\"tab\" href=\"#attributes\" role=\"tab\" aria-controls=\"profile\" aria-selected=\"false\" [translate]=\"'AddItems.EditAttributes'\"></a>\n </li>\n <li class=\"nav-item\" *ngIf=\"messages && messages.length > 0\">\n <a class=\"nav-link\" id=\"messages-tab\" data-toggle=\"tab\" href=\"#messages\" role=\"tab\" aria-controls=\"profile\" aria-selected=\"false\" [translate]=\"'AddItems.Messages'\"></a>\n </li>\n </ul>\n <div class=\"tab-content border-0\" id=\"myTabContent\">\n <div class=\"tab-pane fade show active\" id=\"courses\" role=\"tabpanel\" aria-labelledby=\"home-tab\">\n <app-advanced-table-modal #selectComponent (filter)=\"onFilterChange($event)\" [tableConfig]=\"tableConfiguration\"></app-advanced-table-modal>\n </div>\n <div class=\"tab-pane fade\" id=\"attributes\" role=\"tabpanel\" aria-labelledby=\"profile-tab\"\n [ngClass]=\"{'invisible':(formName == null)}\">\n<!-- <advanced-form-edit #formComponent [formName]=\"formName\" ></advanced-form-edit>-->\n </div>\n <div class=\"tab-pane fade\" id=\"messages\" role=\"tabpanel\" aria-labelledby=\"profile-tab\"\n [ngClass]=\"{'invisible':(messages == null)}\">\n <ul *ngIf=\"messages && messages.length>0\" class=\"sis--list-group sis--list-group-header\">\n <li class=\"sis--list-group-item\">\n <div class=\"d-flex justify-content-center align-items-center\">\n <div class=\"sis--list-group-item_lg_column\" [translate]=\"'AddItems.Messages'\"></div>\n </div>\n </li>\n </ul>\n <ul class=\"sis--list-group sis--list-group-body\">\n <li *ngFor=\"let message of messages\" class=\"sis--list-group-item\">\n <div class=\"d-flex justify-content-center align-items-center list-color\">\n <div class=\"sis--list-group-item_lg_column\">{{ message?.message }}</div>\n </div>\n </li>\n </ul>\n <div *ngIf=\"messages && messages.length>0\">\n <button [disabled]=\"loading\" (click)=\"clearMessages()\" type=\"button\" class=\"btn btn-indigo mt-3\">\n {{'Clear' | translate}}\n </button>\n </div>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n <div class=\"modal-footer justify-content-end\">\n <button [disabled]=\"okButtonDisabled || loading || !(selectComponent.advancedTable.selected && selectComponent.advancedTable.selected.length) \" (click)=\"ok()\"\n type=\"button\" [ngClass]=\"okButtonClass\">\n {{'OK' | translate}}\n </button>\n <button [disabled]=\"cancelButtonDisabled || loading\"\n (click)=\"cancel()\" type=\"button\" [ngClass]=\"cancelButtonClass\" data-dismiss=\"modal\">\n {{'Cancel' | translate}}\n </button>\n </div>\n</div>\n"
|
|
3997
|
+
}] }
|
|
3998
|
+
];
|
|
3999
|
+
/** @nocollapse */
|
|
4000
|
+
AddItemsComponent.ctorParameters = () => [
|
|
4001
|
+
{ type: Router },
|
|
4002
|
+
{ type: ActivatedRoute },
|
|
4003
|
+
{ type: ErrorService },
|
|
4004
|
+
{ type: ModalService },
|
|
4005
|
+
{ type: AppEventService },
|
|
4006
|
+
{ type: ToastService },
|
|
4007
|
+
{ type: LoadingService },
|
|
4008
|
+
{ type: TranslateService },
|
|
4009
|
+
{ type: AngularDataContext }
|
|
4010
|
+
];
|
|
4011
|
+
AddItemsComponent.propDecorators = {
|
|
4012
|
+
execute: [{ type: Input }],
|
|
4013
|
+
items: [{ type: Input }],
|
|
4014
|
+
target: [{ type: Input }],
|
|
4015
|
+
tableConfiguration: [{ type: Input }],
|
|
4016
|
+
formName: [{ type: Input }],
|
|
4017
|
+
messages: [{ type: Input }],
|
|
4018
|
+
formComponent: [{ type: ViewChild, args: ['formComponent',] }],
|
|
4019
|
+
selectComponent: [{ type: ViewChild, args: ['selectComponent',] }]
|
|
4020
|
+
};
|
|
4021
|
+
|
|
4022
|
+
const ɵ0 = FORMATTERS;
|
|
4023
|
+
class TablesModule {
|
|
4024
|
+
constructor(_translateService) {
|
|
4025
|
+
this._translateService = _translateService;
|
|
4026
|
+
this.ngOnInit().catch(err => {
|
|
4027
|
+
console.error('An error occurred while loading tables module');
|
|
4028
|
+
console.error(err);
|
|
4029
|
+
});
|
|
4030
|
+
}
|
|
4031
|
+
ngOnInit() {
|
|
4032
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4033
|
+
this._translateService.setTranslation('en', TABLES_EN, true);
|
|
4034
|
+
this._translateService.setTranslation('el', TABLES_EL, true);
|
|
4035
|
+
});
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
TablesModule.decorators = [
|
|
4039
|
+
{ type: NgModule, args: [{
|
|
4040
|
+
imports: [
|
|
4041
|
+
CommonModule,
|
|
4042
|
+
SharedModule,
|
|
4043
|
+
TranslateModule,
|
|
4044
|
+
FormsModule,
|
|
4045
|
+
ReactiveFormsModule,
|
|
4046
|
+
RouterModule,
|
|
4047
|
+
RouterModalModule,
|
|
4048
|
+
FormioModule,
|
|
4049
|
+
AdvancedFormsModule,
|
|
4050
|
+
SharedModule,
|
|
4051
|
+
ModalModule,
|
|
4052
|
+
BsDropdownModule,
|
|
4053
|
+
ProgressbarModule,
|
|
4054
|
+
NgPipesModule
|
|
4055
|
+
],
|
|
4056
|
+
providers: [DatePipe, TemplatePipe,
|
|
4057
|
+
{
|
|
4058
|
+
provide: COLUMN_FORMATTERS,
|
|
4059
|
+
useValue: ɵ0
|
|
4060
|
+
},
|
|
4061
|
+
AdvancedTableConfigurationResolver,
|
|
4062
|
+
AdvancedSearchConfigurationResolver,
|
|
4063
|
+
AdvancedSelectService,
|
|
4064
|
+
AdvancedAggregatorService,
|
|
4065
|
+
GroupByPipe
|
|
4066
|
+
],
|
|
4067
|
+
declarations: [
|
|
4068
|
+
AdvancedTableComponent,
|
|
4069
|
+
AdvancedTableSearchComponent,
|
|
4070
|
+
BtnExpDirective,
|
|
4071
|
+
AdvancedTableSettingsComponent,
|
|
4072
|
+
AdvancedTableModalBaseComponent,
|
|
4073
|
+
AdvancedSearchFormComponent,
|
|
4074
|
+
AdvancedListComponent,
|
|
4075
|
+
AdvancedRowActionComponent,
|
|
4076
|
+
TableSimpleHeaderComponent,
|
|
4077
|
+
SimpleHeaderToolsComponent,
|
|
4078
|
+
AdvancedSelectComponent,
|
|
4079
|
+
AdvancedTableEditorDirective,
|
|
4080
|
+
AdvancedAggregatorComponent,
|
|
4081
|
+
AdvancedAggregatorListComponent,
|
|
4082
|
+
AdvancedAggregatorPipe,
|
|
4083
|
+
ParseTitlePipe,
|
|
4084
|
+
AddItemsComponent
|
|
4085
|
+
],
|
|
4086
|
+
exports: [
|
|
4087
|
+
AdvancedTableComponent,
|
|
4088
|
+
AdvancedTableSearchComponent,
|
|
4089
|
+
BtnExpDirective,
|
|
4090
|
+
AdvancedTableSettingsComponent,
|
|
4091
|
+
AdvancedSearchFormComponent,
|
|
4092
|
+
AdvancedListComponent,
|
|
4093
|
+
AdvancedRowActionComponent,
|
|
4094
|
+
TableSimpleHeaderComponent,
|
|
4095
|
+
SimpleHeaderToolsComponent,
|
|
4096
|
+
AdvancedSelectComponent,
|
|
4097
|
+
AdvancedTableEditorDirective,
|
|
4098
|
+
AdvancedAggregatorComponent,
|
|
4099
|
+
AdvancedAggregatorListComponent,
|
|
4100
|
+
AddItemsComponent
|
|
4101
|
+
],
|
|
4102
|
+
entryComponents: [
|
|
4103
|
+
AdvancedTableModalBaseComponent,
|
|
4104
|
+
AdvancedTableSettingsComponent,
|
|
4105
|
+
AdvancedRowActionComponent,
|
|
4106
|
+
AdvancedSelectComponent,
|
|
4107
|
+
AddItemsComponent
|
|
4108
|
+
],
|
|
4109
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
|
4110
|
+
},] }
|
|
4111
|
+
];
|
|
4112
|
+
/** @nocollapse */
|
|
4113
|
+
TablesModule.ctorParameters = () => [
|
|
4114
|
+
{ type: TranslateService }
|
|
4115
|
+
];
|
|
4116
|
+
|
|
4117
|
+
/*
|
|
4118
|
+
* Public API Surface of tables
|
|
4119
|
+
*/
|
|
4120
|
+
|
|
4121
|
+
/**
|
|
4122
|
+
* Generated bundle index. Do not edit.
|
|
4123
|
+
*/
|
|
4124
|
+
|
|
4125
|
+
export { AdvancedFilterValueProvider as ɵb, AdvancedTableSearchBaseComponent as ɵa, AdvancedTableSettingsComponent as ɵe, SimpleHeaderToolsComponent as ɵf, TableSimpleHeaderComponent as ɵg, BtnExpDirective as ɵd, AdvancedAggregatorPipe as ɵh, ParseTitlePipe as ɵi, AdvancedAggregatorService as ɵc, AdvancedListComponent, AdvancedRowActionComponent, AdvancedSearchFormComponent, AdvancedTableConfigurationResolver, AdvancedSearchConfigurationResolver, AdvancedColumnFormatter, NestedPropertyFormatter, ButtonFormatter, LinkFormatter, ActionLinkFormatter, SelectRowFormatter, TemplateFormatter, TrueFalseFormatter, TranslationFormatter, DateTimeFormatter, NgClassFormatter, LanguageFormatter, COLUMN_FORMATTERS, AdvancedTableConfiguration, AdvancedTableComponent, AdvancedTableSearchComponent, FORMATTERS, AdvancedTableModalBaseTemplate, AdvancedTableModalBaseComponent, AdvancedSelectComponent, AdvancedSelectService, TablesModule, ɵ0, ActivatedTableService, AdvancedTableNotNullError, AdvancedTableEditorDirective, AdvancedAggregatorComponent, AdvancedAggregatorListComponent, AddItemsComponent };
|
|
4126
|
+
|
|
4127
|
+
//# sourceMappingURL=universis-ngx-tables.js.map
|