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