@universis/ngx-tables 0.6.14 → 0.6.19
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/bundles/universis-ngx-tables.umd.js +1077 -1044
- package/bundles/universis-ngx-tables.umd.js.map +1 -1
- package/bundles/universis-ngx-tables.umd.min.js +1 -1
- package/bundles/universis-ngx-tables.umd.min.js.map +1 -1
- package/esm2015/lib/components/advanced-list/advanced-list.component.js +2 -2
- package/esm2015/lib/components/advanced-search-form/advanced-search-form.component.js +5 -2
- package/esm2015/lib/components/advanced-table/advanced-table.component.js +50 -22
- package/esm2015/lib/i18n/tables.el.js +2 -1
- package/esm2015/lib/i18n/tables.en.js +2 -1
- package/esm2015/public_api.js +1 -4
- package/esm5/lib/components/advanced-list/advanced-list.component.js +2 -2
- package/esm5/lib/components/advanced-search-form/advanced-search-form.component.js +5 -2
- package/esm5/lib/components/advanced-table/advanced-table.component.js +50 -22
- package/esm5/lib/i18n/tables.el.js +2 -1
- package/esm5/lib/i18n/tables.en.js +2 -1
- package/esm5/public_api.js +1 -4
- package/fesm2015/universis-ngx-tables.js +1478 -1445
- package/fesm2015/universis-ngx-tables.js.map +1 -1
- package/fesm5/universis-ngx-tables.js +2335 -2302
- package/fesm5/universis-ngx-tables.js.map +1 -1
- package/lib/i18n/tables.el.d.ts +1 -0
- package/lib/i18n/tables.en.d.ts +1 -0
- package/package.json +16 -15
- package/public_api.d.ts +0 -3
- package/universis-ngx-tables.metadata.json +1 -1
|
@@ -4,16 +4,16 @@ import 'datatables.net';
|
|
|
4
4
|
import { template, at } from 'lodash';
|
|
5
5
|
import 'datatables.net-scroller';
|
|
6
6
|
import 'rxjs/add/observable/combineLatest';
|
|
7
|
-
import { BehaviorSubject } from 'rxjs';
|
|
8
7
|
import { HttpClient } from '@angular/common/http';
|
|
9
|
-
import { AngularDataContext } from '@themost/angular';
|
|
10
8
|
import { ClientDataQueryable, ResponseError, TextUtils } from '@themost/client';
|
|
9
|
+
import { BehaviorSubject } from 'rxjs';
|
|
10
|
+
import { AngularDataContext } from '@themost/angular';
|
|
11
11
|
import { __awaiter } from 'tslib';
|
|
12
12
|
import { Injectable, Directive, Output, EventEmitter, Pipe, Component, Input, ViewChild, ViewEncapsulation, ChangeDetectorRef, ElementRef, Inject, InjectionToken, Injector, NgModule, CUSTOM_ELEMENTS_SCHEMA, defineInjectable, inject, HostListener, ChangeDetectionStrategy } from '@angular/core';
|
|
13
13
|
import { DatePipe, CommonModule } from '@angular/common';
|
|
14
14
|
import { TranslateService, TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
|
15
15
|
import { FormArray, FormBuilder, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
16
|
-
import { TemplatePipe, LoadingService,
|
|
16
|
+
import { TemplatePipe, LoadingService, ErrorService, DIALOG_BUTTONS, ModalService, UserActivityService, ToastService, AppEventService, UserStorageService, ConfigurationService, SharedModule } from '@universis/common';
|
|
17
17
|
import { RouterModalOkCancel, RouterModalModule } from '@universis/common/routing';
|
|
18
18
|
import { ServiceUrlPreProcessor, TranslatePreProcessor, AdvancedFormsModule } from '@universis/forms';
|
|
19
19
|
import { FormioModule } from 'angular-formio';
|
|
@@ -667,32 +667,55 @@ class AdvancedTableComponent {
|
|
|
667
667
|
// simple search
|
|
668
668
|
search(searchText) {
|
|
669
669
|
this.query = this.query || this._context.model(this.config.model).asQueryable();
|
|
670
|
-
if (
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
670
|
+
if (this.serverSide) {
|
|
671
|
+
if (searchText && this.config.searchExpression) {
|
|
672
|
+
// validate search text in double quotes
|
|
673
|
+
if (/^"(.*?)"$/.test(searchText)) {
|
|
674
|
+
this.query.setParam('$filter', this._template.transform(this.config.searchExpression, {
|
|
675
|
+
text: searchText.replace(/^"|"$/g, ''),
|
|
676
|
+
}));
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
// try to split words
|
|
680
|
+
const words = searchText.split(' ');
|
|
681
|
+
// join words to a single filter
|
|
682
|
+
const filter = words.map((word) => {
|
|
683
|
+
return this._template.transform(this.config.searchExpression, {
|
|
684
|
+
text: word,
|
|
685
|
+
});
|
|
686
|
+
}).join(' and ');
|
|
687
|
+
// set filter
|
|
688
|
+
this.query.setParam('$filter', filter);
|
|
689
|
+
}
|
|
676
690
|
}
|
|
677
691
|
else {
|
|
678
|
-
//
|
|
679
|
-
|
|
680
|
-
// join words to a single filter
|
|
681
|
-
const filter = words.map((word) => {
|
|
682
|
-
return this._template.transform(this.config.searchExpression, {
|
|
683
|
-
text: word,
|
|
684
|
-
});
|
|
685
|
-
}).join(' and ');
|
|
686
|
-
// set filter
|
|
687
|
-
this.query.setParam('$filter', filter);
|
|
692
|
+
// clear filter
|
|
693
|
+
this.query.setParam('$filter', null);
|
|
688
694
|
}
|
|
695
|
+
this._firstLoad = false;
|
|
696
|
+
this.dataTable.draw();
|
|
689
697
|
}
|
|
690
698
|
else {
|
|
691
|
-
//
|
|
692
|
-
this.
|
|
699
|
+
// ensure empty table layout
|
|
700
|
+
this.resetEmptyTable();
|
|
701
|
+
// search locally
|
|
702
|
+
this.dataTable.search(searchText.trim()).draw();
|
|
703
|
+
// get indexes of filtered rows
|
|
704
|
+
const indexes = Array.from(this.dataTable.context[0].aiDisplay);
|
|
705
|
+
// set recordsFiltered
|
|
706
|
+
const recordsFiltered = indexes.length;
|
|
707
|
+
// set recordsTotal
|
|
708
|
+
const recordsTotal = indexes.length;
|
|
709
|
+
// retrieve data from indexes
|
|
710
|
+
const rows = Array.from(this.dataTable.rows().data());
|
|
711
|
+
const data = indexes.map(index => rows[index]);
|
|
712
|
+
// emit results to load output
|
|
713
|
+
this.load.emit({
|
|
714
|
+
data: data,
|
|
715
|
+
recordsFiltered: recordsFiltered,
|
|
716
|
+
recordsTotal: recordsTotal
|
|
717
|
+
});
|
|
693
718
|
}
|
|
694
|
-
this._firstLoad = false;
|
|
695
|
-
this.dataTable.draw();
|
|
696
719
|
}
|
|
697
720
|
fetch(clearSelected) {
|
|
698
721
|
// if data table is null
|
|
@@ -1355,6 +1378,10 @@ class AdvancedTableComponent {
|
|
|
1355
1378
|
const query = this._context.model(this.config.model).asQueryable();
|
|
1356
1379
|
// select attributes
|
|
1357
1380
|
query.setParam('$select', this.lastQueryParams.$select);
|
|
1381
|
+
if (this.lastQueryParams.$expand) {
|
|
1382
|
+
// apply expand attributes
|
|
1383
|
+
query.setParam('$expand', this.lastQueryParams.$expand);
|
|
1384
|
+
}
|
|
1358
1385
|
// apply filter for row
|
|
1359
1386
|
findKeys.forEach((key) => {
|
|
1360
1387
|
const column = this.config.columns.find((col) => {
|
|
@@ -1441,7 +1468,8 @@ class AdvancedTableComponent {
|
|
|
1441
1468
|
// due to ng data binding
|
|
1442
1469
|
// empty table template has no translated text during ngOnInit()
|
|
1443
1470
|
if (this.dataTable && this.dataTable.context[0]) {
|
|
1444
|
-
this.dataTable.context[0].oLanguage.sEmptyTable =
|
|
1471
|
+
this.dataTable.context[0].oLanguage.sEmptyTable =
|
|
1472
|
+
this.dataTable.context[0].oLanguage.sZeroRecords = this.emptyTable.nativeElement.innerHTML;
|
|
1445
1473
|
}
|
|
1446
1474
|
}
|
|
1447
1475
|
clearEmptyTable() {
|
|
@@ -1558,418 +1586,233 @@ AdvancedTableComponent.propDecorators = {
|
|
|
1558
1586
|
query: [{ type: Input, args: ['query',] }]
|
|
1559
1587
|
};
|
|
1560
1588
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
const AdvancedTableModalBaseTemplate = `
|
|
1564
|
-
<div>
|
|
1565
|
-
<!-- search -->
|
|
1566
|
-
<div class="row mt-3">
|
|
1567
|
-
<div class="col-12 col-lg-3">
|
|
1568
|
-
<div class="input-group mb-3">
|
|
1569
|
-
<input spellcheck="false" type="text"
|
|
1570
|
-
(keyup)="onKeyUp($event)" [(ngModel)]="searchText"
|
|
1571
|
-
class="form-control bg-gray-100 border-0"
|
|
1572
|
-
placeholder="{{'Tables.SearchPlaceholder' | translate}}" aria-label="Search" aria-describedby="basic-addon2">
|
|
1573
|
-
<div class="input-group-append">
|
|
1574
|
-
<button class="btn btn-default bg-gray-100 border-0" type="button">
|
|
1575
|
-
<i class="fa fa-search text-secondary"></i>
|
|
1576
|
-
</button>
|
|
1577
|
-
</div>
|
|
1578
|
-
</div>
|
|
1579
|
-
</div>
|
|
1580
|
-
<div #emptyTable class="d-none">
|
|
1581
|
-
<div>
|
|
1582
|
-
<div class="mt-4">
|
|
1583
|
-
<div class="icon-circle bg-gray-300 border-gray-100"></div>
|
|
1584
|
-
</div>
|
|
1585
|
-
<div class="mt-4">
|
|
1586
|
-
<h4 class="font-3xl text-dark" [translate]="'Tables.EmptyTableTitle'"></h4>
|
|
1587
|
-
<p class="font-lg text-dark" [translate]="'Tables.EmptyTableMessage'"></p>
|
|
1588
|
-
</div>
|
|
1589
|
-
|
|
1590
|
-
</div>
|
|
1591
|
-
</div>
|
|
1592
|
-
<div class="col-12 col-lg-5">
|
|
1593
|
-
<div class="btn-toolbar">
|
|
1594
|
-
<button class="btn btn-gray-100">
|
|
1595
|
-
<i class="fa fa-sliders-h pr-2 text-secondary" aria-hidden="true"></i>{{'Tables.Filters' | translate}}
|
|
1596
|
-
</button>
|
|
1597
|
-
<div #dropdownFilter class="dropdown ml-3">
|
|
1598
|
-
<button class="btn btn-gray-100 dropdown-toggle"
|
|
1599
|
-
type="button" id="dropdownDefaultFilters"
|
|
1600
|
-
data-toggle="dropdown" dropdownToggle>
|
|
1601
|
-
{{ this.defaultFilter.name | translate }}
|
|
1602
|
-
</button>
|
|
1603
|
-
<div class="dropdown-menu" aria-labelledby="dropdownDefaultFilters">
|
|
1604
|
-
<button type="button" class="dropdown-item" *ngFor="let filter of defaultFilters" (click)="onFilterSelect(filter)">
|
|
1605
|
-
{{ filter.name | translate }}
|
|
1606
|
-
</button>
|
|
1607
|
-
</div>
|
|
1608
|
-
</div>
|
|
1609
|
-
</div>
|
|
1610
|
-
</div>
|
|
1611
|
-
<div class="col text-lg-right mt-3 mt-lg-0 d-none">
|
|
1612
|
-
<button [disabled]="true" class="btn rounded-pill btn-outline-theme">
|
|
1613
|
-
<span class="pr-2 font-lg">+</span><span [translate]="'Tables.AddItem'"></span>
|
|
1614
|
-
</button>
|
|
1615
|
-
</div>
|
|
1616
|
-
</div>
|
|
1617
|
-
<div class="row">
|
|
1618
|
-
<div class="col-12" [ngClass]="{ 'component-loading': loading}">
|
|
1619
|
-
<app-advanced-table #advancedTable (init)="onTableInit($event)"
|
|
1620
|
-
(load)="onDataLoad($event)"
|
|
1621
|
-
[selectable]="true"
|
|
1622
|
-
[multipleSelect]="true"
|
|
1623
|
-
[scrollX]="false" [scrollY]="320"
|
|
1624
|
-
[pageLength]="pageLength"
|
|
1625
|
-
[scrollable]="false" [showFooter]="false"
|
|
1626
|
-
[showActions]="false" [lengthMenu]="lengthMenu"
|
|
1627
|
-
[config]="tableConfig" [configSrc]="tableConfigSrc"></app-advanced-table>
|
|
1628
|
-
</div>
|
|
1629
|
-
</div>
|
|
1630
|
-
<div class="row mt-3 table-custom-pager">
|
|
1631
|
-
<div class="col-12 d-flex" *ngIf="recordsTotal">
|
|
1632
|
-
<div class="mt-2 ml-auto mr-3 text-secondary" [translate]="'Tables.RecordsPerPage'"></div>
|
|
1633
|
-
<div #dropdownLength class="dropdown mr-3">
|
|
1634
|
-
<button class="btn btn-gray-100 dropdown-toggle"
|
|
1635
|
-
type="button" id="dropdownPageSize" data-toggle="dropdown"
|
|
1636
|
-
dropdownToggle>
|
|
1637
|
-
{{pageLength}}
|
|
1638
|
-
</button>
|
|
1639
|
-
<div class="dropdown-menu" aria-labelledby="dropdownPageSize">
|
|
1640
|
-
<button (click)="setLength(item)"
|
|
1641
|
-
class="dropdown-item dropdown-item-theme"
|
|
1642
|
-
type="button" *ngFor="let item of lengthMenu">{{item}}</button>
|
|
1643
|
-
</div>
|
|
1644
|
-
</div>
|
|
1645
|
-
<div class="mt-2 mr-2" [translate]="'Tables.PageInfo'" [translateParams]="pageInfo"></div>
|
|
1646
|
-
<button [disabled]="pageInfo && pageInfo.page === 0" class="btn text-nowrap" (click)="firstPage()">
|
|
1647
|
-
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
1648
|
-
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
1649
|
-
</button>
|
|
1650
|
-
<button [disabled]="pageInfo && pageInfo.page===0" class="btn" (click)="previousPage()">
|
|
1651
|
-
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
1652
|
-
</button>
|
|
1653
|
-
<button [disabled]="pageInfo && pageInfo.page===pageInfo.pages-1" class="btn" (click)="nextPage()">
|
|
1654
|
-
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
1655
|
-
</button>
|
|
1656
|
-
<button [disabled]="pageInfo && pageInfo.page===pageInfo.pages-1" class="btn text-nowrap" (click)="lastPage()">
|
|
1657
|
-
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
1658
|
-
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
1659
|
-
</button>
|
|
1660
|
-
</div>
|
|
1661
|
-
</div>
|
|
1662
|
-
</div>
|
|
1663
|
-
`;
|
|
1664
|
-
class AdvancedTableModalBaseComponent extends RouterModalOkCancel {
|
|
1665
|
-
constructor(_router, _activatedRoute, _context, advancedFilterValueProvider, datePipe) {
|
|
1666
|
-
super(_router, _activatedRoute);
|
|
1589
|
+
class AdvancedListComponent {
|
|
1590
|
+
constructor(_context, _translateService, _activatedRoute, _errorService, _toastService, _modalService, _userActivityService) {
|
|
1667
1591
|
this._context = _context;
|
|
1668
|
-
this.
|
|
1669
|
-
this.
|
|
1670
|
-
this.
|
|
1671
|
-
this.
|
|
1672
|
-
this.
|
|
1673
|
-
this.
|
|
1674
|
-
this.
|
|
1675
|
-
this.defaultFilters = [AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER];
|
|
1676
|
-
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
1677
|
-
this.pageInfo = {
|
|
1678
|
-
serverSide: true
|
|
1679
|
-
};
|
|
1680
|
-
this.modalClass = 'modal-xl modal-table';
|
|
1681
|
-
this._template = new TemplatePipe();
|
|
1682
|
-
}
|
|
1683
|
-
ngAfterViewInit() {
|
|
1684
|
-
//
|
|
1685
|
-
}
|
|
1686
|
-
_getInputs(componentType) {
|
|
1687
|
-
const props = componentType.__prop__metadata__;
|
|
1688
|
-
const inputs = [];
|
|
1689
|
-
if (props == null) {
|
|
1690
|
-
return inputs;
|
|
1691
|
-
}
|
|
1692
|
-
Object.keys(props).forEach(prop => {
|
|
1693
|
-
const member = props[prop][0];
|
|
1694
|
-
if (member.ngMetadataName === 'Input') {
|
|
1695
|
-
inputs.push(prop);
|
|
1696
|
-
}
|
|
1697
|
-
});
|
|
1698
|
-
return inputs.sort();
|
|
1699
|
-
}
|
|
1700
|
-
hasInputs() {
|
|
1701
|
-
return [];
|
|
1702
|
-
}
|
|
1703
|
-
ngOnInit() {
|
|
1704
|
-
this.okButtonText = 'Tables.Apply';
|
|
1705
|
-
this.cancelButtonText = 'Tables.Cancel';
|
|
1706
|
-
// get route data
|
|
1707
|
-
this.subscription = this.activatedRoute.data.subscribe(data => {
|
|
1708
|
-
// set modal title if any
|
|
1709
|
-
if (data.title) {
|
|
1710
|
-
this.modalTitle = data.title;
|
|
1711
|
-
}
|
|
1712
|
-
if (data.config) {
|
|
1713
|
-
this.tableConfig = data.config;
|
|
1714
|
-
}
|
|
1715
|
-
if (this.tableConfig) {
|
|
1716
|
-
this.defaultFilters.push.apply(this.defaultFilters, this.tableConfig.searches);
|
|
1717
|
-
}
|
|
1718
|
-
// important: use hasInputs due to AOT compilation. Metadata properties cannot be used (e.g. @Input())
|
|
1719
|
-
// because there are empty during runtime
|
|
1720
|
-
// so we need to define manually input properties that are going to be bind through this process
|
|
1721
|
-
// bind all inputs
|
|
1722
|
-
const inputs = this.hasInputs();
|
|
1723
|
-
inputs.forEach(input => {
|
|
1724
|
-
if (Object.prototype.hasOwnProperty.call(data, input)) {
|
|
1725
|
-
this[input] = data[input];
|
|
1726
|
-
}
|
|
1727
|
-
});
|
|
1728
|
-
this.configChangesSubscription = this.advancedTable.configChanges.subscribe((config) => {
|
|
1729
|
-
if (this.tableConfig) {
|
|
1730
|
-
// reset default filter
|
|
1731
|
-
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
1732
|
-
// init filter
|
|
1733
|
-
this.defaultFilters = [AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER];
|
|
1734
|
-
// add extra filters
|
|
1735
|
-
this.defaultFilters.push.apply(this.defaultFilters, this.tableConfig.searches);
|
|
1736
|
-
}
|
|
1737
|
-
});
|
|
1738
|
-
});
|
|
1739
|
-
}
|
|
1740
|
-
cancel() {
|
|
1741
|
-
return this.close();
|
|
1742
|
-
}
|
|
1743
|
-
ok() {
|
|
1744
|
-
return this.close();
|
|
1592
|
+
this._translateService = _translateService;
|
|
1593
|
+
this._activatedRoute = _activatedRoute;
|
|
1594
|
+
this._errorService = _errorService;
|
|
1595
|
+
this._toastService = _toastService;
|
|
1596
|
+
this._modalService = _modalService;
|
|
1597
|
+
this._userActivityService = _userActivityService;
|
|
1598
|
+
this.allowSaveOption = true;
|
|
1745
1599
|
}
|
|
1746
1600
|
ngOnDestroy() {
|
|
1601
|
+
if (this.dataSubscription) {
|
|
1602
|
+
this.dataSubscription.unsubscribe();
|
|
1603
|
+
}
|
|
1604
|
+
if (this.reloadSubscription) {
|
|
1605
|
+
this.reloadSubscription.unsubscribe();
|
|
1606
|
+
}
|
|
1747
1607
|
if (this.subscription) {
|
|
1748
1608
|
this.subscription.unsubscribe();
|
|
1749
1609
|
}
|
|
1750
1610
|
}
|
|
1751
|
-
|
|
1752
|
-
this.recordsTotal = data.recordsTotal;
|
|
1753
|
-
this.recordsFiltered = data.recordsFiltered;
|
|
1754
|
-
// get first page info (because page is not going to be fired for first page)
|
|
1755
|
-
if (data.recordsTotal > 0 && this.pageInfo.recordsTotal === 0) {
|
|
1756
|
-
this.pageInfo.recordsTotal = data.recordsTotal;
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
previousPage() {
|
|
1760
|
-
// go to next page
|
|
1761
|
-
// noinspection TypeScriptValidateJSTypes
|
|
1762
|
-
this.advancedTable.dataTable.page('previous').draw('page');
|
|
1763
|
-
}
|
|
1764
|
-
nextPage() {
|
|
1765
|
-
// go to next page
|
|
1766
|
-
// noinspection TypeScriptValidateJSTypes
|
|
1767
|
-
this.advancedTable.dataTable.page('next').draw('page');
|
|
1768
|
-
}
|
|
1769
|
-
onTableInit(dataTable) {
|
|
1770
|
-
const self = this;
|
|
1771
|
-
// get initial page info
|
|
1772
|
-
const initialPageInfo = dataTable.page.info();
|
|
1773
|
-
initialPageInfo.start += 1;
|
|
1774
|
-
self.pageInfo = initialPageInfo;
|
|
1775
|
-
// change emptyTableMessage
|
|
1776
|
-
dataTable.context[0].oLanguage.sEmptyTable = this.emptyTable.nativeElement.innerHTML;
|
|
1777
|
-
// bind page change event
|
|
1778
|
-
dataTable.on('page.dt', function () {
|
|
1779
|
-
const pageInfo = dataTable.page.info();
|
|
1780
|
-
pageInfo.start += 1;
|
|
1781
|
-
self.pageInfo = pageInfo;
|
|
1782
|
-
});
|
|
1783
|
-
// bind draw change event
|
|
1784
|
-
dataTable.on('draw.dt', function () {
|
|
1785
|
-
// for future use
|
|
1786
|
-
self.loading = false;
|
|
1787
|
-
const pageInfo = dataTable.page.info();
|
|
1788
|
-
pageInfo.start += 1;
|
|
1789
|
-
self.pageInfo = pageInfo;
|
|
1790
|
-
});
|
|
1791
|
-
}
|
|
1792
|
-
firstPage() {
|
|
1793
|
-
// go to first page
|
|
1794
|
-
// noinspection TypeScriptValidateJSTypes
|
|
1795
|
-
this.advancedTable.dataTable.page('first').draw('page');
|
|
1796
|
-
}
|
|
1797
|
-
lastPage() {
|
|
1798
|
-
// go to last page
|
|
1799
|
-
// noinspection TypeScriptValidateJSTypes
|
|
1800
|
-
this.advancedTable.dataTable.page('last').draw('page');
|
|
1801
|
-
}
|
|
1802
|
-
setLength(value) {
|
|
1803
|
-
this.pageLength = value;
|
|
1804
|
-
this.advancedTable.dataTable.page.len(value).draw();
|
|
1805
|
-
}
|
|
1806
|
-
onKeyUp(event) {
|
|
1807
|
-
if (event.code === 'Enter') {
|
|
1808
|
-
// loading
|
|
1809
|
-
this.loading = true;
|
|
1810
|
-
this.advancedTable.search(event.target.value);
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
/**
|
|
1814
|
-
*
|
|
1815
|
-
* Reads the modal table and returns a list of filters to use in the table
|
|
1816
|
-
* query.
|
|
1817
|
-
*
|
|
1818
|
-
* @param tableConfig {object} The table configuration from json file
|
|
1819
|
-
* @param defaultFilterValues {object} A list of default filters
|
|
1820
|
-
* @param {object} searchItem The searchItem to use
|
|
1821
|
-
*
|
|
1822
|
-
*/
|
|
1823
|
-
getFilters(tableConfig, defaultFilterValues = {}, searchItem) {
|
|
1824
|
-
// Do nothing in absence of configuration.
|
|
1825
|
-
if (!tableConfig || !Array.isArray(tableConfig.criteria)) {
|
|
1826
|
-
return [];
|
|
1827
|
-
}
|
|
1828
|
-
if (searchItem.filter == null) {
|
|
1829
|
-
return [];
|
|
1830
|
-
}
|
|
1831
|
-
const matchedCriterion = tableConfig.criteria.find(criterion => Object.prototype.hasOwnProperty.call(searchItem.filter, criterion.name));
|
|
1832
|
-
if (matchedCriterion == null) {
|
|
1833
|
-
return [];
|
|
1834
|
-
}
|
|
1835
|
-
const value = searchItem.filter[matchedCriterion.name];
|
|
1836
|
-
const criterionSafeName = matchedCriterion.name !== null
|
|
1837
|
-
? matchedCriterion.name.toString()
|
|
1838
|
-
: '';
|
|
1839
|
-
let applicableFilter;
|
|
1840
|
-
// Raw dates should be transformed to a specific format
|
|
1841
|
-
if (criterionSafeName.includes('GMT')) {
|
|
1842
|
-
const newDate = this.datePipe.transform(criterionSafeName, 'MM/dd/yyyy');
|
|
1843
|
-
applicableFilter = this._template.transform(matchedCriterion.filter, {
|
|
1844
|
-
value: newDate
|
|
1845
|
-
});
|
|
1846
|
-
}
|
|
1847
|
-
else if (criterionSafeName !== 'undefined') {
|
|
1848
|
-
applicableFilter = (this._template.transform(matchedCriterion.filter, Object.assign({
|
|
1849
|
-
value: value
|
|
1850
|
-
}, defaultFilterValues)));
|
|
1851
|
-
}
|
|
1852
|
-
else {
|
|
1853
|
-
applicableFilter = '';
|
|
1854
|
-
}
|
|
1855
|
-
return [applicableFilter];
|
|
1856
|
-
}
|
|
1857
|
-
onFilterSelect(value) {
|
|
1858
|
-
this.advancedSearch(value);
|
|
1859
|
-
}
|
|
1860
|
-
/**
|
|
1861
|
-
*
|
|
1862
|
-
* Runs a filter against the modal table
|
|
1863
|
-
*
|
|
1864
|
-
* @param {object} filter The filter to use in the search
|
|
1865
|
-
*
|
|
1866
|
-
*/
|
|
1867
|
-
advancedSearch(filter) {
|
|
1611
|
+
ngOnInit() {
|
|
1868
1612
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1869
|
-
// Do nothing in absence of configuration.
|
|
1870
|
-
if (!this.tableConfig && !Array.isArray(this.tableConfig.criteria)) {
|
|
1871
|
-
console.warn('No table configuration available', this.tableConfig);
|
|
1872
|
-
return;
|
|
1873
|
-
}
|
|
1874
|
-
// An advanced table has some filters by default
|
|
1875
|
-
let defaultFilterValues;
|
|
1876
1613
|
try {
|
|
1877
|
-
|
|
1614
|
+
// get metadata
|
|
1615
|
+
const metadata = yield this._context.getMetadata();
|
|
1616
|
+
// get route data
|
|
1617
|
+
this.dataSubscription = this._activatedRoute.data.subscribe(routeData => {
|
|
1618
|
+
// get entityType
|
|
1619
|
+
const re = new RegExp(`^${routeData.model}$`, 'ig');
|
|
1620
|
+
const entitySet = metadata.EntityContainer.EntitySet.find(x => {
|
|
1621
|
+
return re.test(x.Name);
|
|
1622
|
+
});
|
|
1623
|
+
// if entity type is null
|
|
1624
|
+
if (entitySet == null) {
|
|
1625
|
+
// navigate to error
|
|
1626
|
+
return this._errorService.navigateToError(new ResponseError('EntitySet Not Found', 404));
|
|
1627
|
+
}
|
|
1628
|
+
// get entity type
|
|
1629
|
+
const entityType = metadata.EntityType.find(x => {
|
|
1630
|
+
return x.Name === entitySet.EntityType;
|
|
1631
|
+
});
|
|
1632
|
+
if (entityType == null) {
|
|
1633
|
+
return this._errorService.navigateToError(new ResponseError('EntityType Not Found', 404));
|
|
1634
|
+
}
|
|
1635
|
+
if (routeData.searchConfiguration) {
|
|
1636
|
+
if (Object.prototype.hasOwnProperty.call(routeData.searchConfiguration, 'allowSave')) {
|
|
1637
|
+
this.allowSaveOption = routeData.searchConfiguration.allowSave;
|
|
1638
|
+
}
|
|
1639
|
+
this.searchConfiguration = routeData.searchConfiguration;
|
|
1640
|
+
}
|
|
1641
|
+
if (routeData.tableConfiguration) {
|
|
1642
|
+
this.tableConfiguration = routeData.tableConfiguration;
|
|
1643
|
+
}
|
|
1644
|
+
if (routeData.description) {
|
|
1645
|
+
this.description = this._translateService.instant(routeData.description);
|
|
1646
|
+
}
|
|
1647
|
+
if (routeData.longDescription) {
|
|
1648
|
+
this.longDescription = this._translateService.instant(routeData.longDescription);
|
|
1649
|
+
}
|
|
1650
|
+
if (routeData.category) {
|
|
1651
|
+
this.category = this._translateService.instant(routeData.category);
|
|
1652
|
+
}
|
|
1653
|
+
// clone default configuration
|
|
1654
|
+
const defaultConfiguration = AdvancedTableConfiguration.cast(this.tableConfiguration, true);
|
|
1655
|
+
// correct edit button link
|
|
1656
|
+
const column = defaultConfiguration.columns.find(col => {
|
|
1657
|
+
return col.formatter === 'ButtonFormatter' && col.formatString === '(modal:edit)';
|
|
1658
|
+
});
|
|
1659
|
+
if (column) {
|
|
1660
|
+
const url = [];
|
|
1661
|
+
this._activatedRoute.snapshot.pathFromRoot.forEach(route => {
|
|
1662
|
+
url.push.apply(url, route.url);
|
|
1663
|
+
});
|
|
1664
|
+
column.formatString = `#/${url.join('/')}/(modal:\$\{id\}/edit)`;
|
|
1665
|
+
}
|
|
1666
|
+
// create list configuration by assigning list entity set
|
|
1667
|
+
this.table.config = Object.assign({
|
|
1668
|
+
model: entitySet.Name
|
|
1669
|
+
}, defaultConfiguration);
|
|
1670
|
+
// fetch data
|
|
1671
|
+
this.table.fetch(true);
|
|
1672
|
+
// do reload by using hidden fragment e.g. /classes#reload
|
|
1673
|
+
this.reloadSubscription = this._activatedRoute.fragment.subscribe(fragment => {
|
|
1674
|
+
if (fragment && fragment === 'reload') {
|
|
1675
|
+
this._toastService.show(this.description, this._translateService.instant('Settings.OperationCompleted'));
|
|
1676
|
+
this.table.fetch(true);
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
// finally add user activity
|
|
1680
|
+
this._userActivityService.setItem({
|
|
1681
|
+
category: this.category,
|
|
1682
|
+
description: this.description,
|
|
1683
|
+
url: window.location.hash.substring(1),
|
|
1684
|
+
dateCreated: new Date
|
|
1685
|
+
});
|
|
1686
|
+
});
|
|
1878
1687
|
}
|
|
1879
1688
|
catch (err) {
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
const expressions = [];
|
|
1883
|
-
let predefinedCriteria;
|
|
1884
|
-
if (filter) {
|
|
1885
|
-
this.defaultFilter = filter;
|
|
1886
|
-
predefinedCriteria = this.getFilters(this.tableConfig, defaultFilterValues, filter);
|
|
1887
|
-
}
|
|
1888
|
-
else {
|
|
1889
|
-
// Empty a falsy filter resets the results
|
|
1890
|
-
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
1689
|
+
console.error(err);
|
|
1690
|
+
return this._errorService.showError(err);
|
|
1891
1691
|
}
|
|
1892
|
-
// search filter is applied separately, there is no need to add here.
|
|
1893
|
-
expressions.push(predefinedCriteria);
|
|
1894
|
-
const query = this._context.model(this.advancedTable.config.model).asQueryable();
|
|
1895
|
-
query.setParam('filter', expressions.join(' and ')).prepare();
|
|
1896
|
-
this.advancedTable.query = query;
|
|
1897
|
-
this.advancedTable.fetch();
|
|
1898
1692
|
});
|
|
1899
1693
|
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Data load event handler of advanced table component
|
|
1696
|
+
* @param {AdvancedTableDataResult} data
|
|
1697
|
+
*/
|
|
1698
|
+
onDataLoad(data) {
|
|
1699
|
+
this.recordsTotal = data.recordsTotal;
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Remove selected items
|
|
1703
|
+
*/
|
|
1704
|
+
remove() {
|
|
1705
|
+
if (this.table && this.table.selected && this.table.selected.length) {
|
|
1706
|
+
// get items to remove
|
|
1707
|
+
const items = this.table.selected;
|
|
1708
|
+
return this._modalService.showWarningDialog(this._translateService.instant('Tables.RemoveItemsTitle'), this._translateService.instant('Tables.RemoveItemsMessage'), DIALOG_BUTTONS.OkCancel).then(result => {
|
|
1709
|
+
if (result === 'ok') {
|
|
1710
|
+
this._context.model(this.table.config.model).remove(items).then(() => {
|
|
1711
|
+
this._toastService.show(this.description + ':' + this._translateService.instant('Tables.RemoveItemMessage.title'), this._translateService.instant((items.length === 1 ?
|
|
1712
|
+
'Tables.RemoveItemMessage.one' : 'Tables.RemoveItemMessage.many'), { value: items.length }));
|
|
1713
|
+
this.table.fetch(true);
|
|
1714
|
+
}).catch(err => {
|
|
1715
|
+
this._errorService.showError(err, {
|
|
1716
|
+
continueLink: '.'
|
|
1717
|
+
});
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1900
1723
|
}
|
|
1901
|
-
|
|
1902
|
-
name: 'Tables.All',
|
|
1903
|
-
filter: null
|
|
1904
|
-
};
|
|
1905
|
-
AdvancedTableModalBaseComponent.decorators = [
|
|
1724
|
+
AdvancedListComponent.decorators = [
|
|
1906
1725
|
{ type: Component, args: [{
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
template: AdvancedTableModalBaseTemplate,
|
|
1910
|
-
styles: [`
|
|
1911
|
-
|
|
1912
|
-
`]
|
|
1726
|
+
selector: 'app-advanced-list',
|
|
1727
|
+
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 [showFooter]=\"false\"\n (load)=\"onDataLoad($event)\" [height]=\"600\"></app-advanced-table>\n </div>\n </div>\n <router-modal></router-modal>\n</div>\n"
|
|
1913
1728
|
}] }
|
|
1914
1729
|
];
|
|
1915
1730
|
/** @nocollapse */
|
|
1916
|
-
|
|
1917
|
-
{ type: Router },
|
|
1918
|
-
{ type: ActivatedRoute },
|
|
1731
|
+
AdvancedListComponent.ctorParameters = () => [
|
|
1919
1732
|
{ type: AngularDataContext },
|
|
1920
|
-
{ type:
|
|
1921
|
-
{ type:
|
|
1733
|
+
{ type: TranslateService },
|
|
1734
|
+
{ type: ActivatedRoute },
|
|
1735
|
+
{ type: ErrorService },
|
|
1736
|
+
{ type: ToastService },
|
|
1737
|
+
{ type: ModalService },
|
|
1738
|
+
{ type: UserActivityService }
|
|
1922
1739
|
];
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
lengthMenu: [{ type: Input, args: ['lengthMenu',] }],
|
|
1933
|
-
defaultFilter: [{ type: Input, args: ['defaultFilter',] }]
|
|
1740
|
+
AdvancedListComponent.propDecorators = {
|
|
1741
|
+
entitySet: [{ type: Input, args: ['entitySet',] }],
|
|
1742
|
+
category: [{ type: Input, args: ['category',] }],
|
|
1743
|
+
description: [{ type: Input, args: ['description',] }],
|
|
1744
|
+
longDescription: [{ type: Input, args: ['longDescription',] }],
|
|
1745
|
+
tableConfiguration: [{ type: Input, args: ['tableConfiguration',] }],
|
|
1746
|
+
searchConfiguration: [{ type: Input, args: ['searchConfiguration',] }],
|
|
1747
|
+
table: [{ type: ViewChild, args: ['table',] }],
|
|
1748
|
+
allowSaveOption: [{ type: Input, args: ['allowSaveOption',] }]
|
|
1934
1749
|
};
|
|
1935
1750
|
|
|
1936
|
-
class
|
|
1937
|
-
constructor(router, activatedRoute,
|
|
1751
|
+
class AdvancedRowActionComponent extends RouterModalOkCancel {
|
|
1752
|
+
constructor(router, activatedRoute, _translateService, _context, _errorService, _modalService, _appEvent) {
|
|
1938
1753
|
super(router, activatedRoute);
|
|
1939
|
-
this._errorService = _errorService;
|
|
1940
|
-
this._modalService = _modalService;
|
|
1941
|
-
this._appEvent = _appEvent;
|
|
1942
|
-
this._toastService = _toastService;
|
|
1943
|
-
this._loadingService = _loadingService;
|
|
1944
1754
|
this._translateService = _translateService;
|
|
1945
1755
|
this._context = _context;
|
|
1756
|
+
this._errorService = _errorService;
|
|
1757
|
+
this._modalService = _modalService;
|
|
1758
|
+
this._appEvent = _appEvent;
|
|
1759
|
+
this.modalIcon = 'fas fa-project-diagram';
|
|
1760
|
+
this.progressType = 'indigo';
|
|
1761
|
+
this.errorMessage = 'Tables.Actions.CompletedWithErrors';
|
|
1946
1762
|
this.loading = false;
|
|
1947
|
-
this.
|
|
1948
|
-
this.messages = [];
|
|
1763
|
+
this.refresh = new EventEmitter();
|
|
1949
1764
|
// set modal size
|
|
1950
1765
|
this.modalClass = 'modal-lg';
|
|
1951
|
-
|
|
1766
|
+
// set modal title
|
|
1767
|
+
this.modalTitle = 'Tables.Actions.Label';
|
|
1768
|
+
// disable ok button
|
|
1769
|
+
this.okButtonDisabled = false;
|
|
1770
|
+
// set button text
|
|
1771
|
+
this.okButtonText = 'Tables.Actions.Start';
|
|
1772
|
+
}
|
|
1773
|
+
get form() {
|
|
1774
|
+
return this.formComponent;
|
|
1952
1775
|
}
|
|
1953
1776
|
ngOnInit() {
|
|
1954
|
-
this.
|
|
1955
|
-
if (
|
|
1956
|
-
|
|
1777
|
+
this.refreshSubscription = this.refresh.subscribe((value) => {
|
|
1778
|
+
if (value && value.progress) {
|
|
1779
|
+
let progress = 0;
|
|
1780
|
+
if (value.progress < 0) {
|
|
1781
|
+
progress = 0;
|
|
1782
|
+
}
|
|
1783
|
+
else if (value.progress > 100) {
|
|
1784
|
+
progress = 100;
|
|
1785
|
+
}
|
|
1786
|
+
else {
|
|
1787
|
+
progress = value.progress;
|
|
1788
|
+
}
|
|
1789
|
+
this.progressComponent.value = progress;
|
|
1790
|
+
this.showProgress();
|
|
1957
1791
|
}
|
|
1958
1792
|
});
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1793
|
+
}
|
|
1794
|
+
ngAfterViewInit() {
|
|
1795
|
+
if (this.formComponent && this.formComponent.form) {
|
|
1796
|
+
this.okButtonDisabled = true;
|
|
1797
|
+
this.invalidSubscription = this.formComponent.form.change.subscribe((event) => {
|
|
1798
|
+
if (Object.prototype.hasOwnProperty.call(event, "isValid") && (event.changed != null)) {
|
|
1799
|
+
this.okButtonDisabled = !event.isValid;
|
|
1964
1800
|
}
|
|
1965
1801
|
});
|
|
1966
1802
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1803
|
+
}
|
|
1804
|
+
ngOnDestroy() {
|
|
1805
|
+
if (this.refreshSubscription) {
|
|
1806
|
+
this.refreshSubscription.unsubscribe();
|
|
1807
|
+
}
|
|
1808
|
+
if (this.executeSubscription) {
|
|
1809
|
+
this.executeSubscription.unsubscribe();
|
|
1810
|
+
}
|
|
1811
|
+
if (this.invalidSubscription) {
|
|
1812
|
+
this.invalidSubscription.unsubscribe();
|
|
1969
1813
|
}
|
|
1970
1814
|
}
|
|
1971
1815
|
cancel() {
|
|
1972
|
-
this.clearMessages();
|
|
1973
1816
|
if (this.loading) {
|
|
1974
1817
|
return;
|
|
1975
1818
|
}
|
|
@@ -1978,847 +1821,626 @@ class AddItemsComponent extends RouterModalOkCancel {
|
|
|
1978
1821
|
return this._modalService.modalRef.hide();
|
|
1979
1822
|
}
|
|
1980
1823
|
}
|
|
1981
|
-
ngOnDestroy() {
|
|
1982
|
-
if (this.formLoadSubscription) {
|
|
1983
|
-
this.formLoadSubscription.unsubscribe();
|
|
1984
|
-
}
|
|
1985
|
-
if (this.formChangeSubscription) {
|
|
1986
|
-
this.formChangeSubscription.unsubscribe();
|
|
1987
|
-
}
|
|
1988
|
-
if (this.selectedSubscription) {
|
|
1989
|
-
this.selectedSubscription.unsubscribe();
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
get formData() {
|
|
1993
|
-
if (this.formComponent === null) {
|
|
1994
|
-
return null;
|
|
1995
|
-
}
|
|
1996
|
-
return this.formComponent.form.formio.data;
|
|
1997
|
-
}
|
|
1998
1824
|
ok() {
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
this.loading = false;
|
|
1825
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1826
|
+
try {
|
|
1827
|
+
this.loading = true;
|
|
2003
1828
|
this.lastError = null;
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
1829
|
+
if (this.formComponent && this.formComponent.form &&
|
|
1830
|
+
this.formComponent.form.formio && this.formComponent.form.formio.data) {
|
|
1831
|
+
// set data
|
|
1832
|
+
this.data = this.formComponent.form.formio.data;
|
|
1833
|
+
}
|
|
1834
|
+
// do action
|
|
1835
|
+
this.execute.subscribe((result) => {
|
|
2008
1836
|
this.loading = false;
|
|
2009
|
-
|
|
2010
|
-
|
|
1837
|
+
this.refresh.emit({
|
|
1838
|
+
progress: 100
|
|
1839
|
+
});
|
|
1840
|
+
if (result && result.errors && result.errors > 0) {
|
|
1841
|
+
// show message for partial success
|
|
1842
|
+
if (result.errors === 1) {
|
|
1843
|
+
this.lastError = new Error(this._translateService.instant(`${this.errorMessage}.Description.One`));
|
|
1844
|
+
}
|
|
1845
|
+
else {
|
|
1846
|
+
this.lastError = new Error(this._translateService.instant(`${this.errorMessage}.Description.Many`, result));
|
|
1847
|
+
}
|
|
1848
|
+
this.refresh.emit({
|
|
1849
|
+
progress: 1
|
|
1850
|
+
});
|
|
1851
|
+
this.hideProgress();
|
|
1852
|
+
return;
|
|
2011
1853
|
}
|
|
2012
|
-
// close modal
|
|
2013
1854
|
if (this._modalService.modalRef) {
|
|
2014
1855
|
this._modalService.modalRef.hide();
|
|
2015
1856
|
}
|
|
2016
|
-
return resolve();
|
|
2017
1857
|
}, (err) => {
|
|
2018
1858
|
this.loading = false;
|
|
2019
|
-
// ensure that loading is hidden
|
|
2020
|
-
this._loadingService.hideLoading();
|
|
2021
|
-
// set last error
|
|
2022
1859
|
this.lastError = err;
|
|
2023
|
-
|
|
1860
|
+
this.refresh.emit({
|
|
1861
|
+
progress: 0
|
|
1862
|
+
});
|
|
2024
1863
|
});
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
}
|
|
1864
|
+
}
|
|
1865
|
+
catch (err) {
|
|
1866
|
+
this.loading = false;
|
|
1867
|
+
this.lastError = err;
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
2032
1870
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
1871
|
+
showProgress() {
|
|
1872
|
+
this.progressContainer.nativeElement.classList.replace('invisible', 'visible');
|
|
1873
|
+
}
|
|
1874
|
+
hideProgress() {
|
|
1875
|
+
this.progressContainer.nativeElement.classList.replace('visible', 'invisible');
|
|
2036
1876
|
}
|
|
2037
|
-
onFilterChange(event) { }
|
|
2038
1877
|
}
|
|
2039
|
-
|
|
1878
|
+
AdvancedRowActionComponent.decorators = [
|
|
2040
1879
|
{ type: Component, args: [{
|
|
2041
|
-
selector: 'app-
|
|
2042
|
-
template: "<div>\n
|
|
1880
|
+
selector: 'app-advanced-row-action',
|
|
1881
|
+
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 invisible\" #progressContainer>\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",
|
|
1882
|
+
encapsulation: ViewEncapsulation.None,
|
|
1883
|
+
styles: [`
|
|
1884
|
+
.fa-1_5x {
|
|
1885
|
+
font-size: 1.5rem;
|
|
1886
|
+
}
|
|
1887
|
+
.form-control:disabled, .form-control[readonly] {
|
|
1888
|
+
background-color: inherit;
|
|
1889
|
+
}
|
|
1890
|
+
.form-check.checkbox {
|
|
1891
|
+
position: relative
|
|
1892
|
+
}
|
|
1893
|
+
`]
|
|
2043
1894
|
}] }
|
|
2044
1895
|
];
|
|
2045
1896
|
/** @nocollapse */
|
|
2046
|
-
|
|
1897
|
+
AdvancedRowActionComponent.ctorParameters = () => [
|
|
2047
1898
|
{ type: Router },
|
|
2048
1899
|
{ type: ActivatedRoute },
|
|
1900
|
+
{ type: TranslateService },
|
|
1901
|
+
{ type: AngularDataContext },
|
|
2049
1902
|
{ type: ErrorService },
|
|
2050
1903
|
{ type: ModalService },
|
|
2051
|
-
{ type: AppEventService }
|
|
2052
|
-
{ type: ToastService },
|
|
2053
|
-
{ type: LoadingService },
|
|
2054
|
-
{ type: TranslateService },
|
|
2055
|
-
{ type: AngularDataContext }
|
|
1904
|
+
{ type: AppEventService }
|
|
2056
1905
|
];
|
|
2057
|
-
|
|
2058
|
-
execute: [{ type: Input }],
|
|
1906
|
+
AdvancedRowActionComponent.propDecorators = {
|
|
2059
1907
|
items: [{ type: Input }],
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
1908
|
+
modalIcon: [{ type: Input }],
|
|
1909
|
+
description: [{ type: Input }],
|
|
1910
|
+
formTemplate: [{ type: Input }],
|
|
1911
|
+
progressType: [{ type: Input }],
|
|
1912
|
+
data: [{ type: Output }],
|
|
1913
|
+
additionalMessage: [{ type: Input }],
|
|
1914
|
+
errorMessage: [{ type: Input }],
|
|
1915
|
+
progressComponent: [{ type: ViewChild, args: ['progress',] }],
|
|
1916
|
+
progressContainer: [{ type: ViewChild, args: ['progressContainer',] }],
|
|
2064
1917
|
formComponent: [{ type: ViewChild, args: ['formComponent',] }],
|
|
2065
|
-
|
|
1918
|
+
refresh: [{ type: Input }],
|
|
1919
|
+
execute: [{ type: Input }]
|
|
2066
1920
|
};
|
|
2067
1921
|
|
|
2068
|
-
class
|
|
2069
|
-
constructor(
|
|
1922
|
+
class AdvancedTableSearchBaseComponent {
|
|
1923
|
+
constructor() {
|
|
1924
|
+
this.filterChange = new EventEmitter();
|
|
1925
|
+
}
|
|
1926
|
+
get filter() {
|
|
1927
|
+
return this._filter;
|
|
1928
|
+
}
|
|
1929
|
+
set filter(value) {
|
|
1930
|
+
this._filter = value;
|
|
1931
|
+
}
|
|
1932
|
+
onKeyEnter(event) {
|
|
1933
|
+
if (event.code === 'Enter') {
|
|
1934
|
+
this.filterChange.emit(this._filter);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
AdvancedTableSearchBaseComponent.propDecorators = {
|
|
1939
|
+
filterChange: [{ type: Output }],
|
|
1940
|
+
filter: [{ type: Input }]
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
class AdvancedSearchFormComponent extends AdvancedTableSearchBaseComponent {
|
|
1944
|
+
constructor(_context, _translateService, _activatedRoute, _http) {
|
|
1945
|
+
super();
|
|
2070
1946
|
this._context = _context;
|
|
2071
|
-
this.
|
|
1947
|
+
this._translateService = _translateService;
|
|
1948
|
+
this._activatedRoute = _activatedRoute;
|
|
1949
|
+
this._http = _http;
|
|
1950
|
+
this.form = {};
|
|
1951
|
+
this.refreshForm = new EventEmitter();
|
|
1952
|
+
this.submission = { data: {} };
|
|
1953
|
+
this.loading = true;
|
|
2072
1954
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
* @param {Array<string>} filters A list of filter expressions to append at the query
|
|
2081
|
-
* @param {string} searchTemplate The template to use for searching expressions
|
|
2082
|
-
* @param {string} searchValue The value of the user search
|
|
2083
|
-
*
|
|
2084
|
-
* @returns {Promise<any>} The response of the query execution
|
|
2085
|
-
*/
|
|
2086
|
-
fetchData(model, fixedParams, queryStatement = '', filters, searchTemplate, searchValue) {
|
|
2087
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2088
|
-
const query = this._context.model(model).asQueryable();
|
|
2089
|
-
if (!fixedParams || typeof fixedParams !== 'object') {
|
|
2090
|
-
fixedParams = {};
|
|
1955
|
+
ngOnChanges(changes) {
|
|
1956
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'form')) {
|
|
1957
|
+
if (changes.form != null) {
|
|
1958
|
+
if (changes.form.currentValue &&
|
|
1959
|
+
changes.form.currentValue != changes.form.previousValue) {
|
|
1960
|
+
this.ngOnInit();
|
|
1961
|
+
}
|
|
2091
1962
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
if (/^"(.*?)"$/.test(searchValue)) { // validate search text in double quotes
|
|
2107
|
-
evaluatedSearch = this._template.transform(searchTemplate, Object.assign({}, fixedParams, { text: searchValue.replace(/^"|"$/g, '') }));
|
|
2108
|
-
}
|
|
2109
|
-
else {
|
|
2110
|
-
// try to split words
|
|
2111
|
-
const words = searchValue.split(' ');
|
|
2112
|
-
// join words to a single filter
|
|
2113
|
-
evaluatedSearch = words.map(word => {
|
|
2114
|
-
return this._template.transform(searchTemplate, Object.assign({}, fixedParams, { text: word }));
|
|
2115
|
-
}).join(' and ');
|
|
1963
|
+
}
|
|
1964
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'formSrc')) {
|
|
1965
|
+
if (changes.formSrc != null) {
|
|
1966
|
+
if (changes.formSrc.currentValue &&
|
|
1967
|
+
changes.formSrc.currentValue != changes.formSrc.previousValue) {
|
|
1968
|
+
this._http.get(changes.formSrc.currentValue).toPromise().then((form) => {
|
|
1969
|
+
// set form
|
|
1970
|
+
this.form = form;
|
|
1971
|
+
// and init
|
|
1972
|
+
this.ngOnInit();
|
|
1973
|
+
}).catch((err) => {
|
|
1974
|
+
console.error('An error occurred while loading search configuration');
|
|
1975
|
+
console.error(err);
|
|
1976
|
+
});
|
|
2116
1977
|
}
|
|
2117
|
-
filterExpressions.push(evaluatedSearch);
|
|
2118
1978
|
}
|
|
2119
|
-
query.setParam('$filter', filterExpressions.join('and'));
|
|
2120
|
-
return query.expand(queryStatement).take(-1).getItems();
|
|
2121
|
-
});
|
|
2122
|
-
}
|
|
2123
|
-
/**
|
|
2124
|
-
*
|
|
2125
|
-
* Formats the received data to use flat keys
|
|
2126
|
-
*
|
|
2127
|
-
* @param config The applied configuration
|
|
2128
|
-
* @param data The raw data
|
|
2129
|
-
*
|
|
2130
|
-
* @returns {Array<any>} The flat-key object list
|
|
2131
|
-
*
|
|
2132
|
-
*/
|
|
2133
|
-
formatData(config, data) {
|
|
2134
|
-
try {
|
|
2135
|
-
return data.map((entry) => {
|
|
2136
|
-
if (entry.subitems && Array.isArray(entry.subitems)) {
|
|
2137
|
-
entry.subitems = this.formatData(config, entry.subitems);
|
|
2138
|
-
}
|
|
2139
|
-
let entryObject = {};
|
|
2140
|
-
for (let i = 0; i < config.columns.length; i++) {
|
|
2141
|
-
const keys = config.columns[i].name.split('/');
|
|
2142
|
-
let helperObject = entry[keys[0]];
|
|
2143
|
-
if (helperObject && keys.length > 1) {
|
|
2144
|
-
for (let i = 1; i < keys.length; i++) {
|
|
2145
|
-
if (helperObject === undefined || helperObject === null) {
|
|
2146
|
-
break;
|
|
2147
|
-
}
|
|
2148
|
-
else {
|
|
2149
|
-
helperObject = helperObject[keys[i]] !== undefined && helperObject[keys[i]] !== null
|
|
2150
|
-
? helperObject[keys[i]]
|
|
2151
|
-
: undefined;
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
entryObject[config.columns[i].name] = helperObject;
|
|
2156
|
-
}
|
|
2157
|
-
if (entry.subitems) {
|
|
2158
|
-
entryObject.subitems = entry.subitems;
|
|
2159
|
-
}
|
|
2160
|
-
return entryObject;
|
|
2161
|
-
});
|
|
2162
1979
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
1980
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'filter')) {
|
|
1981
|
+
if (changes.filter != null) {
|
|
1982
|
+
this.submission.data = changes.filter.currentValue || {};
|
|
1983
|
+
this.refreshForm.emit({
|
|
1984
|
+
submission: this.submission
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
2165
1987
|
}
|
|
2166
1988
|
}
|
|
2167
|
-
}
|
|
2168
|
-
AdvancedAggregatorService.decorators = [
|
|
2169
|
-
{ type: Injectable, args: [{
|
|
2170
|
-
providedIn: 'root'
|
|
2171
|
-
},] }
|
|
2172
|
-
];
|
|
2173
|
-
/** @nocollapse */
|
|
2174
|
-
AdvancedAggregatorService.ctorParameters = () => [
|
|
2175
|
-
{ type: AngularDataContext },
|
|
2176
|
-
{ type: TemplatePipe }
|
|
2177
|
-
];
|
|
2178
|
-
AdvancedAggregatorService.ngInjectableDef = defineInjectable({ factory: function AdvancedAggregatorService_Factory() { return new AdvancedAggregatorService(inject(AngularDataContext), inject(TemplatePipe)); }, token: AdvancedAggregatorService, providedIn: "root" });
|
|
2179
|
-
|
|
2180
|
-
/**
|
|
2181
|
-
*
|
|
2182
|
-
* @class AdvancedAggregatorComponent
|
|
2183
|
-
* @classdesc An advanced list with the capability of aggregate data based on configurable filters
|
|
2184
|
-
*
|
|
2185
|
-
*/
|
|
2186
|
-
class AdvancedAggregatorComponent {
|
|
2187
|
-
constructor(_changeDetector, _advancedAggregatorService, _loadingService) {
|
|
2188
|
-
this._changeDetector = _changeDetector;
|
|
2189
|
-
this._advancedAggregatorService = _advancedAggregatorService;
|
|
2190
|
-
this._loadingService = _loadingService;
|
|
2191
|
-
this.groups = [];
|
|
2192
|
-
this.eventHandler = new EventEmitter();
|
|
2193
|
-
}
|
|
2194
|
-
set config(value) {
|
|
2195
|
-
this._config = value;
|
|
2196
|
-
}
|
|
2197
|
-
set aggregators(value) {
|
|
2198
|
-
this._aggregators = value;
|
|
2199
|
-
}
|
|
2200
|
-
set postProcessors(value) {
|
|
2201
|
-
this._postProcessors = value;
|
|
2202
|
-
}
|
|
2203
|
-
get config() {
|
|
2204
|
-
return this._config;
|
|
2205
|
-
}
|
|
2206
|
-
get aggregators() {
|
|
2207
|
-
return this._aggregators;
|
|
2208
|
-
}
|
|
2209
|
-
get postProcessors() {
|
|
2210
|
-
return this._postProcessors;
|
|
2211
|
-
}
|
|
2212
1989
|
ngOnInit() {
|
|
2213
|
-
|
|
2214
|
-
this.
|
|
2215
|
-
|
|
2216
|
-
|
|
1990
|
+
if (this.form) {
|
|
1991
|
+
new ServiceUrlPreProcessor(this._context).parse(this.form);
|
|
1992
|
+
new TranslatePreProcessor(this._translateService).parse(this.form);
|
|
1993
|
+
this.submission = {
|
|
1994
|
+
data: this.filter
|
|
1995
|
+
};
|
|
1996
|
+
this.refreshForm.emit({
|
|
1997
|
+
form: this.form
|
|
2217
1998
|
});
|
|
2218
|
-
this.
|
|
2219
|
-
|
|
2220
|
-
|
|
1999
|
+
this.loading = false;
|
|
2000
|
+
this.filterSubscription = this.filterChange.subscribe(filter => {
|
|
2001
|
+
this.submission.data = filter;
|
|
2002
|
+
this.refreshForm.emit({
|
|
2003
|
+
submission: this.submission
|
|
2004
|
+
});
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
onChange(event) {
|
|
2009
|
+
Object.keys(this.formComponent.submission.data).forEach(key => {
|
|
2010
|
+
if (Object.prototype.hasOwnProperty.call(this.formComponent.submission.data, key)) {
|
|
2011
|
+
const value = this.formComponent.submission.data[key];
|
|
2012
|
+
// the filter with type is a string and is not empty
|
|
2013
|
+
const isEmptyString = typeof value === 'string' && value.length === 0;
|
|
2014
|
+
// do not include empty objects in filter
|
|
2015
|
+
const isEmptyObject = typeof value === 'object' && Object.keys(value).length === 0;
|
|
2016
|
+
// make isEmptyString, isEmptyObject mutex
|
|
2017
|
+
if (!isEmptyString && !isEmptyObject) {
|
|
2018
|
+
Object.defineProperty(this.filter, key, {
|
|
2019
|
+
configurable: true,
|
|
2020
|
+
enumerable: true,
|
|
2021
|
+
value: value
|
|
2022
|
+
});
|
|
2221
2023
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2024
|
+
else {
|
|
2025
|
+
delete this.filter[key];
|
|
2224
2026
|
}
|
|
2225
|
-
}
|
|
2027
|
+
}
|
|
2226
2028
|
});
|
|
2227
2029
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
});
|
|
2030
|
+
onFormLoad(form) {
|
|
2031
|
+
const currentLang = this._translateService.currentLang;
|
|
2032
|
+
this.formComponent.formio.i18next.options.resources[currentLang] = { translation: this._translateService.instant(`Forms`) };
|
|
2033
|
+
this.formComponent.formio.language = currentLang;
|
|
2233
2034
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
* Retrieves the data
|
|
2237
|
-
*
|
|
2238
|
-
*/
|
|
2239
|
-
fetch(filters) {
|
|
2240
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2241
|
-
try {
|
|
2242
|
-
this._loadingService.showLoading();
|
|
2243
|
-
let data = yield this._advancedAggregatorService.fetchData(this.config.model, this.params, this.config.query, filters.filters, this.searchExpression, filters.text);
|
|
2244
|
-
if (this.postProcessors && Object.keys(this.postProcessors).length > 0 && this.config.postProcessors && this.config.postProcessors.length > 0) {
|
|
2245
|
-
for (let i = 0; i < this.config.postProcessors.length; i++) {
|
|
2246
|
-
data = yield this.postProcessors[this.config.postProcessors[i].name](data);
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
this.body = this._advancedAggregatorService.formatData(this.config, data);
|
|
2250
|
-
this._changeDetector.markForCheck();
|
|
2251
|
-
}
|
|
2252
|
-
catch (err) {
|
|
2253
|
-
console.error(err);
|
|
2254
|
-
}
|
|
2255
|
-
finally {
|
|
2256
|
-
this._loadingService.hideLoading();
|
|
2257
|
-
}
|
|
2258
|
-
});
|
|
2035
|
+
ngAfterViewInit() {
|
|
2036
|
+
//
|
|
2259
2037
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
* @param {*} event The event data
|
|
2265
|
-
*
|
|
2266
|
-
*/
|
|
2267
|
-
onActionEvent(event) {
|
|
2268
|
-
this.eventHandler.emit(event);
|
|
2038
|
+
ngOnDestroy() {
|
|
2039
|
+
if (this.filterSubscription) {
|
|
2040
|
+
this.filterSubscription.unsubscribe();
|
|
2041
|
+
}
|
|
2269
2042
|
}
|
|
2270
2043
|
}
|
|
2271
|
-
|
|
2044
|
+
AdvancedSearchFormComponent.decorators = [
|
|
2272
2045
|
{ type: Component, args: [{
|
|
2273
|
-
selector: 'app-advanced-
|
|
2274
|
-
template:
|
|
2275
|
-
|
|
2046
|
+
selector: 'app-advanced-search-form',
|
|
2047
|
+
template: `
|
|
2048
|
+
<div class="p-4">
|
|
2049
|
+
<formio #formioComponent class="formio-search" (keydown)="onKeyEnter($event)" (change)="onChange($event)" (formLoad)="onFormLoad($event)"
|
|
2050
|
+
[refresh]="refreshForm" [submission]="submission" [form]="form"></formio>
|
|
2051
|
+
</div>
|
|
2052
|
+
`,
|
|
2053
|
+
encapsulation: ViewEncapsulation.None,
|
|
2054
|
+
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}"]
|
|
2276
2055
|
}] }
|
|
2277
2056
|
];
|
|
2278
2057
|
/** @nocollapse */
|
|
2279
|
-
|
|
2280
|
-
{ type:
|
|
2281
|
-
{ type:
|
|
2282
|
-
{ type:
|
|
2058
|
+
AdvancedSearchFormComponent.ctorParameters = () => [
|
|
2059
|
+
{ type: AngularDataContext },
|
|
2060
|
+
{ type: TranslateService },
|
|
2061
|
+
{ type: ActivatedRoute },
|
|
2062
|
+
{ type: HttpClient }
|
|
2283
2063
|
];
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
selectedGroups$: [{ type: Input, args: ['selectedGroups',] }],
|
|
2290
|
-
filters$: [{ type: Input, args: ['filters',] }],
|
|
2291
|
-
searchExpression: [{ type: Input, args: ['searchExpression',] }],
|
|
2292
|
-
eventHandler: [{ type: Output }]
|
|
2293
|
-
};
|
|
2294
|
-
|
|
2295
|
-
var Lengths;
|
|
2296
|
-
(function (Lengths) {
|
|
2297
|
-
Lengths["XL"] = "xl";
|
|
2298
|
-
Lengths["LG"] = "lg";
|
|
2299
|
-
Lengths["MD"] = "md";
|
|
2300
|
-
Lengths["SM"] = "sm";
|
|
2301
|
-
Lengths["XS"] = "xs";
|
|
2302
|
-
})(Lengths || (Lengths = {}));
|
|
2303
|
-
class AdvancedAggregatorListComponent {
|
|
2304
|
-
constructor(_template) {
|
|
2305
|
-
this._template = _template;
|
|
2306
|
-
this.clickEvent = new EventEmitter();
|
|
2307
|
-
}
|
|
2308
|
-
ngOnInit() {
|
|
2309
|
-
this.config.columns.forEach(x => {
|
|
2310
|
-
x.length = Object.values(Lengths).includes(x.length) ? this._template.transform(`sis--list-group-item_${x.length}_column`, x) :
|
|
2311
|
-
!x.length ? 'sis--list-group-item_sm_column' : x.length;
|
|
2312
|
-
});
|
|
2313
|
-
}
|
|
2314
|
-
/**
|
|
2315
|
-
*
|
|
2316
|
-
* Triggers the output event emitter
|
|
2317
|
-
*
|
|
2318
|
-
* @param type The type of the event as it's declared in the configuration
|
|
2319
|
-
* @param item The value of the current item
|
|
2320
|
-
* }
|
|
2321
|
-
*/
|
|
2322
|
-
callCallback(type, data) {
|
|
2323
|
-
this.clickEvent.emit({
|
|
2324
|
-
type,
|
|
2325
|
-
data
|
|
2326
|
-
});
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
AdvancedAggregatorListComponent.decorators = [
|
|
2330
|
-
{ type: Component, args: [{
|
|
2331
|
-
selector: 'app-advanced-aggregator-list',
|
|
2332
|
-
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 *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n <span class=\"px-0\">\n <i class=\"fa fa-fw\"> </i>\n </span>\n\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 <button *ngIf=\"config.columns[i].type === 'action' && config.columns[i].variant === 'edit'\"\n (click)=\"callCallback(config.columns[i].identifier, entry)\"\n [title]=\"(config.columns[i].title ? config.columns[i].title : '') | translate \"\n class=\"btn btn-default px-0 mx-0\">\n <i class=\"fas fa-edit text-indigo\"></i>\n </button>\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 <div *ngIf=\"entry && entry.subitems\">\n <div *ngFor=\"let subitem of entry.subitems\" class=\"d-flex justify-content-center align-items-center list-color\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <span class=\"px-0\">\n <i class=\"fa fa-fw\"> </i>\n </span>\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, subitem)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n </div>\n <div class=\"text-left\"></div>\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') && 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 <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': subitem[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': subitem[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"subitem[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 </div>\n </li>\n </ul>\n</div>\n"
|
|
2333
|
-
}] }
|
|
2334
|
-
];
|
|
2335
|
-
/** @nocollapse */
|
|
2336
|
-
AdvancedAggregatorListComponent.ctorParameters = () => [
|
|
2337
|
-
{ type: TemplatePipe }
|
|
2338
|
-
];
|
|
2339
|
-
AdvancedAggregatorListComponent.propDecorators = {
|
|
2340
|
-
config: [{ type: Input }],
|
|
2341
|
-
body: [{ type: Input }],
|
|
2342
|
-
clickEvent: [{ type: Output }]
|
|
2064
|
+
AdvancedSearchFormComponent.propDecorators = {
|
|
2065
|
+
formComponent: [{ type: ViewChild, args: ['formioComponent',] }],
|
|
2066
|
+
form: [{ type: Input }],
|
|
2067
|
+
formSrc: [{ type: Input }],
|
|
2068
|
+
refreshForm: [{ type: Output }]
|
|
2343
2069
|
};
|
|
2344
2070
|
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
this.dataSubscription = this._activatedRoute.data.subscribe(routeData => {
|
|
2374
|
-
// get entityType
|
|
2375
|
-
const re = new RegExp(`^${routeData.model}$`, 'ig');
|
|
2376
|
-
const entitySet = metadata.EntityContainer.EntitySet.find(x => {
|
|
2377
|
-
return re.test(x.Name);
|
|
2378
|
-
});
|
|
2379
|
-
// if entity type is null
|
|
2380
|
-
if (entitySet == null) {
|
|
2381
|
-
// navigate to error
|
|
2382
|
-
return this._errorService.navigateToError(new ResponseError('EntitySet Not Found', 404));
|
|
2383
|
-
}
|
|
2384
|
-
// get entity type
|
|
2385
|
-
const entityType = metadata.EntityType.find(x => {
|
|
2386
|
-
return x.Name === entitySet.EntityType;
|
|
2387
|
-
});
|
|
2388
|
-
if (entityType == null) {
|
|
2389
|
-
return this._errorService.navigateToError(new ResponseError('EntityType Not Found', 404));
|
|
2390
|
-
}
|
|
2391
|
-
if (routeData.searchConfiguration) {
|
|
2392
|
-
if (Object.prototype.hasOwnProperty.call(routeData.searchConfiguration, 'allowSave')) {
|
|
2393
|
-
this.allowSaveOption = routeData.searchConfiguration.allowSave;
|
|
2394
|
-
}
|
|
2395
|
-
this.searchConfiguration = routeData.searchConfiguration;
|
|
2396
|
-
}
|
|
2397
|
-
if (routeData.tableConfiguration) {
|
|
2398
|
-
this.tableConfiguration = routeData.tableConfiguration;
|
|
2399
|
-
}
|
|
2400
|
-
if (routeData.description) {
|
|
2401
|
-
this.description = this._translateService.instant(routeData.description);
|
|
2402
|
-
}
|
|
2403
|
-
if (routeData.longDescription) {
|
|
2404
|
-
this.longDescription = this._translateService.instant(routeData.longDescription);
|
|
2405
|
-
}
|
|
2406
|
-
if (routeData.category) {
|
|
2407
|
-
this.category = this._translateService.instant(routeData.category);
|
|
2408
|
-
}
|
|
2409
|
-
// clone default configuration
|
|
2410
|
-
const defaultConfiguration = AdvancedTableConfiguration.cast(this.tableConfiguration, true);
|
|
2411
|
-
// correct edit button link
|
|
2412
|
-
const column = defaultConfiguration.columns.find(col => {
|
|
2413
|
-
return col.formatter === 'ButtonFormatter' && col.formatString === '(modal:edit)';
|
|
2414
|
-
});
|
|
2415
|
-
if (column) {
|
|
2416
|
-
const url = [];
|
|
2417
|
-
this._activatedRoute.snapshot.pathFromRoot.forEach(route => {
|
|
2418
|
-
url.push.apply(url, route.url);
|
|
2419
|
-
});
|
|
2420
|
-
column.formatString = `#/${url.join('/')}/(modal:\$\{id\}/edit)`;
|
|
2421
|
-
}
|
|
2422
|
-
// create list configuration by assigning list entity set
|
|
2423
|
-
this.table.config = Object.assign({
|
|
2424
|
-
model: entitySet.Name
|
|
2425
|
-
}, defaultConfiguration);
|
|
2426
|
-
// fetch data
|
|
2427
|
-
this.table.fetch(true);
|
|
2428
|
-
// do reload by using hidden fragment e.g. /classes#reload
|
|
2429
|
-
this.reloadSubscription = this._activatedRoute.fragment.subscribe(fragment => {
|
|
2430
|
-
if (fragment && fragment === 'reload') {
|
|
2431
|
-
this._toastService.show(this.description, this._translateService.instant('Settings.OperationCompleted'));
|
|
2432
|
-
this.table.fetch(true);
|
|
2433
|
-
}
|
|
2434
|
-
});
|
|
2435
|
-
// finally add user activity
|
|
2436
|
-
this._userActivityService.setItem({
|
|
2437
|
-
category: this.category,
|
|
2438
|
-
description: this.description,
|
|
2439
|
-
url: window.location.hash.substring(1),
|
|
2440
|
-
dateCreated: new Date
|
|
2441
|
-
});
|
|
2442
|
-
});
|
|
2443
|
-
}
|
|
2444
|
-
catch (err) {
|
|
2445
|
-
console.error(err);
|
|
2446
|
-
return this._errorService.showError(err);
|
|
2447
|
-
}
|
|
2448
|
-
});
|
|
2449
|
-
}
|
|
2450
|
-
/**
|
|
2451
|
-
* Data load event handler of advanced table component
|
|
2452
|
-
* @param {AdvancedTableDataResult} data
|
|
2453
|
-
*/
|
|
2454
|
-
onDataLoad(data) {
|
|
2455
|
-
this.recordsTotal = data.recordsTotal;
|
|
2456
|
-
}
|
|
2457
|
-
/**
|
|
2458
|
-
* Remove selected items
|
|
2459
|
-
*/
|
|
2460
|
-
remove() {
|
|
2461
|
-
if (this.table && this.table.selected && this.table.selected.length) {
|
|
2462
|
-
// get items to remove
|
|
2463
|
-
const items = this.table.selected;
|
|
2464
|
-
return this._modalService.showWarningDialog(this._translateService.instant('Tables.RemoveItemsTitle'), this._translateService.instant('Tables.RemoveItemsMessage'), DIALOG_BUTTONS.OkCancel).then(result => {
|
|
2465
|
-
if (result === 'ok') {
|
|
2466
|
-
this._context.model(this.table.config.model).remove(items).then(() => {
|
|
2467
|
-
this._toastService.show(this.description + ':' + this._translateService.instant('Tables.RemoveItemMessage.title'), this._translateService.instant((items.length === 1 ?
|
|
2468
|
-
'Tables.RemoveItemMessage.one' : 'Tables.RemoveItemMessage.many'), { value: items.length }));
|
|
2469
|
-
this.table.fetch(true);
|
|
2470
|
-
}).catch(err => {
|
|
2471
|
-
this._errorService.showError(err, {
|
|
2472
|
-
continueLink: '.'
|
|
2473
|
-
});
|
|
2474
|
-
});
|
|
2475
|
-
}
|
|
2476
|
-
});
|
|
2477
|
-
}
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
AdvancedListComponent.decorators = [
|
|
2481
|
-
{ type: Component, args: [{
|
|
2482
|
-
selector: 'app-advanced-list',
|
|
2483
|
-
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"
|
|
2484
|
-
}] }
|
|
2485
|
-
];
|
|
2486
|
-
/** @nocollapse */
|
|
2487
|
-
AdvancedListComponent.ctorParameters = () => [
|
|
2488
|
-
{ type: AngularDataContext },
|
|
2489
|
-
{ type: TranslateService },
|
|
2490
|
-
{ type: ActivatedRoute },
|
|
2491
|
-
{ type: ErrorService },
|
|
2492
|
-
{ type: ToastService },
|
|
2493
|
-
{ type: ModalService },
|
|
2494
|
-
{ type: UserActivityService }
|
|
2495
|
-
];
|
|
2496
|
-
AdvancedListComponent.propDecorators = {
|
|
2497
|
-
entitySet: [{ type: Input, args: ['entitySet',] }],
|
|
2498
|
-
category: [{ type: Input, args: ['category',] }],
|
|
2499
|
-
description: [{ type: Input, args: ['description',] }],
|
|
2500
|
-
longDescription: [{ type: Input, args: ['longDescription',] }],
|
|
2501
|
-
tableConfiguration: [{ type: Input, args: ['tableConfiguration',] }],
|
|
2502
|
-
searchConfiguration: [{ type: Input, args: ['searchConfiguration',] }],
|
|
2503
|
-
table: [{ type: ViewChild, args: ['table',] }],
|
|
2504
|
-
allowSaveOption: [{ type: Input, args: ['allowSaveOption',] }]
|
|
2505
|
-
};
|
|
2071
|
+
// important: export this const due to AOT compilation errors
|
|
2072
|
+
// see more https://angular.io/guide/aot-metadata-errors#only-initialized-variables
|
|
2073
|
+
const AdvancedTableModalBaseTemplate = `
|
|
2074
|
+
<div>
|
|
2075
|
+
<!-- search -->
|
|
2076
|
+
<div class="row mt-3">
|
|
2077
|
+
<div class="col-12 col-lg-3">
|
|
2078
|
+
<div class="input-group mb-3">
|
|
2079
|
+
<input spellcheck="false" type="text"
|
|
2080
|
+
(keyup)="onKeyUp($event)" [(ngModel)]="searchText"
|
|
2081
|
+
class="form-control bg-gray-100 border-0"
|
|
2082
|
+
placeholder="{{'Tables.SearchPlaceholder' | translate}}" aria-label="Search" aria-describedby="basic-addon2">
|
|
2083
|
+
<div class="input-group-append">
|
|
2084
|
+
<button class="btn btn-default bg-gray-100 border-0" type="button">
|
|
2085
|
+
<i class="fa fa-search text-secondary"></i>
|
|
2086
|
+
</button>
|
|
2087
|
+
</div>
|
|
2088
|
+
</div>
|
|
2089
|
+
</div>
|
|
2090
|
+
<div #emptyTable class="d-none">
|
|
2091
|
+
<div>
|
|
2092
|
+
<div class="mt-4">
|
|
2093
|
+
<div class="icon-circle bg-gray-300 border-gray-100"></div>
|
|
2094
|
+
</div>
|
|
2095
|
+
<div class="mt-4">
|
|
2096
|
+
<h4 class="font-3xl text-dark" [translate]="'Tables.EmptyTableTitle'"></h4>
|
|
2097
|
+
<p class="font-lg text-dark" [translate]="'Tables.EmptyTableMessage'"></p>
|
|
2098
|
+
</div>
|
|
2506
2099
|
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2100
|
+
</div>
|
|
2101
|
+
</div>
|
|
2102
|
+
<div class="col-12 col-lg-5">
|
|
2103
|
+
<div class="btn-toolbar">
|
|
2104
|
+
<button class="btn btn-gray-100">
|
|
2105
|
+
<i class="fa fa-sliders-h pr-2 text-secondary" aria-hidden="true"></i>{{'Tables.Filters' | translate}}
|
|
2106
|
+
</button>
|
|
2107
|
+
<div #dropdownFilter class="dropdown ml-3">
|
|
2108
|
+
<button class="btn btn-gray-100 dropdown-toggle"
|
|
2109
|
+
type="button" id="dropdownDefaultFilters"
|
|
2110
|
+
data-toggle="dropdown" dropdownToggle>
|
|
2111
|
+
{{ this.defaultFilter.name | translate }}
|
|
2112
|
+
</button>
|
|
2113
|
+
<div class="dropdown-menu" aria-labelledby="dropdownDefaultFilters">
|
|
2114
|
+
<button type="button" class="dropdown-item" *ngFor="let filter of defaultFilters" (click)="onFilterSelect(filter)">
|
|
2115
|
+
{{ filter.name | translate }}
|
|
2116
|
+
</button>
|
|
2117
|
+
</div>
|
|
2118
|
+
</div>
|
|
2119
|
+
</div>
|
|
2120
|
+
</div>
|
|
2121
|
+
<div class="col text-lg-right mt-3 mt-lg-0 d-none">
|
|
2122
|
+
<button [disabled]="true" class="btn rounded-pill btn-outline-theme">
|
|
2123
|
+
<span class="pr-2 font-lg">+</span><span [translate]="'Tables.AddItem'"></span>
|
|
2124
|
+
</button>
|
|
2125
|
+
</div>
|
|
2126
|
+
</div>
|
|
2127
|
+
<div class="row">
|
|
2128
|
+
<div class="col-12" [ngClass]="{ 'component-loading': loading}">
|
|
2129
|
+
<app-advanced-table #advancedTable (init)="onTableInit($event)"
|
|
2130
|
+
(load)="onDataLoad($event)"
|
|
2131
|
+
[selectable]="true"
|
|
2132
|
+
[multipleSelect]="true"
|
|
2133
|
+
[scrollX]="false" [scrollY]="320"
|
|
2134
|
+
[pageLength]="pageLength"
|
|
2135
|
+
[scrollable]="false" [showFooter]="false"
|
|
2136
|
+
[showActions]="false" [lengthMenu]="lengthMenu"
|
|
2137
|
+
[config]="tableConfig" [configSrc]="tableConfigSrc"></app-advanced-table>
|
|
2138
|
+
</div>
|
|
2139
|
+
</div>
|
|
2140
|
+
<div class="row mt-3 table-custom-pager">
|
|
2141
|
+
<div class="col-12 d-flex" *ngIf="recordsTotal">
|
|
2142
|
+
<div class="mt-2 ml-auto mr-3 text-secondary" [translate]="'Tables.RecordsPerPage'"></div>
|
|
2143
|
+
<div #dropdownLength class="dropdown mr-3">
|
|
2144
|
+
<button class="btn btn-gray-100 dropdown-toggle"
|
|
2145
|
+
type="button" id="dropdownPageSize" data-toggle="dropdown"
|
|
2146
|
+
dropdownToggle>
|
|
2147
|
+
{{pageLength}}
|
|
2148
|
+
</button>
|
|
2149
|
+
<div class="dropdown-menu" aria-labelledby="dropdownPageSize">
|
|
2150
|
+
<button (click)="setLength(item)"
|
|
2151
|
+
class="dropdown-item dropdown-item-theme"
|
|
2152
|
+
type="button" *ngFor="let item of lengthMenu">{{item}}</button>
|
|
2153
|
+
</div>
|
|
2154
|
+
</div>
|
|
2155
|
+
<div class="mt-2 mr-2" [translate]="'Tables.PageInfo'" [translateParams]="pageInfo"></div>
|
|
2156
|
+
<button [disabled]="pageInfo && pageInfo.page === 0" class="btn text-nowrap" (click)="firstPage()">
|
|
2157
|
+
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
2158
|
+
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
2159
|
+
</button>
|
|
2160
|
+
<button [disabled]="pageInfo && pageInfo.page===0" class="btn" (click)="previousPage()">
|
|
2161
|
+
<i class="fa fa-chevron-left text-color-theme"></i>
|
|
2162
|
+
</button>
|
|
2163
|
+
<button [disabled]="pageInfo && pageInfo.page===pageInfo.pages-1" class="btn" (click)="nextPage()">
|
|
2164
|
+
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
2165
|
+
</button>
|
|
2166
|
+
<button [disabled]="pageInfo && pageInfo.page===pageInfo.pages-1" class="btn text-nowrap" (click)="lastPage()">
|
|
2167
|
+
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
2168
|
+
<i class="fa fa-chevron-right text-color-theme"></i>
|
|
2169
|
+
</button>
|
|
2170
|
+
</div>
|
|
2171
|
+
</div>
|
|
2172
|
+
</div>
|
|
2173
|
+
`;
|
|
2174
|
+
class AdvancedTableModalBaseComponent extends RouterModalOkCancel {
|
|
2175
|
+
constructor(_router, _activatedRoute, _context, advancedFilterValueProvider, datePipe) {
|
|
2176
|
+
super(_router, _activatedRoute);
|
|
2511
2177
|
this._context = _context;
|
|
2512
|
-
this.
|
|
2513
|
-
this.
|
|
2514
|
-
this.
|
|
2515
|
-
this.
|
|
2516
|
-
this.
|
|
2517
|
-
this.errorMessage = 'Tables.Actions.CompletedWithErrors';
|
|
2178
|
+
this.advancedFilterValueProvider = advancedFilterValueProvider;
|
|
2179
|
+
this.datePipe = datePipe;
|
|
2180
|
+
this.pageLength = 25;
|
|
2181
|
+
this.lengthMenu = [5, 10, 25, 50, 100];
|
|
2182
|
+
this.recordsTotal = 0;
|
|
2518
2183
|
this.loading = false;
|
|
2519
|
-
this.
|
|
2520
|
-
|
|
2521
|
-
this.
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
this.
|
|
2526
|
-
|
|
2527
|
-
this.okButtonText = 'Tables.Actions.Start';
|
|
2528
|
-
}
|
|
2529
|
-
get form() {
|
|
2530
|
-
return this.formComponent;
|
|
2531
|
-
}
|
|
2532
|
-
ngOnInit() {
|
|
2533
|
-
this.refreshSubscription = this.refresh.subscribe((value) => {
|
|
2534
|
-
if (value && value.progress) {
|
|
2535
|
-
let progress = 0;
|
|
2536
|
-
if (value.progress < 0) {
|
|
2537
|
-
progress = 0;
|
|
2538
|
-
}
|
|
2539
|
-
else if (value.progress > 100) {
|
|
2540
|
-
progress = 100;
|
|
2541
|
-
}
|
|
2542
|
-
else {
|
|
2543
|
-
progress = value.progress;
|
|
2544
|
-
}
|
|
2545
|
-
this.progressComponent.value = progress;
|
|
2546
|
-
this.showProgress();
|
|
2547
|
-
}
|
|
2548
|
-
});
|
|
2184
|
+
this.recordsFiltered = 0;
|
|
2185
|
+
this.defaultFilters = [AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER];
|
|
2186
|
+
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
2187
|
+
this.pageInfo = {
|
|
2188
|
+
serverSide: true
|
|
2189
|
+
};
|
|
2190
|
+
this.modalClass = 'modal-xl modal-table';
|
|
2191
|
+
this._template = new TemplatePipe();
|
|
2549
2192
|
}
|
|
2550
2193
|
ngAfterViewInit() {
|
|
2551
|
-
|
|
2552
|
-
this.okButtonDisabled = true;
|
|
2553
|
-
this.invalidSubscription = this.formComponent.form.change.subscribe((event) => {
|
|
2554
|
-
if (Object.prototype.hasOwnProperty.call(event, "isValid") && (event.changed != null)) {
|
|
2555
|
-
this.okButtonDisabled = !event.isValid;
|
|
2556
|
-
}
|
|
2557
|
-
});
|
|
2558
|
-
}
|
|
2194
|
+
//
|
|
2559
2195
|
}
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
this.executeSubscription.unsubscribe();
|
|
2566
|
-
}
|
|
2567
|
-
if (this.invalidSubscription) {
|
|
2568
|
-
this.invalidSubscription.unsubscribe();
|
|
2196
|
+
_getInputs(componentType) {
|
|
2197
|
+
const props = componentType.__prop__metadata__;
|
|
2198
|
+
const inputs = [];
|
|
2199
|
+
if (props == null) {
|
|
2200
|
+
return inputs;
|
|
2569
2201
|
}
|
|
2202
|
+
Object.keys(props).forEach(prop => {
|
|
2203
|
+
const member = props[prop][0];
|
|
2204
|
+
if (member.ngMetadataName === 'Input') {
|
|
2205
|
+
inputs.push(prop);
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
return inputs.sort();
|
|
2570
2209
|
}
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
return;
|
|
2574
|
-
}
|
|
2575
|
-
// close
|
|
2576
|
-
if (this._modalService.modalRef) {
|
|
2577
|
-
return this._modalService.modalRef.hide();
|
|
2578
|
-
}
|
|
2210
|
+
hasInputs() {
|
|
2211
|
+
return [];
|
|
2579
2212
|
}
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
this.data = this.formComponent.form.formio.data;
|
|
2589
|
-
}
|
|
2590
|
-
// do action
|
|
2591
|
-
this.execute.subscribe((result) => {
|
|
2592
|
-
this.loading = false;
|
|
2593
|
-
this.refresh.emit({
|
|
2594
|
-
progress: 100
|
|
2595
|
-
});
|
|
2596
|
-
if (result && result.errors && result.errors > 0) {
|
|
2597
|
-
// show message for partial success
|
|
2598
|
-
if (result.errors === 1) {
|
|
2599
|
-
this.lastError = new Error(this._translateService.instant(`${this.errorMessage}.Description.One`));
|
|
2600
|
-
}
|
|
2601
|
-
else {
|
|
2602
|
-
this.lastError = new Error(this._translateService.instant(`${this.errorMessage}.Description.Many`, result));
|
|
2603
|
-
}
|
|
2604
|
-
this.refresh.emit({
|
|
2605
|
-
progress: 1
|
|
2606
|
-
});
|
|
2607
|
-
this.hideProgress();
|
|
2608
|
-
return;
|
|
2609
|
-
}
|
|
2610
|
-
if (this._modalService.modalRef) {
|
|
2611
|
-
this._modalService.modalRef.hide();
|
|
2612
|
-
}
|
|
2613
|
-
}, (err) => {
|
|
2614
|
-
this.loading = false;
|
|
2615
|
-
this.lastError = err;
|
|
2616
|
-
this.refresh.emit({
|
|
2617
|
-
progress: 0
|
|
2618
|
-
});
|
|
2619
|
-
});
|
|
2213
|
+
ngOnInit() {
|
|
2214
|
+
this.okButtonText = 'Tables.Apply';
|
|
2215
|
+
this.cancelButtonText = 'Tables.Cancel';
|
|
2216
|
+
// get route data
|
|
2217
|
+
this.subscription = this.activatedRoute.data.subscribe(data => {
|
|
2218
|
+
// set modal title if any
|
|
2219
|
+
if (data.title) {
|
|
2220
|
+
this.modalTitle = data.title;
|
|
2620
2221
|
}
|
|
2621
|
-
|
|
2622
|
-
this.
|
|
2623
|
-
|
|
2222
|
+
if (data.config) {
|
|
2223
|
+
this.tableConfig = data.config;
|
|
2224
|
+
}
|
|
2225
|
+
if (this.tableConfig) {
|
|
2226
|
+
this.defaultFilters.push.apply(this.defaultFilters, this.tableConfig.searches);
|
|
2624
2227
|
}
|
|
2228
|
+
// important: use hasInputs due to AOT compilation. Metadata properties cannot be used (e.g. @Input())
|
|
2229
|
+
// because there are empty during runtime
|
|
2230
|
+
// so we need to define manually input properties that are going to be bind through this process
|
|
2231
|
+
// bind all inputs
|
|
2232
|
+
const inputs = this.hasInputs();
|
|
2233
|
+
inputs.forEach(input => {
|
|
2234
|
+
if (Object.prototype.hasOwnProperty.call(data, input)) {
|
|
2235
|
+
this[input] = data[input];
|
|
2236
|
+
}
|
|
2237
|
+
});
|
|
2238
|
+
this.configChangesSubscription = this.advancedTable.configChanges.subscribe((config) => {
|
|
2239
|
+
if (this.tableConfig) {
|
|
2240
|
+
// reset default filter
|
|
2241
|
+
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
2242
|
+
// init filter
|
|
2243
|
+
this.defaultFilters = [AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER];
|
|
2244
|
+
// add extra filters
|
|
2245
|
+
this.defaultFilters.push.apply(this.defaultFilters, this.tableConfig.searches);
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2625
2248
|
});
|
|
2626
2249
|
}
|
|
2627
|
-
|
|
2628
|
-
this.
|
|
2250
|
+
cancel() {
|
|
2251
|
+
return this.close();
|
|
2629
2252
|
}
|
|
2630
|
-
|
|
2631
|
-
this.
|
|
2253
|
+
ok() {
|
|
2254
|
+
return this.close();
|
|
2632
2255
|
}
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
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 invisible\" #progressContainer>\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",
|
|
2638
|
-
encapsulation: ViewEncapsulation.None,
|
|
2639
|
-
styles: [`
|
|
2640
|
-
.fa-1_5x {
|
|
2641
|
-
font-size: 1.5rem;
|
|
2642
|
-
}
|
|
2643
|
-
.form-control:disabled, .form-control[readonly] {
|
|
2644
|
-
background-color: inherit;
|
|
2645
|
-
}
|
|
2646
|
-
.form-check.checkbox {
|
|
2647
|
-
position: relative
|
|
2648
|
-
}
|
|
2649
|
-
`]
|
|
2650
|
-
}] }
|
|
2651
|
-
];
|
|
2652
|
-
/** @nocollapse */
|
|
2653
|
-
AdvancedRowActionComponent.ctorParameters = () => [
|
|
2654
|
-
{ type: Router },
|
|
2655
|
-
{ type: ActivatedRoute },
|
|
2656
|
-
{ type: TranslateService },
|
|
2657
|
-
{ type: AngularDataContext },
|
|
2658
|
-
{ type: ErrorService },
|
|
2659
|
-
{ type: ModalService },
|
|
2660
|
-
{ type: AppEventService }
|
|
2661
|
-
];
|
|
2662
|
-
AdvancedRowActionComponent.propDecorators = {
|
|
2663
|
-
items: [{ type: Input }],
|
|
2664
|
-
modalIcon: [{ type: Input }],
|
|
2665
|
-
description: [{ type: Input }],
|
|
2666
|
-
formTemplate: [{ type: Input }],
|
|
2667
|
-
progressType: [{ type: Input }],
|
|
2668
|
-
data: [{ type: Output }],
|
|
2669
|
-
additionalMessage: [{ type: Input }],
|
|
2670
|
-
errorMessage: [{ type: Input }],
|
|
2671
|
-
progressComponent: [{ type: ViewChild, args: ['progress',] }],
|
|
2672
|
-
progressContainer: [{ type: ViewChild, args: ['progressContainer',] }],
|
|
2673
|
-
formComponent: [{ type: ViewChild, args: ['formComponent',] }],
|
|
2674
|
-
refresh: [{ type: Input }],
|
|
2675
|
-
execute: [{ type: Input }]
|
|
2676
|
-
};
|
|
2677
|
-
|
|
2678
|
-
class AdvancedTableSearchBaseComponent {
|
|
2679
|
-
constructor() {
|
|
2680
|
-
this.filterChange = new EventEmitter();
|
|
2256
|
+
ngOnDestroy() {
|
|
2257
|
+
if (this.subscription) {
|
|
2258
|
+
this.subscription.unsubscribe();
|
|
2259
|
+
}
|
|
2681
2260
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2261
|
+
onDataLoad(data) {
|
|
2262
|
+
this.recordsTotal = data.recordsTotal;
|
|
2263
|
+
this.recordsFiltered = data.recordsFiltered;
|
|
2264
|
+
// get first page info (because page is not going to be fired for first page)
|
|
2265
|
+
if (data.recordsTotal > 0 && this.pageInfo.recordsTotal === 0) {
|
|
2266
|
+
this.pageInfo.recordsTotal = data.recordsTotal;
|
|
2267
|
+
}
|
|
2684
2268
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2269
|
+
previousPage() {
|
|
2270
|
+
// go to next page
|
|
2271
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2272
|
+
this.advancedTable.dataTable.page('previous').draw('page');
|
|
2687
2273
|
}
|
|
2688
|
-
|
|
2274
|
+
nextPage() {
|
|
2275
|
+
// go to next page
|
|
2276
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2277
|
+
this.advancedTable.dataTable.page('next').draw('page');
|
|
2278
|
+
}
|
|
2279
|
+
onTableInit(dataTable) {
|
|
2280
|
+
const self = this;
|
|
2281
|
+
// get initial page info
|
|
2282
|
+
const initialPageInfo = dataTable.page.info();
|
|
2283
|
+
initialPageInfo.start += 1;
|
|
2284
|
+
self.pageInfo = initialPageInfo;
|
|
2285
|
+
// change emptyTableMessage
|
|
2286
|
+
dataTable.context[0].oLanguage.sEmptyTable = this.emptyTable.nativeElement.innerHTML;
|
|
2287
|
+
// bind page change event
|
|
2288
|
+
dataTable.on('page.dt', function () {
|
|
2289
|
+
const pageInfo = dataTable.page.info();
|
|
2290
|
+
pageInfo.start += 1;
|
|
2291
|
+
self.pageInfo = pageInfo;
|
|
2292
|
+
});
|
|
2293
|
+
// bind draw change event
|
|
2294
|
+
dataTable.on('draw.dt', function () {
|
|
2295
|
+
// for future use
|
|
2296
|
+
self.loading = false;
|
|
2297
|
+
const pageInfo = dataTable.page.info();
|
|
2298
|
+
pageInfo.start += 1;
|
|
2299
|
+
self.pageInfo = pageInfo;
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
firstPage() {
|
|
2303
|
+
// go to first page
|
|
2304
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2305
|
+
this.advancedTable.dataTable.page('first').draw('page');
|
|
2306
|
+
}
|
|
2307
|
+
lastPage() {
|
|
2308
|
+
// go to last page
|
|
2309
|
+
// noinspection TypeScriptValidateJSTypes
|
|
2310
|
+
this.advancedTable.dataTable.page('last').draw('page');
|
|
2311
|
+
}
|
|
2312
|
+
setLength(value) {
|
|
2313
|
+
this.pageLength = value;
|
|
2314
|
+
this.advancedTable.dataTable.page.len(value).draw();
|
|
2315
|
+
}
|
|
2316
|
+
onKeyUp(event) {
|
|
2689
2317
|
if (event.code === 'Enter') {
|
|
2690
|
-
|
|
2318
|
+
// loading
|
|
2319
|
+
this.loading = true;
|
|
2320
|
+
this.advancedTable.search(event.target.value);
|
|
2691
2321
|
}
|
|
2692
2322
|
}
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
this.refreshForm = new EventEmitter();
|
|
2708
|
-
this.submission = { data: {} };
|
|
2709
|
-
this.loading = true;
|
|
2710
|
-
}
|
|
2711
|
-
ngOnChanges(changes) {
|
|
2712
|
-
if (Object.prototype.hasOwnProperty.call(changes, 'form')) {
|
|
2713
|
-
if (changes.form != null) {
|
|
2714
|
-
if (changes.form.currentValue &&
|
|
2715
|
-
changes.form.currentValue != changes.form.previousValue) {
|
|
2716
|
-
this.ngOnInit();
|
|
2717
|
-
}
|
|
2718
|
-
}
|
|
2323
|
+
/**
|
|
2324
|
+
*
|
|
2325
|
+
* Reads the modal table and returns a list of filters to use in the table
|
|
2326
|
+
* query.
|
|
2327
|
+
*
|
|
2328
|
+
* @param tableConfig {object} The table configuration from json file
|
|
2329
|
+
* @param defaultFilterValues {object} A list of default filters
|
|
2330
|
+
* @param {object} searchItem The searchItem to use
|
|
2331
|
+
*
|
|
2332
|
+
*/
|
|
2333
|
+
getFilters(tableConfig, defaultFilterValues = {}, searchItem) {
|
|
2334
|
+
// Do nothing in absence of configuration.
|
|
2335
|
+
if (!tableConfig || !Array.isArray(tableConfig.criteria)) {
|
|
2336
|
+
return [];
|
|
2719
2337
|
}
|
|
2720
|
-
if (
|
|
2721
|
-
|
|
2722
|
-
if (changes.formSrc.currentValue &&
|
|
2723
|
-
changes.formSrc.currentValue != changes.formSrc.previousValue) {
|
|
2724
|
-
this._http.get(changes.formSrc.currentValue).toPromise().then((form) => {
|
|
2725
|
-
// set form
|
|
2726
|
-
this.form = form;
|
|
2727
|
-
// and init
|
|
2728
|
-
this.ngOnInit();
|
|
2729
|
-
}).catch((err) => {
|
|
2730
|
-
console.error('An error occurred while loading search configuration');
|
|
2731
|
-
console.error(err);
|
|
2732
|
-
});
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2338
|
+
if (searchItem.filter == null) {
|
|
2339
|
+
return [];
|
|
2735
2340
|
}
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
this.refreshForm.emit({
|
|
2740
|
-
submission: this.submission
|
|
2741
|
-
});
|
|
2742
|
-
}
|
|
2341
|
+
const matchedCriterion = tableConfig.criteria.find(criterion => Object.prototype.hasOwnProperty.call(searchItem.filter, criterion.name));
|
|
2342
|
+
if (matchedCriterion == null) {
|
|
2343
|
+
return [];
|
|
2743
2344
|
}
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
this.
|
|
2753
|
-
|
|
2754
|
-
});
|
|
2755
|
-
this.loading = false;
|
|
2756
|
-
this.filterSubscription = this.filterChange.subscribe(filter => {
|
|
2757
|
-
this.submission.data = filter;
|
|
2758
|
-
this.refreshForm.emit({
|
|
2759
|
-
submission: this.submission
|
|
2760
|
-
});
|
|
2345
|
+
const value = searchItem.filter[matchedCriterion.name];
|
|
2346
|
+
const criterionSafeName = matchedCriterion.name !== null
|
|
2347
|
+
? matchedCriterion.name.toString()
|
|
2348
|
+
: '';
|
|
2349
|
+
let applicableFilter;
|
|
2350
|
+
// Raw dates should be transformed to a specific format
|
|
2351
|
+
if (criterionSafeName.includes('GMT')) {
|
|
2352
|
+
const newDate = this.datePipe.transform(criterionSafeName, 'MM/dd/yyyy');
|
|
2353
|
+
applicableFilter = this._template.transform(matchedCriterion.filter, {
|
|
2354
|
+
value: newDate
|
|
2761
2355
|
});
|
|
2762
2356
|
}
|
|
2357
|
+
else if (criterionSafeName !== 'undefined') {
|
|
2358
|
+
applicableFilter = (this._template.transform(matchedCriterion.filter, Object.assign({
|
|
2359
|
+
value: value
|
|
2360
|
+
}, defaultFilterValues)));
|
|
2361
|
+
}
|
|
2362
|
+
else {
|
|
2363
|
+
applicableFilter = '';
|
|
2364
|
+
}
|
|
2365
|
+
return [applicableFilter];
|
|
2763
2366
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2367
|
+
onFilterSelect(value) {
|
|
2368
|
+
this.advancedSearch(value);
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
*
|
|
2372
|
+
* Runs a filter against the modal table
|
|
2373
|
+
*
|
|
2374
|
+
* @param {object} filter The filter to use in the search
|
|
2375
|
+
*
|
|
2376
|
+
*/
|
|
2377
|
+
advancedSearch(filter) {
|
|
2378
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2379
|
+
// Do nothing in absence of configuration.
|
|
2380
|
+
if (!this.tableConfig && !Array.isArray(this.tableConfig.criteria)) {
|
|
2381
|
+
console.warn('No table configuration available', this.tableConfig);
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
// An advanced table has some filters by default
|
|
2385
|
+
let defaultFilterValues;
|
|
2386
|
+
try {
|
|
2387
|
+
defaultFilterValues = yield this.advancedFilterValueProvider.getValues();
|
|
2388
|
+
}
|
|
2389
|
+
catch (err) {
|
|
2390
|
+
defaultFilterValues = {};
|
|
2391
|
+
}
|
|
2392
|
+
const expressions = [];
|
|
2393
|
+
let predefinedCriteria;
|
|
2394
|
+
if (filter) {
|
|
2395
|
+
this.defaultFilter = filter;
|
|
2396
|
+
predefinedCriteria = this.getFilters(this.tableConfig, defaultFilterValues, filter);
|
|
2397
|
+
}
|
|
2398
|
+
else {
|
|
2399
|
+
// Empty a falsy filter resets the results
|
|
2400
|
+
this.defaultFilter = AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER;
|
|
2780
2401
|
}
|
|
2402
|
+
// search filter is applied separately, there is no need to add here.
|
|
2403
|
+
expressions.push(predefinedCriteria);
|
|
2404
|
+
const query = this._context.model(this.advancedTable.config.model).asQueryable();
|
|
2405
|
+
query.setParam('filter', expressions.join(' and ')).prepare();
|
|
2406
|
+
this.advancedTable.query = query;
|
|
2407
|
+
this.advancedTable.fetch();
|
|
2781
2408
|
});
|
|
2782
2409
|
}
|
|
2783
|
-
onFormLoad(form) {
|
|
2784
|
-
const currentLang = this._translateService.currentLang;
|
|
2785
|
-
this.formComponent.formio.i18next.options.resources[currentLang] = { translation: this._translateService.instant(`Forms`) };
|
|
2786
|
-
this.formComponent.formio.language = currentLang;
|
|
2787
|
-
}
|
|
2788
|
-
ngAfterViewInit() {
|
|
2789
|
-
//
|
|
2790
|
-
}
|
|
2791
|
-
ngOnDestroy() {
|
|
2792
|
-
if (this.filterSubscription) {
|
|
2793
|
-
this.filterSubscription.unsubscribe();
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
2410
|
}
|
|
2797
|
-
|
|
2411
|
+
AdvancedTableModalBaseComponent.DEFAULT_ALL_FILTER = {
|
|
2412
|
+
name: 'Tables.All',
|
|
2413
|
+
filter: null
|
|
2414
|
+
};
|
|
2415
|
+
AdvancedTableModalBaseComponent.decorators = [
|
|
2798
2416
|
{ type: Component, args: [{
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
`,
|
|
2806
|
-
encapsulation: ViewEncapsulation.None,
|
|
2807
|
-
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}"]
|
|
2417
|
+
// tslint:disable-next-line: component-selector
|
|
2418
|
+
selector: 'app-advanced-table-modal',
|
|
2419
|
+
template: AdvancedTableModalBaseTemplate,
|
|
2420
|
+
styles: [`
|
|
2421
|
+
|
|
2422
|
+
`]
|
|
2808
2423
|
}] }
|
|
2809
2424
|
];
|
|
2810
2425
|
/** @nocollapse */
|
|
2811
|
-
|
|
2812
|
-
{ type:
|
|
2813
|
-
{ type: TranslateService },
|
|
2426
|
+
AdvancedTableModalBaseComponent.ctorParameters = () => [
|
|
2427
|
+
{ type: Router },
|
|
2814
2428
|
{ type: ActivatedRoute },
|
|
2815
|
-
{ type:
|
|
2429
|
+
{ type: AngularDataContext },
|
|
2430
|
+
{ type: AdvancedFilterValueProvider },
|
|
2431
|
+
{ type: DatePipe }
|
|
2816
2432
|
];
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2433
|
+
AdvancedTableModalBaseComponent.propDecorators = {
|
|
2434
|
+
advancedTable: [{ type: ViewChild, args: ['advancedTable',] }],
|
|
2435
|
+
emptyTable: [{ type: ViewChild, args: ['emptyTable',] }],
|
|
2436
|
+
dropdownFilter: [{ type: ViewChild, args: ['dropdownFilter',] }],
|
|
2437
|
+
dropdownLength: [{ type: ViewChild, args: ['dropdownLength',] }],
|
|
2438
|
+
tableConfig: [{ type: Input, args: ['tableConfig',] }],
|
|
2439
|
+
tableConfigSrc: [{ type: Input, args: ['tableConfigSrc',] }],
|
|
2440
|
+
pageLength: [{ type: Input, args: ['pageLength',] }],
|
|
2441
|
+
searchText: [{ type: Input, args: ['searchText',] }],
|
|
2442
|
+
lengthMenu: [{ type: Input, args: ['lengthMenu',] }],
|
|
2443
|
+
defaultFilter: [{ type: Input, args: ['defaultFilter',] }]
|
|
2822
2444
|
};
|
|
2823
2445
|
|
|
2824
2446
|
class AdvancedSelectComponent extends AdvancedTableModalBaseComponent {
|
|
@@ -3655,357 +3277,766 @@ class AdvancedTableEditorDirective {
|
|
|
3655
3277
|
//
|
|
3656
3278
|
}
|
|
3657
3279
|
/**
|
|
3658
|
-
* Add rows to table and mark them as added rows
|
|
3280
|
+
* Add rows to table and mark them as added rows
|
|
3281
|
+
*/
|
|
3282
|
+
add(...addItems) {
|
|
3283
|
+
if (this.table == null) {
|
|
3284
|
+
throw new AdvancedTableNotNullError();
|
|
3285
|
+
}
|
|
3286
|
+
// decorate items
|
|
3287
|
+
const key = this.table.config.columns[0].property || this.table.config.columns[0].name;
|
|
3288
|
+
for (const element of addItems) {
|
|
3289
|
+
let tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3290
|
+
if (tr.hasClass('removed')) {
|
|
3291
|
+
tr.toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3292
|
+
}
|
|
3293
|
+
else {
|
|
3294
|
+
this.table.dataTable.rows.add([element]);
|
|
3295
|
+
tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3296
|
+
tr.toggleClass('added font-weight-bold text-success');
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
this.table.dataTable.draw();
|
|
3300
|
+
// find removed
|
|
3301
|
+
const removedLength = this.table.dataTable.rows('.removed')[0].length;
|
|
3302
|
+
// find added
|
|
3303
|
+
const addedLength = this.table.dataTable.rows('.added')[0].length;
|
|
3304
|
+
// and emit event
|
|
3305
|
+
const items = this.table.dataTable.rows().data();
|
|
3306
|
+
this.table.load.emit({
|
|
3307
|
+
data: items,
|
|
3308
|
+
recordsFiltered: items.length - removedLength,
|
|
3309
|
+
recordsTotal: items.length - removedLength,
|
|
3310
|
+
});
|
|
3311
|
+
this.dirty = (removedLength > 0 || addedLength > 0);
|
|
3312
|
+
this.dirtyChange.emit(this.dirty);
|
|
3313
|
+
}
|
|
3314
|
+
/**
|
|
3315
|
+
* Marks rows as removed rows
|
|
3316
|
+
*/
|
|
3317
|
+
remove(...removeItems) {
|
|
3318
|
+
if (this.table == null) {
|
|
3319
|
+
throw new AdvancedTableNotNullError();
|
|
3320
|
+
}
|
|
3321
|
+
// get key
|
|
3322
|
+
const key = this.table.config.columns[0].property || this.table.config.columns[0].name;
|
|
3323
|
+
// loop items
|
|
3324
|
+
for (const element of removeItems) {
|
|
3325
|
+
const tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3326
|
+
// if row is marked as added
|
|
3327
|
+
if (tr.hasClass('added')) {
|
|
3328
|
+
// remove this dirty row
|
|
3329
|
+
this.table.dataTable.rows(tr).remove();
|
|
3330
|
+
}
|
|
3331
|
+
else {
|
|
3332
|
+
// otherwise unmarked row
|
|
3333
|
+
tr.toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3334
|
+
}
|
|
3335
|
+
// check if item is selected
|
|
3336
|
+
const selectedIndex = this.table.selected.findIndex((item) => {
|
|
3337
|
+
return item[key] === element[key];
|
|
3338
|
+
});
|
|
3339
|
+
// and remove it from selected collection
|
|
3340
|
+
if (selectedIndex >= 0) {
|
|
3341
|
+
this.table.selected.splice(selectedIndex, 1);
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
// re-draw table
|
|
3345
|
+
this.table.dataTable.draw();
|
|
3346
|
+
// get number of table items
|
|
3347
|
+
const items = this.table.dataTable.rows().data();
|
|
3348
|
+
// get number of removed items
|
|
3349
|
+
const removedLength = this.table.dataTable.rows('.removed')[0].length;
|
|
3350
|
+
// get number of added items
|
|
3351
|
+
const addedLength = this.table.dataTable.rows('.added')[0].length;
|
|
3352
|
+
// refresh
|
|
3353
|
+
this.table.load.emit({
|
|
3354
|
+
data: items,
|
|
3355
|
+
recordsFiltered: items.length - removedLength,
|
|
3356
|
+
recordsTotal: items.length - removedLength,
|
|
3357
|
+
});
|
|
3358
|
+
this.dirty = (removedLength > 0 || addedLength > 0);
|
|
3359
|
+
this.dirtyChange.emit(this.dirty);
|
|
3360
|
+
}
|
|
3361
|
+
set(items) {
|
|
3362
|
+
if (this.table == null) {
|
|
3363
|
+
throw new AdvancedTableNotNullError();
|
|
3364
|
+
}
|
|
3365
|
+
// clear rows
|
|
3366
|
+
this.table.selected = [];
|
|
3367
|
+
this.table.dataTable.clear();
|
|
3368
|
+
// set items
|
|
3369
|
+
this.table.dataTable.rows.add(items).draw();
|
|
3370
|
+
this.table.load.emit({
|
|
3371
|
+
data: items,
|
|
3372
|
+
recordsFiltered: items.length,
|
|
3373
|
+
recordsTotal: items.length,
|
|
3374
|
+
});
|
|
3375
|
+
}
|
|
3376
|
+
/**
|
|
3377
|
+
* Undo changes
|
|
3378
|
+
*/
|
|
3379
|
+
undo() {
|
|
3380
|
+
if (this.table == null) {
|
|
3381
|
+
throw new AdvancedTableNotNullError();
|
|
3382
|
+
}
|
|
3383
|
+
// reset removed rows
|
|
3384
|
+
this.table.dataTable.rows('.removed').nodes().to$().toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3385
|
+
// remove added rows
|
|
3386
|
+
this.table.dataTable.rows('.added').remove();
|
|
3387
|
+
// clear selected items
|
|
3388
|
+
this.table.selected = [];
|
|
3389
|
+
const items = this.table.dataTable.rows().data();
|
|
3390
|
+
this.table.dataTable.draw();
|
|
3391
|
+
this.table.load.emit({
|
|
3392
|
+
data: items,
|
|
3393
|
+
recordsFiltered: items.length,
|
|
3394
|
+
recordsTotal: items.length,
|
|
3395
|
+
});
|
|
3396
|
+
this.dirty = false;
|
|
3397
|
+
this.dirtyChange.emit(this.dirty);
|
|
3398
|
+
}
|
|
3399
|
+
rows() {
|
|
3400
|
+
if (this.table == null) {
|
|
3401
|
+
throw new AdvancedTableNotNullError();
|
|
3402
|
+
}
|
|
3403
|
+
return Array.from(this.table.dataTable.rows(':not(.removed)').data());
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
AdvancedTableEditorDirective.decorators = [
|
|
3407
|
+
{ type: Directive, args: [{
|
|
3408
|
+
// tslint:disable-next-line: directive-selector
|
|
3409
|
+
selector: '[tableEditor]',
|
|
3410
|
+
},] }
|
|
3411
|
+
];
|
|
3412
|
+
/** @nocollapse */
|
|
3413
|
+
AdvancedTableEditorDirective.ctorParameters = () => [
|
|
3414
|
+
{ type: AdvancedTableComponent }
|
|
3415
|
+
];
|
|
3416
|
+
AdvancedTableEditorDirective.propDecorators = {
|
|
3417
|
+
dirtyChange: [{ type: Output }]
|
|
3418
|
+
};
|
|
3419
|
+
|
|
3420
|
+
class BtnExpDirective {
|
|
3421
|
+
constructor(_activatedTable, _context, _config, _translate, _loadingService, _errorService) {
|
|
3422
|
+
this._activatedTable = _activatedTable;
|
|
3423
|
+
this._context = _context;
|
|
3424
|
+
this._config = _config;
|
|
3425
|
+
this._translate = _translate;
|
|
3426
|
+
this._loadingService = _loadingService;
|
|
3427
|
+
this._errorService = _errorService;
|
|
3428
|
+
}
|
|
3429
|
+
clickEvent(event) {
|
|
3430
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3431
|
+
event.preventDefault();
|
|
3432
|
+
event.stopPropagation();
|
|
3433
|
+
if (this._activatedTable.activeTable) {
|
|
3434
|
+
const lastQuery = this._activatedTable.activeTable.lastQuery;
|
|
3435
|
+
const tableConfig = this._activatedTable.activeTable.config;
|
|
3436
|
+
const serviceHeaders = this._context.getService().getHeaders();
|
|
3437
|
+
const queryModel = lastQuery.getModel();
|
|
3438
|
+
let query = this._context.model(queryModel).asQueryable();
|
|
3439
|
+
const queryParams = lastQuery.getParams();
|
|
3440
|
+
query = this.constructQuery(query, queryParams, tableConfig, serviceHeaders);
|
|
3441
|
+
const maxExportItems = 1000;
|
|
3442
|
+
{
|
|
3443
|
+
query.skip(0).take(maxExportItems);
|
|
3444
|
+
}
|
|
3445
|
+
// This constructs the URI based on the way @themost does
|
|
3446
|
+
const url = this.constructURL(query);
|
|
3447
|
+
this.downloadFile(url, query.getService().getHeaders(), query.getModel());
|
|
3448
|
+
}
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
/**
|
|
3452
|
+
* Sets the parameters to the query based on
|
|
3453
|
+
* the active filters of the activated table
|
|
3454
|
+
* @param {ClientDataQueryable} query
|
|
3455
|
+
* @param params: The filters of the last query of the activated table
|
|
3456
|
+
* @return ClientDataQueryable
|
|
3457
|
+
*/
|
|
3458
|
+
setParams(query, params) {
|
|
3459
|
+
if (params && typeof params === 'object') {
|
|
3460
|
+
Object.keys(params).forEach((key) => {
|
|
3461
|
+
query.setParam(key, params[key]);
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
return query;
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Sets the headers to the query based on
|
|
3468
|
+
* the default headers of the DataContext
|
|
3469
|
+
* and appending the accept header to get
|
|
3470
|
+
* an xlsx file from the API
|
|
3471
|
+
* @param {ClientDataQueryable} query
|
|
3472
|
+
* @param {Headers} headers: The headers to append to the query
|
|
3473
|
+
* @return ClientDataQueryable
|
|
3474
|
+
*/
|
|
3475
|
+
setHeaders(query, headers) {
|
|
3476
|
+
Object.keys(headers).forEach((key) => {
|
|
3477
|
+
if (headers.hasOwnProperty(key)) {
|
|
3478
|
+
query.getService().setHeader(key, headers[key]);
|
|
3479
|
+
}
|
|
3480
|
+
});
|
|
3481
|
+
// sets a header to accept xlsx files
|
|
3482
|
+
query.getService().setHeader('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
3483
|
+
return query;
|
|
3484
|
+
}
|
|
3485
|
+
/**
|
|
3486
|
+
* Constructs the select parameter of the
|
|
3487
|
+
* ClientDataQuery by replacing the target name
|
|
3488
|
+
* of the field based on the localized translation
|
|
3489
|
+
* of that property on the advanced tabled
|
|
3490
|
+
* @param {ClientDataQueryable} query
|
|
3491
|
+
* @param tableConfig: The configuration of the activated table
|
|
3492
|
+
* @return ClientDataQueryable
|
|
3493
|
+
*/
|
|
3494
|
+
constructQueryParam(query, tableConfig) {
|
|
3495
|
+
let selectQuery = query.getParams().$select;
|
|
3496
|
+
let selectQueryArray = selectQuery.split(',');
|
|
3497
|
+
selectQueryArray = selectQueryArray.map((select) => {
|
|
3498
|
+
const words = select.split(' ');
|
|
3499
|
+
if (words.length === 3) {
|
|
3500
|
+
if (tableConfig && tableConfig.columns) {
|
|
3501
|
+
for (const column of tableConfig.columns) {
|
|
3502
|
+
if (column.property === words[2] && column.title) {
|
|
3503
|
+
words[2] = `"${this._translate.instant(column.title)}"`;
|
|
3504
|
+
}
|
|
3505
|
+
else if (column.property === words[2]) {
|
|
3506
|
+
words[2] = `"${this._translate.instant(words[2])}"`;
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
if (words.length === 1) {
|
|
3512
|
+
if (tableConfig && tableConfig.columns) {
|
|
3513
|
+
for (const column of tableConfig.columns) {
|
|
3514
|
+
if (column.name === words[0] && column.title) {
|
|
3515
|
+
words[0] = `${words[0]} as "${this._translate.instant(column.title)}"`;
|
|
3516
|
+
break;
|
|
3517
|
+
}
|
|
3518
|
+
else if (column.name === words[0] && column.property) {
|
|
3519
|
+
words[0] = `${words[0]} as "${this._translate.instant(column.property)}"`;
|
|
3520
|
+
break;
|
|
3521
|
+
}
|
|
3522
|
+
else if (words[0] === column.name && words[0] === "id" && column.hidden) {
|
|
3523
|
+
break;
|
|
3524
|
+
}
|
|
3525
|
+
else if (words[0] == column.name || words[0] === column.property) {
|
|
3526
|
+
words[0] = `${words[0]} as "${this._translate.instant(words[0])}"`;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
return words[0];
|
|
3531
|
+
}
|
|
3532
|
+
return words.join(' ');
|
|
3533
|
+
});
|
|
3534
|
+
selectQuery = selectQueryArray.join(',');
|
|
3535
|
+
query.setParam('$select', selectQuery);
|
|
3536
|
+
return query;
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Constructs the fetch URL based the query parameters
|
|
3540
|
+
* @param {ClientDataQueryable} query
|
|
3541
|
+
* @return String
|
|
3659
3542
|
*/
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
for (const element of addItems) {
|
|
3667
|
-
let tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3668
|
-
if (tr.hasClass('removed')) {
|
|
3669
|
-
tr.toggleClass('removed text-gray-400 text-decoration-line-through');
|
|
3543
|
+
constructURL(query) {
|
|
3544
|
+
const baseURL = query.getService().getBase();
|
|
3545
|
+
let url = baseURL.endsWith('/') ? (baseURL + query.getModel() + '?') : (baseURL + '/' + query.getModel() + '?');
|
|
3546
|
+
Object.keys(query.getParams()).forEach((key, index) => {
|
|
3547
|
+
if (index === 0) {
|
|
3548
|
+
url += key + '=' + query.getParams()[key];
|
|
3670
3549
|
}
|
|
3671
3550
|
else {
|
|
3672
|
-
|
|
3673
|
-
tr = this.table.dataTable.rows(':has(input[data-id="' + element[key] + '"])').nodes().to$();
|
|
3674
|
-
tr.toggleClass('added font-weight-bold text-success');
|
|
3551
|
+
url += '&' + key + '=' + query.getParams()[key];
|
|
3675
3552
|
}
|
|
3676
|
-
}
|
|
3677
|
-
this.table.dataTable.draw();
|
|
3678
|
-
// find removed
|
|
3679
|
-
const removedLength = this.table.dataTable.rows('.removed')[0].length;
|
|
3680
|
-
// find added
|
|
3681
|
-
const addedLength = this.table.dataTable.rows('.added')[0].length;
|
|
3682
|
-
// and emit event
|
|
3683
|
-
const items = this.table.dataTable.rows().data();
|
|
3684
|
-
this.table.load.emit({
|
|
3685
|
-
data: items,
|
|
3686
|
-
recordsFiltered: items.length - removedLength,
|
|
3687
|
-
recordsTotal: items.length - removedLength,
|
|
3688
3553
|
});
|
|
3689
|
-
|
|
3690
|
-
this.dirtyChange.emit(this.dirty);
|
|
3554
|
+
return url;
|
|
3691
3555
|
}
|
|
3692
3556
|
/**
|
|
3693
|
-
*
|
|
3557
|
+
* Constructs the query by setting the parameters of the
|
|
3558
|
+
* advanced table's active filters, sets the necessary headers
|
|
3559
|
+
* and build the select parameter of the query
|
|
3560
|
+
* @param {ClientDataQueryable} query
|
|
3561
|
+
* @param params: The filters of the last query of the activated table
|
|
3562
|
+
* @param tableConfig: The configuration of the activated table
|
|
3563
|
+
* @param {Headers} headers: The headers to append to the query
|
|
3564
|
+
* @return ClientDataQueryable
|
|
3694
3565
|
*/
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3566
|
+
constructQuery(query, params, tableConfig, headers) {
|
|
3567
|
+
let _query = this.setParams(query, params);
|
|
3568
|
+
_query = this.setHeaders(_query, headers);
|
|
3569
|
+
_query = this.constructQueryParam(_query, tableConfig);
|
|
3570
|
+
return _query;
|
|
3571
|
+
}
|
|
3572
|
+
/**
|
|
3573
|
+
* Fetches and saves the xlsx file
|
|
3574
|
+
* @param {String} url
|
|
3575
|
+
* @param {Headers} serviceHeaders: The headers to append to the query
|
|
3576
|
+
* @param modelName: The model name of the advanced table
|
|
3577
|
+
*/
|
|
3578
|
+
downloadFile(url, serviceHeaders, modelName) {
|
|
3579
|
+
this._loadingService.showLoading();
|
|
3580
|
+
fetch(encodeURI(url), {
|
|
3581
|
+
headers: serviceHeaders,
|
|
3582
|
+
credentials: 'include'
|
|
3583
|
+
}).then(response => {
|
|
3584
|
+
if (response && !response.ok) {
|
|
3585
|
+
throw new ResponseError(response.statusText, response.status);
|
|
3586
|
+
}
|
|
3587
|
+
return response.blob();
|
|
3588
|
+
}).then(blob => {
|
|
3589
|
+
const objectUrl = window.URL.createObjectURL(blob);
|
|
3590
|
+
const a = document.createElement('a');
|
|
3591
|
+
document.body.appendChild(a);
|
|
3592
|
+
a.setAttribute('style', 'display: none');
|
|
3593
|
+
a.href = objectUrl;
|
|
3594
|
+
const downloadName = `${modelName}.xlsx`;
|
|
3595
|
+
a.download = downloadName;
|
|
3596
|
+
// this adds support for IE and MS Edge (up to version 44.19041)
|
|
3597
|
+
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
|
3598
|
+
window.navigator.msSaveOrOpenBlob(blob, downloadName);
|
|
3708
3599
|
}
|
|
3709
3600
|
else {
|
|
3710
|
-
//
|
|
3711
|
-
|
|
3601
|
+
// for all other browsers
|
|
3602
|
+
a.click();
|
|
3712
3603
|
}
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3604
|
+
window.URL.revokeObjectURL(objectUrl);
|
|
3605
|
+
a.remove();
|
|
3606
|
+
this._loadingService.hideLoading();
|
|
3607
|
+
}).catch(err => {
|
|
3608
|
+
this._loadingService.hideLoading();
|
|
3609
|
+
this._errorService.showError(err, {
|
|
3610
|
+
continueLink: '.'
|
|
3716
3611
|
});
|
|
3717
|
-
// and remove it from selected collection
|
|
3718
|
-
if (selectedIndex >= 0) {
|
|
3719
|
-
this.table.selected.splice(selectedIndex, 1);
|
|
3720
|
-
}
|
|
3721
|
-
}
|
|
3722
|
-
// re-draw table
|
|
3723
|
-
this.table.dataTable.draw();
|
|
3724
|
-
// get number of table items
|
|
3725
|
-
const items = this.table.dataTable.rows().data();
|
|
3726
|
-
// get number of removed items
|
|
3727
|
-
const removedLength = this.table.dataTable.rows('.removed')[0].length;
|
|
3728
|
-
// get number of added items
|
|
3729
|
-
const addedLength = this.table.dataTable.rows('.added')[0].length;
|
|
3730
|
-
// refresh
|
|
3731
|
-
this.table.load.emit({
|
|
3732
|
-
data: items,
|
|
3733
|
-
recordsFiltered: items.length - removedLength,
|
|
3734
|
-
recordsTotal: items.length - removedLength,
|
|
3735
3612
|
});
|
|
3736
|
-
this.dirty = (removedLength > 0 || addedLength > 0);
|
|
3737
|
-
this.dirtyChange.emit(this.dirty);
|
|
3738
3613
|
}
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3614
|
+
}
|
|
3615
|
+
BtnExpDirective.decorators = [
|
|
3616
|
+
{ type: Directive, args: [{
|
|
3617
|
+
selector: '.universis-btn-export'
|
|
3618
|
+
},] }
|
|
3619
|
+
];
|
|
3620
|
+
/** @nocollapse */
|
|
3621
|
+
BtnExpDirective.ctorParameters = () => [
|
|
3622
|
+
{ type: ActivatedTableService },
|
|
3623
|
+
{ type: AngularDataContext },
|
|
3624
|
+
{ type: ConfigurationService },
|
|
3625
|
+
{ type: TranslateService },
|
|
3626
|
+
{ type: LoadingService },
|
|
3627
|
+
{ type: ErrorService }
|
|
3628
|
+
];
|
|
3629
|
+
BtnExpDirective.propDecorators = {
|
|
3630
|
+
clickEvent: [{ type: HostListener, args: ['click', ['$event'],] }]
|
|
3631
|
+
};
|
|
3632
|
+
|
|
3633
|
+
class AdvancedAggregatorService {
|
|
3634
|
+
constructor(_context, _template) {
|
|
3635
|
+
this._context = _context;
|
|
3636
|
+
this._template = _template;
|
|
3753
3637
|
}
|
|
3754
3638
|
/**
|
|
3755
|
-
*
|
|
3639
|
+
*
|
|
3640
|
+
* Formats and executes the data query
|
|
3641
|
+
*
|
|
3642
|
+
* @param {string} model The target model
|
|
3643
|
+
* @param {object} fixedParams Pairs of parameters to always be applied
|
|
3644
|
+
* @param {string} queryStatement The default query to use for fetching the data (e.g. expands etc)
|
|
3645
|
+
* @param {Array<string>} filters A list of filter expressions to append at the query
|
|
3646
|
+
* @param {string} searchTemplate The template to use for searching expressions
|
|
3647
|
+
* @param {string} searchValue The value of the user search
|
|
3648
|
+
*
|
|
3649
|
+
* @returns {Promise<any>} The response of the query execution
|
|
3756
3650
|
*/
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3651
|
+
fetchData(model, fixedParams, queryStatement = '', filters, searchTemplate, searchValue) {
|
|
3652
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3653
|
+
const query = this._context.model(model).asQueryable();
|
|
3654
|
+
if (!fixedParams || typeof fixedParams !== 'object') {
|
|
3655
|
+
fixedParams = {};
|
|
3656
|
+
}
|
|
3657
|
+
let filterExpressions = [];
|
|
3658
|
+
if (fixedParams) {
|
|
3659
|
+
for (let param of Object.keys(fixedParams)) {
|
|
3660
|
+
filterExpressions.push(`${param} eq ${fixedParams[param]}`);
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
if (filters && Array.isArray(filters)) {
|
|
3664
|
+
filterExpressions = [
|
|
3665
|
+
...filterExpressions,
|
|
3666
|
+
...filters
|
|
3667
|
+
];
|
|
3668
|
+
}
|
|
3669
|
+
if (searchTemplate && searchValue) {
|
|
3670
|
+
let evaluatedSearch;
|
|
3671
|
+
if (/^"(.*?)"$/.test(searchValue)) { // validate search text in double quotes
|
|
3672
|
+
evaluatedSearch = this._template.transform(searchTemplate, Object.assign({}, fixedParams, { text: searchValue.replace(/^"|"$/g, '') }));
|
|
3673
|
+
}
|
|
3674
|
+
else {
|
|
3675
|
+
// try to split words
|
|
3676
|
+
const words = searchValue.split(' ');
|
|
3677
|
+
// join words to a single filter
|
|
3678
|
+
evaluatedSearch = words.map(word => {
|
|
3679
|
+
return this._template.transform(searchTemplate, Object.assign({}, fixedParams, { text: word }));
|
|
3680
|
+
}).join(' and ');
|
|
3681
|
+
}
|
|
3682
|
+
filterExpressions.push(evaluatedSearch);
|
|
3683
|
+
}
|
|
3684
|
+
query.setParam('$filter', filterExpressions.join('and'));
|
|
3685
|
+
return query.expand(queryStatement).take(-1).getItems();
|
|
3773
3686
|
});
|
|
3774
|
-
this.dirty = false;
|
|
3775
|
-
this.dirtyChange.emit(this.dirty);
|
|
3776
3687
|
}
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3688
|
+
/**
|
|
3689
|
+
*
|
|
3690
|
+
* Formats the received data to use flat keys
|
|
3691
|
+
*
|
|
3692
|
+
* @param config The applied configuration
|
|
3693
|
+
* @param data The raw data
|
|
3694
|
+
*
|
|
3695
|
+
* @returns {Array<any>} The flat-key object list
|
|
3696
|
+
*
|
|
3697
|
+
*/
|
|
3698
|
+
formatData(config, data) {
|
|
3699
|
+
try {
|
|
3700
|
+
return data.map((entry) => {
|
|
3701
|
+
if (entry.subitems && Array.isArray(entry.subitems)) {
|
|
3702
|
+
entry.subitems = this.formatData(config, entry.subitems);
|
|
3703
|
+
}
|
|
3704
|
+
let entryObject = {};
|
|
3705
|
+
for (let i = 0; i < config.columns.length; i++) {
|
|
3706
|
+
const keys = config.columns[i].name.split('/');
|
|
3707
|
+
let helperObject = entry[keys[0]];
|
|
3708
|
+
if (helperObject && keys.length > 1) {
|
|
3709
|
+
for (let i = 1; i < keys.length; i++) {
|
|
3710
|
+
if (helperObject === undefined || helperObject === null) {
|
|
3711
|
+
break;
|
|
3712
|
+
}
|
|
3713
|
+
else {
|
|
3714
|
+
helperObject = helperObject[keys[i]] !== undefined && helperObject[keys[i]] !== null
|
|
3715
|
+
? helperObject[keys[i]]
|
|
3716
|
+
: undefined;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
entryObject[config.columns[i].name] = helperObject;
|
|
3721
|
+
}
|
|
3722
|
+
if (entry.subitems) {
|
|
3723
|
+
entryObject.subitems = entry.subitems;
|
|
3724
|
+
}
|
|
3725
|
+
return entryObject;
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
catch (err) {
|
|
3729
|
+
console.error(err);
|
|
3780
3730
|
}
|
|
3781
|
-
return Array.from(this.table.dataTable.rows(':not(.removed)').data());
|
|
3782
3731
|
}
|
|
3783
3732
|
}
|
|
3784
|
-
|
|
3785
|
-
{ type:
|
|
3786
|
-
|
|
3787
|
-
selector: '[tableEditor]',
|
|
3733
|
+
AdvancedAggregatorService.decorators = [
|
|
3734
|
+
{ type: Injectable, args: [{
|
|
3735
|
+
providedIn: 'root'
|
|
3788
3736
|
},] }
|
|
3789
3737
|
];
|
|
3790
3738
|
/** @nocollapse */
|
|
3791
|
-
|
|
3792
|
-
{ type:
|
|
3739
|
+
AdvancedAggregatorService.ctorParameters = () => [
|
|
3740
|
+
{ type: AngularDataContext },
|
|
3741
|
+
{ type: TemplatePipe }
|
|
3793
3742
|
];
|
|
3794
|
-
|
|
3795
|
-
dirtyChange: [{ type: Output }]
|
|
3796
|
-
};
|
|
3743
|
+
AdvancedAggregatorService.ngInjectableDef = defineInjectable({ factory: function AdvancedAggregatorService_Factory() { return new AdvancedAggregatorService(inject(AngularDataContext), inject(TemplatePipe)); }, token: AdvancedAggregatorService, providedIn: "root" });
|
|
3797
3744
|
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3745
|
+
/**
|
|
3746
|
+
*
|
|
3747
|
+
* @class AdvancedAggregatorComponent
|
|
3748
|
+
* @classdesc An advanced list with the capability of aggregate data based on configurable filters
|
|
3749
|
+
*
|
|
3750
|
+
*/
|
|
3751
|
+
class AdvancedAggregatorComponent {
|
|
3752
|
+
constructor(_changeDetector, _advancedAggregatorService, _loadingService) {
|
|
3753
|
+
this._changeDetector = _changeDetector;
|
|
3754
|
+
this._advancedAggregatorService = _advancedAggregatorService;
|
|
3804
3755
|
this._loadingService = _loadingService;
|
|
3805
|
-
this.
|
|
3756
|
+
this.groups = [];
|
|
3757
|
+
this.eventHandler = new EventEmitter();
|
|
3806
3758
|
}
|
|
3807
|
-
|
|
3759
|
+
set config(value) {
|
|
3760
|
+
this._config = value;
|
|
3761
|
+
}
|
|
3762
|
+
set aggregators(value) {
|
|
3763
|
+
this._aggregators = value;
|
|
3764
|
+
}
|
|
3765
|
+
set postProcessors(value) {
|
|
3766
|
+
this._postProcessors = value;
|
|
3767
|
+
}
|
|
3768
|
+
get config() {
|
|
3769
|
+
return this._config;
|
|
3770
|
+
}
|
|
3771
|
+
get aggregators() {
|
|
3772
|
+
return this._aggregators;
|
|
3773
|
+
}
|
|
3774
|
+
get postProcessors() {
|
|
3775
|
+
return this._postProcessors;
|
|
3776
|
+
}
|
|
3777
|
+
ngOnInit() {
|
|
3808
3778
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
let query = this._context.model(queryModel).asQueryable();
|
|
3817
|
-
const queryParams = lastQuery.getParams();
|
|
3818
|
-
query = this.constructQuery(query, queryParams, tableConfig, serviceHeaders);
|
|
3819
|
-
const maxExportItems = 1000;
|
|
3820
|
-
{
|
|
3821
|
-
query.skip(0).take(maxExportItems);
|
|
3779
|
+
this.selectedGroups$.subscribe((data) => {
|
|
3780
|
+
this.groups = data;
|
|
3781
|
+
this._changeDetector.markForCheck();
|
|
3782
|
+
});
|
|
3783
|
+
this.filters$.subscribe((filters) => __awaiter(this, void 0, void 0, function* () {
|
|
3784
|
+
try {
|
|
3785
|
+
yield this.fetch(filters);
|
|
3822
3786
|
}
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
}
|
|
3787
|
+
catch (err) {
|
|
3788
|
+
console.error(err);
|
|
3789
|
+
}
|
|
3790
|
+
}));
|
|
3827
3791
|
});
|
|
3828
3792
|
}
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
* @param params: The filters of the last query of the activated table
|
|
3834
|
-
* @return ClientDataQueryable
|
|
3835
|
-
*/
|
|
3836
|
-
setParams(query, params) {
|
|
3837
|
-
if (params && typeof params === 'object') {
|
|
3838
|
-
Object.keys(params).forEach((key) => {
|
|
3839
|
-
query.setParam(key, params[key]);
|
|
3840
|
-
});
|
|
3841
|
-
}
|
|
3842
|
-
return query;
|
|
3843
|
-
}
|
|
3844
|
-
/**
|
|
3845
|
-
* Sets the headers to the query based on
|
|
3846
|
-
* the default headers of the DataContext
|
|
3847
|
-
* and appending the accept header to get
|
|
3848
|
-
* an xlsx file from the API
|
|
3849
|
-
* @param {ClientDataQueryable} query
|
|
3850
|
-
* @param {Headers} headers: The headers to append to the query
|
|
3851
|
-
* @return ClientDataQueryable
|
|
3852
|
-
*/
|
|
3853
|
-
setHeaders(query, headers) {
|
|
3854
|
-
Object.keys(headers).forEach((key) => {
|
|
3855
|
-
if (headers.hasOwnProperty(key)) {
|
|
3856
|
-
query.getService().setHeader(key, headers[key]);
|
|
3857
|
-
}
|
|
3793
|
+
ngOnDestroy() {
|
|
3794
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3795
|
+
this.selectedGroups$.unsubscribe();
|
|
3796
|
+
this.filters$.unsubscribe();
|
|
3858
3797
|
});
|
|
3859
|
-
// sets a header to accept xlsx files
|
|
3860
|
-
query.getService().setHeader('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
3861
|
-
return query;
|
|
3862
3798
|
}
|
|
3863
3799
|
/**
|
|
3864
|
-
*
|
|
3865
|
-
*
|
|
3866
|
-
*
|
|
3867
|
-
* of that property on the advanced tabled
|
|
3868
|
-
* @param {ClientDataQueryable} query
|
|
3869
|
-
* @param tableConfig: The configuration of the activated table
|
|
3870
|
-
* @return ClientDataQueryable
|
|
3800
|
+
*
|
|
3801
|
+
* Retrieves the data
|
|
3802
|
+
*
|
|
3871
3803
|
*/
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
if (column.property === words[2] && column.title) {
|
|
3881
|
-
words[2] = `"${this._translate.instant(column.title)}"`;
|
|
3882
|
-
}
|
|
3883
|
-
else if (column.property === words[2]) {
|
|
3884
|
-
words[2] = `"${this._translate.instant(words[2])}"`;
|
|
3885
|
-
}
|
|
3804
|
+
fetch(filters) {
|
|
3805
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3806
|
+
try {
|
|
3807
|
+
this._loadingService.showLoading();
|
|
3808
|
+
let data = yield this._advancedAggregatorService.fetchData(this.config.model, this.params, this.config.query, filters.filters, this.searchExpression, filters.text);
|
|
3809
|
+
if (this.postProcessors && Object.keys(this.postProcessors).length > 0 && this.config.postProcessors && this.config.postProcessors.length > 0) {
|
|
3810
|
+
for (let i = 0; i < this.config.postProcessors.length; i++) {
|
|
3811
|
+
data = yield this.postProcessors[this.config.postProcessors[i].name](data);
|
|
3886
3812
|
}
|
|
3887
3813
|
}
|
|
3814
|
+
this.body = this._advancedAggregatorService.formatData(this.config, data);
|
|
3815
|
+
this._changeDetector.markForCheck();
|
|
3888
3816
|
}
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
for (const column of tableConfig.columns) {
|
|
3892
|
-
if (column.name === words[0] && column.title) {
|
|
3893
|
-
words[0] = `${words[0]} as "${this._translate.instant(column.title)}"`;
|
|
3894
|
-
break;
|
|
3895
|
-
}
|
|
3896
|
-
else if (column.name === words[0] && column.property) {
|
|
3897
|
-
words[0] = `${words[0]} as "${this._translate.instant(column.property)}"`;
|
|
3898
|
-
break;
|
|
3899
|
-
}
|
|
3900
|
-
else if (words[0] === column.name && words[0] === "id" && column.hidden) {
|
|
3901
|
-
break;
|
|
3902
|
-
}
|
|
3903
|
-
else if (words[0] == column.name || words[0] === column.property) {
|
|
3904
|
-
words[0] = `${words[0]} as "${this._translate.instant(words[0])}"`;
|
|
3905
|
-
}
|
|
3906
|
-
}
|
|
3907
|
-
}
|
|
3908
|
-
return words[0];
|
|
3817
|
+
catch (err) {
|
|
3818
|
+
console.error(err);
|
|
3909
3819
|
}
|
|
3910
|
-
|
|
3820
|
+
finally {
|
|
3821
|
+
this._loadingService.hideLoading();
|
|
3822
|
+
}
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
*
|
|
3827
|
+
* Triggers the event handler event
|
|
3828
|
+
*
|
|
3829
|
+
* @param {*} event The event data
|
|
3830
|
+
*
|
|
3831
|
+
*/
|
|
3832
|
+
onActionEvent(event) {
|
|
3833
|
+
this.eventHandler.emit(event);
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
AdvancedAggregatorComponent.decorators = [
|
|
3837
|
+
{ type: Component, args: [{
|
|
3838
|
+
selector: 'app-advanced-aggregator',
|
|
3839
|
+
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>",
|
|
3840
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
3841
|
+
}] }
|
|
3842
|
+
];
|
|
3843
|
+
/** @nocollapse */
|
|
3844
|
+
AdvancedAggregatorComponent.ctorParameters = () => [
|
|
3845
|
+
{ type: ChangeDetectorRef },
|
|
3846
|
+
{ type: AdvancedAggregatorService },
|
|
3847
|
+
{ type: LoadingService }
|
|
3848
|
+
];
|
|
3849
|
+
AdvancedAggregatorComponent.propDecorators = {
|
|
3850
|
+
config: [{ type: Input, args: ['config',] }],
|
|
3851
|
+
aggregators: [{ type: Input, args: ['aggregators',] }],
|
|
3852
|
+
postProcessors: [{ type: Input, args: ['postProcessors',] }],
|
|
3853
|
+
params: [{ type: Input, args: ['params',] }],
|
|
3854
|
+
selectedGroups$: [{ type: Input, args: ['selectedGroups',] }],
|
|
3855
|
+
filters$: [{ type: Input, args: ['filters',] }],
|
|
3856
|
+
searchExpression: [{ type: Input, args: ['searchExpression',] }],
|
|
3857
|
+
eventHandler: [{ type: Output }]
|
|
3858
|
+
};
|
|
3859
|
+
|
|
3860
|
+
var Lengths;
|
|
3861
|
+
(function (Lengths) {
|
|
3862
|
+
Lengths["XL"] = "xl";
|
|
3863
|
+
Lengths["LG"] = "lg";
|
|
3864
|
+
Lengths["MD"] = "md";
|
|
3865
|
+
Lengths["SM"] = "sm";
|
|
3866
|
+
Lengths["XS"] = "xs";
|
|
3867
|
+
})(Lengths || (Lengths = {}));
|
|
3868
|
+
class AdvancedAggregatorListComponent {
|
|
3869
|
+
constructor(_template) {
|
|
3870
|
+
this._template = _template;
|
|
3871
|
+
this.clickEvent = new EventEmitter();
|
|
3872
|
+
}
|
|
3873
|
+
ngOnInit() {
|
|
3874
|
+
this.config.columns.forEach(x => {
|
|
3875
|
+
x.length = Object.values(Lengths).includes(x.length) ? this._template.transform(`sis--list-group-item_${x.length}_column`, x) :
|
|
3876
|
+
!x.length ? 'sis--list-group-item_sm_column' : x.length;
|
|
3911
3877
|
});
|
|
3912
|
-
selectQuery = selectQueryArray.join(',');
|
|
3913
|
-
query.setParam('$select', selectQuery);
|
|
3914
|
-
return query;
|
|
3915
3878
|
}
|
|
3916
3879
|
/**
|
|
3917
|
-
*
|
|
3918
|
-
*
|
|
3919
|
-
*
|
|
3880
|
+
*
|
|
3881
|
+
* Triggers the output event emitter
|
|
3882
|
+
*
|
|
3883
|
+
* @param type The type of the event as it's declared in the configuration
|
|
3884
|
+
* @param item The value of the current item
|
|
3885
|
+
* }
|
|
3920
3886
|
*/
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
if (index === 0) {
|
|
3926
|
-
url += key + '=' + query.getParams()[key];
|
|
3927
|
-
}
|
|
3928
|
-
else {
|
|
3929
|
-
url += '&' + key + '=' + query.getParams()[key];
|
|
3930
|
-
}
|
|
3887
|
+
callCallback(type, data) {
|
|
3888
|
+
this.clickEvent.emit({
|
|
3889
|
+
type,
|
|
3890
|
+
data
|
|
3931
3891
|
});
|
|
3932
|
-
return url;
|
|
3933
3892
|
}
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3893
|
+
}
|
|
3894
|
+
AdvancedAggregatorListComponent.decorators = [
|
|
3895
|
+
{ type: Component, args: [{
|
|
3896
|
+
selector: 'app-advanced-aggregator-list',
|
|
3897
|
+
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 *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n <span class=\"px-0\">\n <i class=\"fa fa-fw\"> </i>\n </span>\n\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 <button *ngIf=\"config.columns[i].type === 'action' && config.columns[i].variant === 'edit'\"\n (click)=\"callCallback(config.columns[i].identifier, entry)\"\n [title]=\"(config.columns[i].title ? config.columns[i].title : '') | translate \"\n class=\"btn btn-default px-0 mx-0\">\n <i class=\"fas fa-edit text-indigo\"></i>\n </button>\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 <div *ngIf=\"entry && entry.subitems\">\n <div *ngFor=\"let subitem of entry.subitems\" class=\"d-flex justify-content-center align-items-center list-color\">\n <div *ngIf=\"config.actions && config.actions.length > 0\" class=\"sis--list-group-item_sm_column text-left pr-0\">\n <span class=\"px-0\">\n <i class=\"fa fa-fw\"> </i>\n </span>\n <button *ngFor=\"let action of config.actions\" (click)=\"callCallback(action.identifier, subitem)\"\n [title]=\"(action.title ? action.title : '') | translate \" class=\"btn btn-default px-0\">\n <i *ngIf=\"action.type === 'edit'\" class=\"fas fa-edit text-indigo\"></i>\n </button>\n </div>\n <div class=\"text-left\"></div>\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') && 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 <span *ngIf=\"config.columns[i].type === 'boolean'\">\n <i class=\"fa font-lg\" [ngClass]=\"{\n 'fa-check text-success': subitem[column.name].toString() === config.columns[i].trueValue,\n 'fa-times text-danger': subitem[column.name].toString() !== config.columns[i].trueValue && config.columns[i].variant !== 'checkOrDash'\n }\"></i>\n <span\n *ngIf=\"subitem[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 </div>\n </li>\n </ul>\n</div>\n"
|
|
3898
|
+
}] }
|
|
3899
|
+
];
|
|
3900
|
+
/** @nocollapse */
|
|
3901
|
+
AdvancedAggregatorListComponent.ctorParameters = () => [
|
|
3902
|
+
{ type: TemplatePipe }
|
|
3903
|
+
];
|
|
3904
|
+
AdvancedAggregatorListComponent.propDecorators = {
|
|
3905
|
+
config: [{ type: Input }],
|
|
3906
|
+
body: [{ type: Input }],
|
|
3907
|
+
clickEvent: [{ type: Output }]
|
|
3908
|
+
};
|
|
3909
|
+
|
|
3910
|
+
class AddItemsComponent extends RouterModalOkCancel {
|
|
3911
|
+
constructor(router, activatedRoute, _errorService, _modalService, _appEvent, _toastService, _loadingService, _translateService, _context) {
|
|
3912
|
+
super(router, activatedRoute);
|
|
3913
|
+
this._errorService = _errorService;
|
|
3914
|
+
this._modalService = _modalService;
|
|
3915
|
+
this._appEvent = _appEvent;
|
|
3916
|
+
this._toastService = _toastService;
|
|
3917
|
+
this._loadingService = _loadingService;
|
|
3918
|
+
this._translateService = _translateService;
|
|
3919
|
+
this._context = _context;
|
|
3920
|
+
this.loading = false;
|
|
3921
|
+
this.items = [];
|
|
3922
|
+
this.messages = [];
|
|
3923
|
+
// set modal size
|
|
3924
|
+
this.modalClass = 'modal-lg';
|
|
3925
|
+
this.okButtonDisabled = true;
|
|
3949
3926
|
}
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
* @param modelName: The model name of the advanced table
|
|
3955
|
-
*/
|
|
3956
|
-
downloadFile(url, serviceHeaders, modelName) {
|
|
3957
|
-
this._loadingService.showLoading();
|
|
3958
|
-
fetch(encodeURI(url), {
|
|
3959
|
-
headers: serviceHeaders,
|
|
3960
|
-
credentials: 'include'
|
|
3961
|
-
}).then(response => {
|
|
3962
|
-
if (response && !response.ok) {
|
|
3963
|
-
throw new ResponseError(response.statusText, response.status);
|
|
3964
|
-
}
|
|
3965
|
-
return response.blob();
|
|
3966
|
-
}).then(blob => {
|
|
3967
|
-
const objectUrl = window.URL.createObjectURL(blob);
|
|
3968
|
-
const a = document.createElement('a');
|
|
3969
|
-
document.body.appendChild(a);
|
|
3970
|
-
a.setAttribute('style', 'display: none');
|
|
3971
|
-
a.href = objectUrl;
|
|
3972
|
-
const downloadName = `${modelName}.xlsx`;
|
|
3973
|
-
a.download = downloadName;
|
|
3974
|
-
// this adds support for IE and MS Edge (up to version 44.19041)
|
|
3975
|
-
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
|
3976
|
-
window.navigator.msSaveOrOpenBlob(blob, downloadName);
|
|
3977
|
-
}
|
|
3978
|
-
else {
|
|
3979
|
-
// for all other browsers
|
|
3980
|
-
a.click();
|
|
3927
|
+
ngOnInit() {
|
|
3928
|
+
this.selectedSubscription = this.selectComponent.advancedTable.selectedItems.subscribe((items) => {
|
|
3929
|
+
if (this.formName == null) {
|
|
3930
|
+
this.okButtonDisabled = items.length === 0;
|
|
3981
3931
|
}
|
|
3982
|
-
window.URL.revokeObjectURL(objectUrl);
|
|
3983
|
-
a.remove();
|
|
3984
|
-
this._loadingService.hideLoading();
|
|
3985
|
-
}).catch(err => {
|
|
3986
|
-
this._loadingService.hideLoading();
|
|
3987
|
-
this._errorService.showError(err, {
|
|
3988
|
-
continueLink: '.'
|
|
3989
|
-
});
|
|
3990
3932
|
});
|
|
3933
|
+
if (this.formName != null) {
|
|
3934
|
+
this.formChangeSubscription = this.formComponent.form.change.subscribe((event) => {
|
|
3935
|
+
if (Object.prototype.hasOwnProperty.call(event, 'isValid')) {
|
|
3936
|
+
// enable or disable button based on form status
|
|
3937
|
+
this.okButtonDisabled = (!event.isValid);
|
|
3938
|
+
}
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
if (this.target) {
|
|
3942
|
+
const target = this.target;
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
cancel() {
|
|
3946
|
+
this.clearMessages();
|
|
3947
|
+
if (this.loading) {
|
|
3948
|
+
return;
|
|
3949
|
+
}
|
|
3950
|
+
// close
|
|
3951
|
+
if (this._modalService.modalRef) {
|
|
3952
|
+
return this._modalService.modalRef.hide();
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
ngOnDestroy() {
|
|
3956
|
+
if (this.formLoadSubscription) {
|
|
3957
|
+
this.formLoadSubscription.unsubscribe();
|
|
3958
|
+
}
|
|
3959
|
+
if (this.formChangeSubscription) {
|
|
3960
|
+
this.formChangeSubscription.unsubscribe();
|
|
3961
|
+
}
|
|
3962
|
+
if (this.selectedSubscription) {
|
|
3963
|
+
this.selectedSubscription.unsubscribe();
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
get formData() {
|
|
3967
|
+
if (this.formComponent === null) {
|
|
3968
|
+
return null;
|
|
3969
|
+
}
|
|
3970
|
+
return this.formComponent.form.formio.data;
|
|
3971
|
+
}
|
|
3972
|
+
ok() {
|
|
3973
|
+
try {
|
|
3974
|
+
this.clearMessages();
|
|
3975
|
+
return new Promise((resolve, reject) => {
|
|
3976
|
+
this.loading = false;
|
|
3977
|
+
this.lastError = null;
|
|
3978
|
+
// set courses
|
|
3979
|
+
this.items = this.selectComponent.advancedTable.selected;
|
|
3980
|
+
// execute add
|
|
3981
|
+
this.executeSubscription = this.execute.subscribe((result) => {
|
|
3982
|
+
this.loading = false;
|
|
3983
|
+
if (this.executeSubscription) {
|
|
3984
|
+
this.executeSubscription.unsubscribe();
|
|
3985
|
+
}
|
|
3986
|
+
// close modal
|
|
3987
|
+
if (this._modalService.modalRef) {
|
|
3988
|
+
this._modalService.modalRef.hide();
|
|
3989
|
+
}
|
|
3990
|
+
return resolve();
|
|
3991
|
+
}, (err) => {
|
|
3992
|
+
this.loading = false;
|
|
3993
|
+
// ensure that loading is hidden
|
|
3994
|
+
this._loadingService.hideLoading();
|
|
3995
|
+
// set last error
|
|
3996
|
+
this.lastError = err;
|
|
3997
|
+
return resolve();
|
|
3998
|
+
});
|
|
3999
|
+
});
|
|
4000
|
+
}
|
|
4001
|
+
catch (err) {
|
|
4002
|
+
this.loading = false;
|
|
4003
|
+
this._loadingService.hideLoading();
|
|
4004
|
+
this.lastError = err;
|
|
4005
|
+
}
|
|
3991
4006
|
}
|
|
4007
|
+
onChange($event) { }
|
|
4008
|
+
clearMessages() {
|
|
4009
|
+
this.messages = [];
|
|
4010
|
+
}
|
|
4011
|
+
onFilterChange(event) { }
|
|
3992
4012
|
}
|
|
3993
|
-
|
|
3994
|
-
{ type:
|
|
3995
|
-
selector: '
|
|
3996
|
-
}
|
|
4013
|
+
AddItemsComponent.decorators = [
|
|
4014
|
+
{ type: Component, args: [{
|
|
4015
|
+
selector: 'app-add-items',
|
|
4016
|
+
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"
|
|
4017
|
+
}] }
|
|
3997
4018
|
];
|
|
3998
4019
|
/** @nocollapse */
|
|
3999
|
-
|
|
4000
|
-
{ type:
|
|
4001
|
-
{ type:
|
|
4002
|
-
{ type:
|
|
4003
|
-
{ type:
|
|
4020
|
+
AddItemsComponent.ctorParameters = () => [
|
|
4021
|
+
{ type: Router },
|
|
4022
|
+
{ type: ActivatedRoute },
|
|
4023
|
+
{ type: ErrorService },
|
|
4024
|
+
{ type: ModalService },
|
|
4025
|
+
{ type: AppEventService },
|
|
4026
|
+
{ type: ToastService },
|
|
4004
4027
|
{ type: LoadingService },
|
|
4005
|
-
{ type:
|
|
4028
|
+
{ type: TranslateService },
|
|
4029
|
+
{ type: AngularDataContext }
|
|
4006
4030
|
];
|
|
4007
|
-
|
|
4008
|
-
|
|
4031
|
+
AddItemsComponent.propDecorators = {
|
|
4032
|
+
execute: [{ type: Input }],
|
|
4033
|
+
items: [{ type: Input }],
|
|
4034
|
+
target: [{ type: Input }],
|
|
4035
|
+
tableConfiguration: [{ type: Input }],
|
|
4036
|
+
formName: [{ type: Input }],
|
|
4037
|
+
messages: [{ type: Input }],
|
|
4038
|
+
formComponent: [{ type: ViewChild, args: ['formComponent',] }],
|
|
4039
|
+
selectComponent: [{ type: ViewChild, args: ['selectComponent',] }]
|
|
4009
4040
|
};
|
|
4010
4041
|
|
|
4011
4042
|
/**
|
|
@@ -4091,6 +4122,7 @@ const TABLES_EL = {
|
|
|
4091
4122
|
"Search": "Αναζήτηση",
|
|
4092
4123
|
"SearchPlaceholder": "Αναζήτηση...",
|
|
4093
4124
|
"GroupBy": "Ομαδοποίηση ανά",
|
|
4125
|
+
"NotDefined": "Μη προσδιορισμένο",
|
|
4094
4126
|
"DataTable": {
|
|
4095
4127
|
"paginate": {
|
|
4096
4128
|
"first": "Πρώτη σελίδα",
|
|
@@ -4194,6 +4226,7 @@ const TABLES_EN = {
|
|
|
4194
4226
|
"Search": "Search",
|
|
4195
4227
|
"SearchPlaceholder": "Search...",
|
|
4196
4228
|
"GroupBy": "Group by",
|
|
4229
|
+
"NotDefined": "Not defined",
|
|
4197
4230
|
"DataTable": {
|
|
4198
4231
|
"paginate": {
|
|
4199
4232
|
"first": "First",
|
|
@@ -4391,6 +4424,6 @@ TablesModule.ctorParameters = () => [
|
|
|
4391
4424
|
* Generated bundle index. Do not edit.
|
|
4392
4425
|
*/
|
|
4393
4426
|
|
|
4394
|
-
export {
|
|
4427
|
+
export { AdvancedListComponent, AdvancedRowActionComponent, AdvancedSearchFormComponent, AdvancedSelectComponent, AdvancedSelectService, AdvancedTableConfigurationResolver, AdvancedSearchConfigurationResolver, AdvancedColumnFormatter, NestedPropertyFormatter, ButtonFormatter, LinkFormatter, ActionLinkFormatter, ActionButtonFormatter, ActionSplitButtonFormatter, SelectRowFormatter, TemplateFormatter, TrueFalseFormatter, TranslationFormatter, DateTimeFormatter, NgClassFormatter, LanguageFormatter, COLUMN_FORMATTERS, AdvancedTableConfiguration, AdvancedTableComponent, AdvancedTableSettingsComponent, AdvancedTableSearchBaseComponent, AdvancedTableSearchComponent, FORMATTERS, AdvancedFilterValueProvider, AdvancedTableModalBaseTemplate, AdvancedTableModalBaseComponent, SimpleHeaderToolsComponent, TableSimpleHeaderComponent, AdvancedTableNotNullError, AdvancedTableEditorDirective, BtnExpDirective, AdvancedAggregatorComponent, AdvancedAggregatorListComponent, AddItemsComponent, AdvancedAggregatorPipe, ParseTitlePipe, AdvancedAggregatorService, TablesModule, ɵ0, ActivatedTableService };
|
|
4395
4428
|
|
|
4396
4429
|
//# sourceMappingURL=universis-ngx-tables.js.map
|