mirror of
https://github.com/valitydev/control-center.git
synced 2024-11-06 02:25:17 +00:00
IMP-56: New repairing table and provider autocomplete (#210)
This commit is contained in:
parent
ea4a714499
commit
fafe19bdfd
140
package-lock.json
generated
140
package-lock.json
generated
@ -22,6 +22,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "15.0.3",
|
"@angular/platform-browser-dynamic": "15.0.3",
|
||||||
"@angular/platform-server": "15.0.3",
|
"@angular/platform-server": "15.0.3",
|
||||||
"@angular/router": "15.0.3",
|
"@angular/router": "15.0.3",
|
||||||
|
"@ng-matero/extensions": "15.3.0",
|
||||||
"@ngneat/input-mask": "6.0.0",
|
"@ngneat/input-mask": "6.0.0",
|
||||||
"@ngneat/until-destroy": "9.2.2",
|
"@ngneat/until-destroy": "9.2.2",
|
||||||
"@s-libs/js-core": "15.0.0",
|
"@s-libs/js-core": "15.0.0",
|
||||||
@ -2818,6 +2819,14 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ctrl/tinycolor": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@discoveryjs/json-ext": {
|
"node_modules/@discoveryjs/json-ext": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -3819,6 +3828,46 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ng-matero/extensions": {
|
||||||
|
"version": "15.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ng-matero/extensions/-/extensions-15.3.0.tgz",
|
||||||
|
"integrity": "sha512-NEPK9ba437d+eDRtj0wSZFUUTCpJlWuJr2ZpbTBwQ/mwN6eyprASM9dq4ydqEcGE+uefxZrSZrVp+eaztNjHjA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ng-select/ng-select": "^10.0.0",
|
||||||
|
"ngx-color": "^8.0.0",
|
||||||
|
"photoviewer": "^3.6.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/animations": ">=15.0.0",
|
||||||
|
"@angular/cdk": ">=15.0.0",
|
||||||
|
"@angular/common": ">=15.0.0",
|
||||||
|
"@angular/core": ">=15.0.0",
|
||||||
|
"@angular/material": ">=15.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ng-matero/extensions/node_modules/@ng-select/ng-select": {
|
||||||
|
"version": "10.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-10.0.4.tgz",
|
||||||
|
"integrity": "sha512-Vc/JIgcFkSgf47cX7+pQQo9HYhDktfqrY7o/ZPGMvu63P7E9d1MibVipqmcLbgms6Ac9lu621CDZPGHdxag7hA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.20.0",
|
||||||
|
"npm": ">= 6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "<16.0.0",
|
||||||
|
"@angular/core": "<16.0.0",
|
||||||
|
"@angular/forms": "<16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ng-matero/extensions/node_modules/tslib": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||||
|
},
|
||||||
"node_modules/@ngneat/input-mask": {
|
"node_modules/@ngneat/input-mask": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -9289,6 +9338,11 @@
|
|||||||
"version": "2.1.6",
|
"version": "2.1.6",
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/domq.js": {
|
||||||
|
"version": "0.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/domq.js/-/domq.js-0.6.7.tgz",
|
||||||
|
"integrity": "sha512-WwRGORo/eYGf7v7YXZ3M6x/PEoxCsP3D0my7pnAVwtbfsKRvW6qioSdlLsy1MFzfwN1TM9oO9QJcvkE8ERYmlg=="
|
||||||
|
},
|
||||||
"node_modules/domutils": {
|
"node_modules/domutils": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -13596,6 +13650,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/material-colors": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||||
|
},
|
||||||
"node_modules/md5.js": {
|
"node_modules/md5.js": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -14357,6 +14416,20 @@
|
|||||||
"rxjs": ">= 6.0.0"
|
"rxjs": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ngx-color": {
|
||||||
|
"version": "8.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-8.0.3.tgz",
|
||||||
|
"integrity": "sha512-tuLP+uIoDEu2m0bh711kb2P1M1bh/oIrOn8mJd9mb8xGL2v+OcokcxPmVvWRn0avMG1lXL53CjSlWXGkdV4CDA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ctrl/tinycolor": "^3.4.1",
|
||||||
|
"material-colors": "^1.2.6",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">=14.0.0-0",
|
||||||
|
"@angular/core": ">=14.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ngx-mat-select-search": {
|
"node_modules/ngx-mat-select-search": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-7.0.1.tgz",
|
||||||
@ -15657,6 +15730,14 @@
|
|||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/photoviewer": {
|
||||||
|
"version": "3.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/photoviewer/-/photoviewer-3.6.6.tgz",
|
||||||
|
"integrity": "sha512-TYuxoEdlVkIngVnoCEO+CWjeTO/F4TOPDv9ic4zbVU8ZXMiDggUzMj7jv50Kl0n1Yks72hleujPjAfGmkl7n9w==",
|
||||||
|
"dependencies": {
|
||||||
|
"domq.js": "^0.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
@ -21195,6 +21276,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@ctrl/tinycolor": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ=="
|
||||||
|
},
|
||||||
"@discoveryjs/json-ext": {
|
"@discoveryjs/json-ext": {
|
||||||
"version": "0.5.7"
|
"version": "0.5.7"
|
||||||
},
|
},
|
||||||
@ -22032,6 +22118,32 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@ng-matero/extensions": {
|
||||||
|
"version": "15.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ng-matero/extensions/-/extensions-15.3.0.tgz",
|
||||||
|
"integrity": "sha512-NEPK9ba437d+eDRtj0wSZFUUTCpJlWuJr2ZpbTBwQ/mwN6eyprASM9dq4ydqEcGE+uefxZrSZrVp+eaztNjHjA==",
|
||||||
|
"requires": {
|
||||||
|
"@ng-select/ng-select": "^10.0.0",
|
||||||
|
"ngx-color": "^8.0.0",
|
||||||
|
"photoviewer": "^3.6.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ng-select/ng-select": {
|
||||||
|
"version": "10.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-10.0.4.tgz",
|
||||||
|
"integrity": "sha512-Vc/JIgcFkSgf47cX7+pQQo9HYhDktfqrY7o/ZPGMvu63P7E9d1MibVipqmcLbgms6Ac9lu621CDZPGHdxag7hA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@ngneat/input-mask": {
|
"@ngneat/input-mask": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -25726,6 +25838,11 @@
|
|||||||
"domino": {
|
"domino": {
|
||||||
"version": "2.1.6"
|
"version": "2.1.6"
|
||||||
},
|
},
|
||||||
|
"domq.js": {
|
||||||
|
"version": "0.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/domq.js/-/domq.js-0.6.7.tgz",
|
||||||
|
"integrity": "sha512-WwRGORo/eYGf7v7YXZ3M6x/PEoxCsP3D0my7pnAVwtbfsKRvW6qioSdlLsy1MFzfwN1TM9oO9QJcvkE8ERYmlg=="
|
||||||
|
},
|
||||||
"domutils": {
|
"domutils": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -28469,6 +28586,11 @@
|
|||||||
"object-visit": "^1.0.0"
|
"object-visit": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"material-colors": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||||
|
},
|
||||||
"md5.js": {
|
"md5.js": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -28994,6 +29116,16 @@
|
|||||||
"webpack-merge": "^5.0.0"
|
"webpack-merge": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ngx-color": {
|
||||||
|
"version": "8.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-8.0.3.tgz",
|
||||||
|
"integrity": "sha512-tuLP+uIoDEu2m0bh711kb2P1M1bh/oIrOn8mJd9mb8xGL2v+OcokcxPmVvWRn0avMG1lXL53CjSlWXGkdV4CDA==",
|
||||||
|
"requires": {
|
||||||
|
"@ctrl/tinycolor": "^3.4.1",
|
||||||
|
"material-colors": "^1.2.6",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ngx-mat-select-search": {
|
"ngx-mat-select-search": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-7.0.1.tgz",
|
||||||
@ -29850,6 +29982,14 @@
|
|||||||
"sha.js": "^2.4.8"
|
"sha.js": "^2.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"photoviewer": {
|
||||||
|
"version": "3.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/photoviewer/-/photoviewer-3.6.6.tgz",
|
||||||
|
"integrity": "sha512-TYuxoEdlVkIngVnoCEO+CWjeTO/F4TOPDv9ic4zbVU8ZXMiDggUzMj7jv50Kl0n1Yks72hleujPjAfGmkl7n9w==",
|
||||||
|
"requires": {
|
||||||
|
"domq.js": "^0.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"picocolors": {
|
"picocolors": {
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"build-libs": "ng build ng-core",
|
"build-libs": "ng build ng-core",
|
||||||
"build": "npm run build-libs && npm run build-app",
|
"build": "npm run build-libs && npm run build-app",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 552",
|
"lint": "eslint \"src/**/*.{ts,js,html}\" --max-warnings 420",
|
||||||
"lint-fix": "npm run lint -- --fix",
|
"lint-fix": "npm run lint -- --fix",
|
||||||
"lint-errors": "npm run lint -- --quiet",
|
"lint-errors": "npm run lint -- --quiet",
|
||||||
"lint-libs": "eslint \"projects/**/*.{ts,js,html}\" --max-warnings 0",
|
"lint-libs": "eslint \"projects/**/*.{ts,js,html}\" --max-warnings 0",
|
||||||
@ -36,6 +36,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "15.0.3",
|
"@angular/platform-browser-dynamic": "15.0.3",
|
||||||
"@angular/platform-server": "15.0.3",
|
"@angular/platform-server": "15.0.3",
|
||||||
"@angular/router": "15.0.3",
|
"@angular/router": "15.0.3",
|
||||||
|
"@ng-matero/extensions": "15.3.0",
|
||||||
"@ngneat/input-mask": "6.0.0",
|
"@ngneat/input-mask": "6.0.0",
|
||||||
"@ngneat/until-destroy": "9.2.2",
|
"@ngneat/until-destroy": "9.2.2",
|
||||||
"@s-libs/js-core": "15.0.0",
|
"@s-libs/js-core": "15.0.0",
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
<input formControlName="ns" matInput />
|
<input formControlName="ns" matInput />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<cc-date-range formControlName="timespan"></cc-date-range>
|
<cc-date-range formControlName="timespan"></cc-date-range>
|
||||||
<mat-form-field>
|
<cc-domain-object-field
|
||||||
<mat-label>Provider ID</mat-label>
|
formControlName="provider_id"
|
||||||
<input formControlName="provider_id" matInput />
|
name="provider"
|
||||||
</mat-form-field>
|
></cc-domain-object-field>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Status</mat-label>
|
<mat-label>Status</mat-label>
|
||||||
<mat-select formControlName="status">
|
<mat-select formControlName="status">
|
||||||
@ -34,16 +34,24 @@
|
|||||||
<input formControlName="error_message" matInput />
|
<input formControlName="error_message" matInput />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-footer *ngIf="inProgress$ | async">
|
|
||||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
|
||||||
</mat-card-footer>
|
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<ng-container *ngIf="!(inProgress$ | async) || (machines$ | async)">
|
<cc-simple-table
|
||||||
<cc-actions>
|
[cellTemplate]="cellTemplate"
|
||||||
<button mat-button (click)="update()">UPDATE</button>
|
[columns]="columns$ | async"
|
||||||
|
[data]="machines$ | async"
|
||||||
|
[hasMore]="hasMore$ | async"
|
||||||
|
[loading]="(inProgress$ | async) && !!(columns$ | async)"
|
||||||
|
[trackBy]="trackById"
|
||||||
|
rowSelectable
|
||||||
|
(fetchMore)="fetchMore()"
|
||||||
|
(rowSelectionChange)="selected$.next($event)"
|
||||||
|
(size)="update($event)"
|
||||||
|
(update)="update($event.size)"
|
||||||
|
>
|
||||||
|
<cc-simple-table-actions>
|
||||||
<button
|
<button
|
||||||
[disabled]="!selection?.selected?.length"
|
[disabled]="!(selected$ | async)?.length"
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-button
|
mat-button
|
||||||
(click)="repairByScenario()"
|
(click)="repairByScenario()"
|
||||||
@ -51,82 +59,17 @@
|
|||||||
REPAIR BY SCENARIO
|
REPAIR BY SCENARIO
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
[disabled]="!selection?.selected?.length"
|
[disabled]="!(selected$ | async)?.length"
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-button
|
mat-button
|
||||||
(click)="repair()"
|
(click)="repair()"
|
||||||
>
|
>
|
||||||
SIMPLE REPAIR
|
SIMPLE REPAIR
|
||||||
</button>
|
</button>
|
||||||
</cc-actions>
|
</cc-simple-table-actions>
|
||||||
|
<cc-simple-table-tooltip-cell-template
|
||||||
<cc-empty-search-result *ngIf="!(machines$ | async)?.length"></cc-empty-search-result>
|
(template)="cellTemplate.status = cellTemplate.history = $event"
|
||||||
<mat-card *ngIf="(machines$ | async)?.length" fxLayout="column" fxLayoutGap="18px">
|
></cc-simple-table-tooltip-cell-template>
|
||||||
<table [dataSource]="machines$ | async" mat-table>
|
</cc-simple-table>
|
||||||
<cc-select-column
|
|
||||||
[dataSource]="machines$ | async"
|
|
||||||
(changed)="selection = $event"
|
|
||||||
></cc-select-column>
|
|
||||||
<ng-container [matColumnDef]="cols.def.id">
|
|
||||||
<th *matHeaderCellDef mat-header-cell>ID</th>
|
|
||||||
<td *matCellDef="let i" mat-cell>{{ i.id }}</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container [matColumnDef]="cols.def.namespace">
|
|
||||||
<th *matHeaderCellDef mat-header-cell>Namespace</th>
|
|
||||||
<td *matCellDef="let i" mat-cell>{{ i.ns }}</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container [matColumnDef]="cols.def.createdAt">
|
|
||||||
<th *matHeaderCellDef mat-header-cell>Created at</th>
|
|
||||||
<td *matCellDef="let i" mat-cell>
|
|
||||||
{{ i.created_at | date : 'dd.MM.yyyy HH:mm:ss' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container [matColumnDef]="cols.def.status">
|
|
||||||
<th *matHeaderCellDef mat-header-cell matTooltip="Hover to view details">
|
|
||||||
<span matBadge="ℹ" matBadgeOverlap="false" matBadgeSize="small">
|
|
||||||
Status
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
<td
|
|
||||||
*matCellDef="let i"
|
|
||||||
[matTooltip]="i.error_message"
|
|
||||||
mat-cell
|
|
||||||
matTooltipPosition="left"
|
|
||||||
>
|
|
||||||
{{ i.status | enumKey : status }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container [matColumnDef]="cols.def.provider">
|
|
||||||
<th *matHeaderCellDef mat-header-cell>Provider</th>
|
|
||||||
<td *matCellDef="let i" mat-cell>
|
|
||||||
{{ i.provider_id }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container [matColumnDef]="cols.def.history">
|
|
||||||
<th *matHeaderCellDef mat-header-cell matTooltip="Hover to view details">
|
|
||||||
<span matBadge="ℹ" matBadgeOverlap="false" matBadgeSize="small"
|
|
||||||
>History</span
|
|
||||||
>
|
|
||||||
</th>
|
|
||||||
<td
|
|
||||||
*matCellDef="let i"
|
|
||||||
[matTooltip]="i.history?.length ? (i.history | json) : ''"
|
|
||||||
mat-cell
|
|
||||||
matTooltipPosition="left"
|
|
||||||
>
|
|
||||||
{{ i.history?.length || '' }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<tr *matHeaderRowDef="cols.list" mat-header-row></tr>
|
|
||||||
<tr *matRowDef="let row; columns: cols.list" mat-row></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<cc-show-more-button
|
|
||||||
*ngIf="hasMore$ | async"
|
|
||||||
[inProgress]="inProgress$ | async"
|
|
||||||
(fetchMore)="fetchMore()"
|
|
||||||
></cc-show-more-button>
|
|
||||||
</mat-card>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
import { SelectionModel } from '@angular/cdk/collections';
|
import { Component, OnInit, TemplateRef } from '@angular/core';
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { FormBuilder } from '@angular/forms';
|
import { FormBuilder } from '@angular/forms';
|
||||||
import { DateRange } from '@angular/material/datepicker';
|
import { DateRange } from '@angular/material/datepicker';
|
||||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||||
import { BaseDialogResponseStatus, BaseDialogService, clean, splitIds } from '@vality/ng-core';
|
import { BaseDialogResponseStatus, BaseDialogService, clean, splitIds } from '@vality/ng-core';
|
||||||
import { repairer } from '@vality/repairer-proto';
|
import { repairer } from '@vality/repairer-proto';
|
||||||
import { Namespace, ProviderID, RepairStatus, Machine } from '@vality/repairer-proto/repairer';
|
import { Namespace, ProviderID, RepairStatus, Machine } from '@vality/repairer-proto/repairer';
|
||||||
|
import isNil from 'lodash-es/isNil';
|
||||||
import { Moment } from 'moment';
|
import { Moment } from 'moment';
|
||||||
import { filter, map, switchMap } from 'rxjs/operators';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { filter, map, switchMap, shareReplay } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { DomainStoreService } from '@cc/app/api/deprecated-damsel';
|
||||||
import { NotificationErrorService } from '@cc/app/shared/services/notification-error';
|
import { NotificationErrorService } from '@cc/app/shared/services/notification-error';
|
||||||
|
import { ConfirmActionDialogComponent } from '@cc/components/confirm-action-dialog';
|
||||||
|
import {
|
||||||
|
createGridColumns,
|
||||||
|
createDatetimeFormattedColumn,
|
||||||
|
createDescriptionFormattedColumn,
|
||||||
|
} from '@cc/components/simple-table';
|
||||||
|
import { createTooltipTemplateGridColumn } from '@cc/components/simple-table/components/simple-table-tooltip-cell-template.component';
|
||||||
|
import { getEnumKey } from '@cc/utils';
|
||||||
|
|
||||||
import { ConfirmActionDialogComponent } from '../../../components/confirm-action-dialog';
|
|
||||||
import { Columns, SELECT_COLUMN_NAME } from '../../../components/table';
|
|
||||||
import { RepairManagementService } from '../../api/repairer';
|
import { RepairManagementService } from '../../api/repairer';
|
||||||
import { QueryParamsService } from '../../shared/services';
|
import { QueryParamsService } from '../../shared/services';
|
||||||
import { NotificationService } from '../../shared/services/notification';
|
import { NotificationService } from '../../shared/services/notification';
|
||||||
@ -55,17 +63,41 @@ export class RepairingComponent implements OnInit {
|
|||||||
error_message: null,
|
error_message: null,
|
||||||
...this.qp.params,
|
...this.qp.params,
|
||||||
});
|
});
|
||||||
selection: SelectionModel<Machine>;
|
selected$ = new BehaviorSubject<Machine[]>([]);
|
||||||
cols = new Columns(
|
|
||||||
SELECT_COLUMN_NAME,
|
|
||||||
'id',
|
|
||||||
'namespace',
|
|
||||||
'createdAt',
|
|
||||||
'provider',
|
|
||||||
'status',
|
|
||||||
'history'
|
|
||||||
);
|
|
||||||
status = repairer.RepairStatus;
|
status = repairer.RepairStatus;
|
||||||
|
columns$ = this.domainStoreService.getObjects('provider').pipe(
|
||||||
|
map((providers) =>
|
||||||
|
createGridColumns<Machine>([
|
||||||
|
'id',
|
||||||
|
{ header: 'Namespace', field: 'ns' },
|
||||||
|
createDatetimeFormattedColumn('created_at'),
|
||||||
|
createDescriptionFormattedColumn<Machine>(
|
||||||
|
'provider',
|
||||||
|
(data) =>
|
||||||
|
providers.find((p) => String(p.ref.id) === data.provider_id)?.data?.name,
|
||||||
|
(data) => data.provider_id
|
||||||
|
),
|
||||||
|
createTooltipTemplateGridColumn(
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
formatter: (data: Machine) =>
|
||||||
|
getEnumKey(repairer.RepairStatus, data.status),
|
||||||
|
},
|
||||||
|
(d) => d.error_message
|
||||||
|
),
|
||||||
|
createTooltipTemplateGridColumn(
|
||||||
|
{
|
||||||
|
field: 'history',
|
||||||
|
formatter: (data: Machine) =>
|
||||||
|
data.history?.length ? String(data.history.length) : '',
|
||||||
|
},
|
||||||
|
(d) => d.history
|
||||||
|
),
|
||||||
|
])
|
||||||
|
),
|
||||||
|
shareReplay({ refCount: true, bufferSize: 1 })
|
||||||
|
);
|
||||||
|
cellTemplate: Record<string, TemplateRef<any>> = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private machinesService: MachinesService,
|
private machinesService: MachinesService,
|
||||||
@ -74,7 +106,8 @@ export class RepairingComponent implements OnInit {
|
|||||||
private baseDialogService: BaseDialogService,
|
private baseDialogService: BaseDialogService,
|
||||||
private repairManagementService: RepairManagementService,
|
private repairManagementService: RepairManagementService,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private notificationErrorService: NotificationErrorService
|
private notificationErrorService: NotificationErrorService,
|
||||||
|
private domainStoreService: DomainStoreService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -90,7 +123,7 @@ export class RepairingComponent implements OnInit {
|
|||||||
clean({
|
clean({
|
||||||
ids: splitIds(ids),
|
ids: splitIds(ids),
|
||||||
ns,
|
ns,
|
||||||
provider_id,
|
provider_id: isNil(provider_id) ? null : String(provider_id),
|
||||||
status,
|
status,
|
||||||
error_message,
|
error_message,
|
||||||
timespan:
|
timespan:
|
||||||
@ -107,8 +140,9 @@ export class RepairingComponent implements OnInit {
|
|||||||
.subscribe((params) => this.machinesService.search(params));
|
.subscribe((params) => this.machinesService.search(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update(size: number) {
|
||||||
this.machinesService.refresh();
|
this.machinesService.refresh(size);
|
||||||
|
this.selected$.next([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchMore() {
|
fetchMore() {
|
||||||
@ -118,14 +152,14 @@ export class RepairingComponent implements OnInit {
|
|||||||
repair() {
|
repair() {
|
||||||
this.baseDialogService
|
this.baseDialogService
|
||||||
.open(ConfirmActionDialogComponent, {
|
.open(ConfirmActionDialogComponent, {
|
||||||
title: `Simple repair ${this.selection.selected.length} machines`,
|
title: `Simple repair ${this.selected$.value.length} machines`,
|
||||||
})
|
})
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(({ status }) => status === BaseDialogResponseStatus.Success),
|
filter(({ status }) => status === BaseDialogResponseStatus.Success),
|
||||||
switchMap(() =>
|
switchMap(() =>
|
||||||
this.repairManagementService.SimpleRepairAll(
|
this.repairManagementService.SimpleRepairAll(
|
||||||
this.selection.selected.map(({ id, ns }) => ({ id, ns }))
|
this.selected$.value.map(({ id, ns }) => ({ id, ns }))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
untilDestroyed(this)
|
untilDestroyed(this)
|
||||||
@ -140,9 +174,13 @@ export class RepairingComponent implements OnInit {
|
|||||||
|
|
||||||
repairByScenario() {
|
repairByScenario() {
|
||||||
this.baseDialogService
|
this.baseDialogService
|
||||||
.open(RepairByScenarioDialogComponent, { machines: this.selection.selected })
|
.open(RepairByScenarioDialogComponent, { machines: this.selected$.value })
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(untilDestroyed(this))
|
.pipe(untilDestroyed(this))
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackById(index: number, item: Machine) {
|
||||||
|
return item.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,9 @@ import { MatTableModule } from '@angular/material/table';
|
|||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { ActionsModule, BaseDialogModule } from '@vality/ng-core';
|
import { ActionsModule, BaseDialogModule } from '@vality/ng-core';
|
||||||
|
|
||||||
import { EnumKeyPipe, EnumKeysPipe } from '@cc/app/shared';
|
import { EnumKeyPipe, EnumKeysPipe, DomainObjectFieldComponent } from '@cc/app/shared';
|
||||||
import { MetadataFormModule } from '@cc/app/shared/components/metadata-form';
|
import { MetadataFormModule } from '@cc/app/shared/components/metadata-form';
|
||||||
|
import { SimpleTableModule } from '@cc/components/simple-table';
|
||||||
|
|
||||||
import { EmptySearchResultModule } from '../../../components/empty-search-result';
|
import { EmptySearchResultModule } from '../../../components/empty-search-result';
|
||||||
import { TableModule } from '../../../components/table';
|
import { TableModule } from '../../../components/table';
|
||||||
@ -50,6 +51,8 @@ import { RepairingComponent } from './repairing.component';
|
|||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
EnumKeyPipe,
|
EnumKeyPipe,
|
||||||
EnumKeysPipe,
|
EnumKeysPipe,
|
||||||
|
DomainObjectFieldComponent,
|
||||||
|
SimpleTableModule,
|
||||||
],
|
],
|
||||||
declarations: [RepairingComponent, RepairByScenarioDialogComponent],
|
declarations: [RepairingComponent, RepairByScenarioDialogComponent],
|
||||||
})
|
})
|
||||||
|
@ -15,9 +15,9 @@ export class MachinesService extends PartialFetcher<Machine, SearchRequest> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fetch(params: SearchRequest, continuationToken: string) {
|
protected fetch(params: SearchRequest, continuationToken: string, size: number) {
|
||||||
return this.repairManagementService
|
return this.repairManagementService
|
||||||
.Search({ limit: 100, continuation_token: continuationToken, ...params })
|
.Search({ limit: size, continuation_token: continuationToken, ...params })
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ machines, continuation_token }) => ({
|
map(({ machines, continuation_token }) => ({
|
||||||
result: machines,
|
result: machines,
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
</cc-actions>
|
</cc-actions>
|
||||||
</div>
|
</div>
|
||||||
<cc-simple-table
|
<cc-simple-table
|
||||||
|
[columns]="columns"
|
||||||
[data]="sources$ | async"
|
[data]="sources$ | async"
|
||||||
[inProgress]="!!(progress$ | async)"
|
[loading]="!!(progress$ | async)"
|
||||||
[schema]="schema"
|
|
||||||
noUpdate
|
noUpdate
|
||||||
></cc-simple-table>
|
></cc-simple-table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { StatSource } from '@vality/fistful-proto/internal/fistful_stat';
|
|
||||||
import { BaseDialogService } from '@vality/ng-core';
|
import { BaseDialogService } from '@vality/ng-core';
|
||||||
|
|
||||||
import { Schema } from '@cc/components/simple-table';
|
import {
|
||||||
|
createGridColumns,
|
||||||
|
createDescriptionFormattedColumn,
|
||||||
|
createDatetimeFormattedColumn,
|
||||||
|
} from '@cc/components/simple-table';
|
||||||
|
|
||||||
import { CreateSourceComponent } from './create-source/create-source.component';
|
import { CreateSourceComponent } from './create-source/create-source.component';
|
||||||
import { FetchSourcesService } from './fetch-sources.service';
|
import { FetchSourcesService } from './fetch-sources.service';
|
||||||
@ -21,11 +24,11 @@ import { FetchSourcesService } from './fetch-sources.service';
|
|||||||
export class SourcesComponent {
|
export class SourcesComponent {
|
||||||
sources$ = this.fetchSourcesService.sources$;
|
sources$ = this.fetchSourcesService.sources$;
|
||||||
progress$ = this.fetchSourcesService.progress$;
|
progress$ = this.fetchSourcesService.progress$;
|
||||||
schema = new Schema<StatSource>([
|
columns = createGridColumns([
|
||||||
{ value: 'name', description: 'id' },
|
createDescriptionFormattedColumn('name', 'id'),
|
||||||
'identity',
|
'identity',
|
||||||
'currency_symbolic_code',
|
'currency_symbolic_code',
|
||||||
'created_at',
|
createDatetimeFormattedColumn('created_at'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<cc-simple-table
|
<cc-simple-table
|
||||||
|
[columns]="columns"
|
||||||
[data]="wallets$ | async"
|
[data]="wallets$ | async"
|
||||||
[hasMore]="hasMore$ | async"
|
[hasMore]="hasMore$ | async"
|
||||||
[inProgress]="inProgress$ | async"
|
[loading]="inProgress$ | async"
|
||||||
[schema]="schema"
|
|
||||||
(fetchMore)="fetchMore()"
|
(fetchMore)="fetchMore()"
|
||||||
(size)="search($event)"
|
(size)="search($event)"
|
||||||
(update)="search($event.size)"
|
(update)="search($event.size)"
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormBuilder } from '@angular/forms';
|
import { FormBuilder } from '@angular/forms';
|
||||||
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
|
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { StatWallet } from '@vality/fistful-proto/internal/fistful_stat';
|
|
||||||
import { clean } from '@vality/ng-core';
|
import { clean } from '@vality/ng-core';
|
||||||
import { startWith, map } from 'rxjs/operators';
|
import { startWith, map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { WalletParams } from '@cc/app/api/fistful-stat/query-dsl/types/wallet';
|
import { WalletParams } from '@cc/app/api/fistful-stat/query-dsl/types/wallet';
|
||||||
import { QueryParamsService } from '@cc/app/shared/services';
|
import { QueryParamsService } from '@cc/app/shared/services';
|
||||||
import { Schema } from '@cc/components/simple-table';
|
import {
|
||||||
|
createDatetimeFormattedColumn,
|
||||||
|
createDescriptionFormattedColumn,
|
||||||
|
createGridColumns,
|
||||||
|
} from '@cc/components/simple-table';
|
||||||
|
|
||||||
import { FetchWalletsService } from './fetch-wallets.service';
|
import { FetchWalletsService } from './fetch-wallets.service';
|
||||||
|
|
||||||
@ -29,11 +32,11 @@ export class WalletsComponent implements OnInit {
|
|||||||
wallets$ = this.fetchWalletsService.searchResult$;
|
wallets$ = this.fetchWalletsService.searchResult$;
|
||||||
inProgress$ = this.fetchWalletsService.doAction$;
|
inProgress$ = this.fetchWalletsService.doAction$;
|
||||||
hasMore$ = this.fetchWalletsService.hasMore$;
|
hasMore$ = this.fetchWalletsService.hasMore$;
|
||||||
schema = new Schema<StatWallet>([
|
columns = createGridColumns([
|
||||||
{ value: 'name', description: 'id' },
|
createDescriptionFormattedColumn('name', 'id'),
|
||||||
'currency_symbolic_code',
|
'currency_symbolic_code',
|
||||||
'identity_id',
|
'identity_id',
|
||||||
{ value: 'created_at', type: 'datetime' },
|
createDatetimeFormattedColumn('created_at'),
|
||||||
]);
|
]);
|
||||||
filters = this.fb.group<WalletParams>({
|
filters = this.fb.group<WalletParams>({
|
||||||
party_id: null,
|
party_id: null,
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
<cc-select-search-field
|
||||||
|
[formControl]="control"
|
||||||
|
[label]="name | titlecase"
|
||||||
|
[options]="options$ | async"
|
||||||
|
[progress]="isLoading$ | async"
|
||||||
|
></cc-select-search-field>
|
@ -0,0 +1,60 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { Component, Input, OnChanges } from '@angular/core';
|
||||||
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { DomainObject } from '@vality/domain-proto/internal/domain';
|
||||||
|
import { defer, switchMap, ReplaySubject } from 'rxjs';
|
||||||
|
import { shareReplay, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { DomainStoreService } from '@cc/app/api/deprecated-damsel';
|
||||||
|
import { ComponentChanges } from '@cc/app/shared';
|
||||||
|
import {
|
||||||
|
DOMAIN_OBJECTS_TO_OPTIONS,
|
||||||
|
OtherDomainObjects,
|
||||||
|
defaultDomainObjectToOption,
|
||||||
|
} from '@cc/app/shared/services/domain-metadata-form-extensions/utils/domains-objects-to-options';
|
||||||
|
import { SelectSearchFieldModule } from '@cc/components/select-search-field';
|
||||||
|
import { ValidatedFormControlSuperclass, provideValueAccessor } from '@cc/utils';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'cc-domain-object-field',
|
||||||
|
templateUrl: './domain-object-field.component.html',
|
||||||
|
providers: [provideValueAccessor(() => DomainObjectFieldComponent)],
|
||||||
|
imports: [CommonModule, SelectSearchFieldModule, ReactiveFormsModule],
|
||||||
|
})
|
||||||
|
export class DomainObjectFieldComponent<T extends keyof DomainObject>
|
||||||
|
extends ValidatedFormControlSuperclass<DomainObject[T]>
|
||||||
|
implements OnChanges
|
||||||
|
{
|
||||||
|
@Input() name: T;
|
||||||
|
|
||||||
|
control = new FormControl<DomainObject[T]>(null);
|
||||||
|
|
||||||
|
options$ = defer(() => this.name$).pipe(
|
||||||
|
switchMap((name) => this.domainStoreService.getObjects(name)),
|
||||||
|
map((objs) => {
|
||||||
|
const domainObjectToOption =
|
||||||
|
this.name in DOMAIN_OBJECTS_TO_OPTIONS
|
||||||
|
? DOMAIN_OBJECTS_TO_OPTIONS[this.name as keyof OtherDomainObjects]
|
||||||
|
: defaultDomainObjectToOption;
|
||||||
|
return objs
|
||||||
|
.map(domainObjectToOption)
|
||||||
|
.map((o) => ({ ...o, description: `#${String(o.value)}` }));
|
||||||
|
}),
|
||||||
|
shareReplay({ bufferSize: 1, refCount: true })
|
||||||
|
);
|
||||||
|
isLoading$ = this.domainStoreService.isLoading$;
|
||||||
|
|
||||||
|
private name$ = new ReplaySubject<keyof DomainObject>(1);
|
||||||
|
|
||||||
|
constructor(private domainStoreService: DomainStoreService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: ComponentChanges<DomainObjectFieldComponent<T>>) {
|
||||||
|
super.ngOnChanges(changes);
|
||||||
|
if (changes.name) {
|
||||||
|
this.name$.next(this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/app/shared/components/domain-object-field/index.ts
Normal file
1
src/app/shared/components/domain-object-field/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './domain-object-field.component';
|
@ -8,3 +8,4 @@ export * from './shop-field';
|
|||||||
export * from './shop-details';
|
export * from './shop-details';
|
||||||
export * from './payout-tool-details';
|
export * from './payout-tool-details';
|
||||||
export * from './payout-tool-field';
|
export * from './payout-tool-field';
|
||||||
|
export * from './domain-object-field';
|
||||||
|
@ -95,8 +95,8 @@ export abstract class PartialFetcher<R, P> {
|
|||||||
this.action$.next({ type: 'search', value, size });
|
this.action$.next({ type: 'search', value, size });
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh(size?: number) {
|
||||||
this.action$.next({ type: 'search' });
|
this.action$.next({ type: 'search', size });
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchMore() {
|
fetchMore() {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@use '@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
@use '@ng-matero/extensions' as mtx;
|
||||||
|
|
||||||
@import '../../../components/components-themes';
|
@import '../../../components/components-themes';
|
||||||
@import '../../shared/components/shared-components-themes';
|
@import '../../shared/components/shared-components-themes';
|
||||||
@ -14,6 +15,7 @@
|
|||||||
@include mat.core-theme($theme);
|
@include mat.core-theme($theme);
|
||||||
@include mat.button-theme($theme);
|
@include mat.button-theme($theme);
|
||||||
@include mat.all-legacy-component-themes($theme);
|
@include mat.all-legacy-component-themes($theme);
|
||||||
|
@include mtx.all-component-themes($theme);
|
||||||
|
|
||||||
$foreground: map-get($theme, foreground);
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
|
@ -21,7 +21,10 @@
|
|||||||
*ngFor="let option of isExternalSearch ? options : (filteredOptions$ | async)"
|
*ngFor="let option of isExternalSearch ? options : (filteredOptions$ | async)"
|
||||||
[value]="option.value"
|
[value]="option.value"
|
||||||
>
|
>
|
||||||
{{ option.label }}
|
<div class="label">{{ option.label }}</div>
|
||||||
|
<div *ngIf="option.description" class="mat-caption cc-secondary-text">
|
||||||
|
{{ option.description }}
|
||||||
|
</div>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
<mat-option
|
<mat-option
|
||||||
*ngIf="
|
*ngIf="
|
||||||
@ -32,11 +35,14 @@
|
|||||||
"
|
"
|
||||||
[value]="cachedOption.value"
|
[value]="cachedOption.value"
|
||||||
>
|
>
|
||||||
{{ cachedOption.label }}
|
<div class="label">{{ cachedOption.label }}</div>
|
||||||
|
<div *ngIf="cachedOption.description" class="mat-caption cc-secondary-text">
|
||||||
|
{{ cachedOption.description }}
|
||||||
|
</div>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #progressBar>
|
<ng-template #progressBar>
|
||||||
<mat-option disabled>
|
<mat-option disabled fxLayout="column" fxLayoutAlign="center none">
|
||||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
mat-form-field {
|
mat-form-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FlexModule } from '@angular/flex-layout';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
@ -22,6 +23,7 @@ import { SelectSearchFieldComponent } from './select-search-field.component';
|
|||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
NgxMatSelectSearchModule,
|
NgxMatSelectSearchModule,
|
||||||
MatProgressBarModule,
|
MatProgressBarModule,
|
||||||
|
FlexModule,
|
||||||
],
|
],
|
||||||
declarations: [SelectSearchFieldComponent],
|
declarations: [SelectSearchFieldComponent],
|
||||||
exports: [SelectSearchFieldComponent],
|
exports: [SelectSearchFieldComponent],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export interface Option<T> {
|
export interface Option<T> {
|
||||||
value: T;
|
value: T;
|
||||||
label: string;
|
label: string;
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,10 @@ import { Option } from '../types';
|
|||||||
const filterPredicate =
|
const filterPredicate =
|
||||||
<T>(searchStr: string) =>
|
<T>(searchStr: string) =>
|
||||||
(option: Option<T>) =>
|
(option: Option<T>) =>
|
||||||
option.label.toLowerCase().includes(searchStr);
|
option.label.toLowerCase().includes(searchStr) ||
|
||||||
|
(option.description && option.description.toLowerCase().includes(searchStr)) ||
|
||||||
|
(typeof option.value !== 'object' &&
|
||||||
|
String(option.value).toLowerCase().includes(searchStr));
|
||||||
|
|
||||||
export const filterOptions = <T>(options: Option<T>[], controlValue: unknown): Option<T>[] =>
|
export const filterOptions = <T>(options: Option<T>[], controlValue: unknown): Option<T>[] =>
|
||||||
controlValue && typeof controlValue === 'string'
|
controlValue && typeof controlValue === 'string'
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'cc-simple-table-actions',
|
||||||
|
template: `
|
||||||
|
<div fxLayout fxLayoutGap="16px">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class SimpleTableActionsComponent {}
|
@ -0,0 +1,39 @@
|
|||||||
|
import { Component, ViewChild, TemplateRef, Output, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
|
import { createGridColumn, GridColumn } from '@cc/components/simple-table';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'cc-simple-table-tooltip-cell-template',
|
||||||
|
template: `
|
||||||
|
<ng-template #tpl let-col="colDef" let-index="index" let-row>
|
||||||
|
<div
|
||||||
|
*ngIf="col.formatter ? col.formatter(row, col) : row[col.field] as val"
|
||||||
|
[matTooltip]="col._data?.tooltip && (col._data.tooltip(row) | json)"
|
||||||
|
[ngClass]="{ dashed: !!col._data?.tooltip?.(row) }"
|
||||||
|
matTooltipPosition="right"
|
||||||
|
>
|
||||||
|
{{ val }}
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
`,
|
||||||
|
styles: [
|
||||||
|
`
|
||||||
|
.dashed {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: default;
|
||||||
|
text-decoration-style: dotted;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SimpleTableTooltipCellTemplateComponent {
|
||||||
|
@Output() template = new EventEmitter<TemplateRef<any>>();
|
||||||
|
|
||||||
|
@ViewChild('tpl', { static: true }) set tpl(tpl: TemplateRef<any>) {
|
||||||
|
this.template.emit(tpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTooltipTemplateGridColumn<T>(col: GridColumn<T>, tooltip: (data: T) => any) {
|
||||||
|
return { ...createGridColumn(col), _data: { tooltip } };
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
export * from './types/schema';
|
|
||||||
export * from './simple-table.component';
|
export * from './simple-table.component';
|
||||||
export * from './simple-table.module';
|
export * from './simple-table.module';
|
||||||
|
export * from './utils/create-grid-columns';
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<div fxLayout="column" fxLayoutGap="32px">
|
<div fxLayout="column" fxLayoutGap="32px">
|
||||||
<ng-container *ngIf="data; else init">
|
<div *ngIf="!noUpdate || actions" fxLayout fxLayoutAlign="space-between" fxLayoutGap="8px">
|
||||||
<div *ngIf="!noUpdate" fxLayout fxLayoutGap="16px">
|
<div *ngIf="!noUpdate" fxLayout fxLayoutGap="16px">
|
||||||
<button
|
<button
|
||||||
[disabled]="inProgress"
|
[disabled]="loading"
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
(click)="update.emit(this.size$.value ? { size: this.size$.value } : {})"
|
(click)="update.emit(this.size$.value ? { size: this.size$.value } : {})"
|
||||||
>
|
>
|
||||||
UPDATE
|
UPDATE
|
||||||
</button>
|
</button>
|
||||||
<button [disabled]="inProgress" [matMenuTriggerFor]="menu" mat-button>
|
<button [disabled]="loading" [matMenuTriggerFor]="menu" mat-button>
|
||||||
{{ size$ | async }} <mat-icon>table_rows_narrow</mat-icon>
|
{{ size$ | async }} <mat-icon>table_rows_narrow</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #menu="matMenu">
|
<mat-menu #menu="matMenu">
|
||||||
@ -17,48 +17,29 @@
|
|||||||
<button mat-menu-item (click)="size$.next(1000)">1000</button>
|
<button mat-menu-item (click)="size$.next(1000)">1000</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-content select="cc-simple-table-actions"></ng-content>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-card *ngIf="data.length; else empty" class="table-card">
|
<mat-card class="table-card">
|
||||||
<table [dataSource]="data" mat-table>
|
<mtx-grid
|
||||||
<ng-container *ngFor="let p of schema.params" [matColumnDef]="p.def">
|
[cellSelectable]="false"
|
||||||
<th *matHeaderCellDef mat-header-cell>{{ p.label }}</th>
|
[cellTemplate]="cellTemplate"
|
||||||
<td *matCellDef="let i" mat-cell>
|
[columns]="columns"
|
||||||
<ng-container [ngSwitch]="p.type">
|
[data]="data"
|
||||||
<div>
|
[loading]="loading || !columns"
|
||||||
<ng-template ngSwitchCase="datetime">
|
[paginationTemplate]="footerTpl"
|
||||||
{{ p.value(i) | date : 'dd.MM.yyyy HH:mm:ss' }}
|
[rowSelectable]="rowSelectable"
|
||||||
</ng-template>
|
[trackBy]="trackBy"
|
||||||
<ng-template ngSwitchDefault>
|
(rowSelectionChange)="rowSelectionChange.emit($event)"
|
||||||
{{ p.value(i) }}
|
></mtx-grid>
|
||||||
</ng-template>
|
<ng-template #footerTpl>
|
||||||
</div>
|
|
||||||
<div *ngIf="p.description" class="mat-caption cc-secondary-text">
|
|
||||||
{{ p.description(i) }}
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<tr *matHeaderRowDef="schema.list" mat-header-row></tr>
|
|
||||||
<tr *matRowDef="let row; columns: schema.list" mat-row></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<cc-show-more-button
|
<cc-show-more-button
|
||||||
*ngIf="hasMore"
|
*ngIf="hasMore && columns"
|
||||||
[inProgress]="inProgress"
|
[inProgress]="loading"
|
||||||
class="show-more"
|
class="show-more"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
(fetchMore)="fetchMore.emit(this.size$.value ? { size: this.size$.value } : {})"
|
(fetchMore)="fetchMore.emit(this.size$.value ? { size: this.size$.value } : {})"
|
||||||
></cc-show-more-button>
|
></cc-show-more-button>
|
||||||
</mat-card>
|
</ng-template>
|
||||||
|
</mat-card>
|
||||||
<ng-template #empty
|
|
||||||
><cc-empty-search-result style="width: 100%"></cc-empty-search-result
|
|
||||||
></ng-template>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-template #init>
|
|
||||||
<div *ngIf="inProgress" fxFlex fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<mat-spinner></mat-spinner>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
.table-card {
|
.table-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-more {
|
.show-more {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, OnInit, ContentChild } from '@angular/core';
|
||||||
|
import { MtxGridColumn } from '@ng-matero/extensions/grid';
|
||||||
|
import { MtxGrid } from '@ng-matero/extensions/grid/grid';
|
||||||
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
|
import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { coerceBoolean } from 'coerce-property';
|
import { coerceBoolean } from 'coerce-property';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
import { Schema } from './types/schema';
|
import { SimpleTableActionsComponent } from './components/simple-table-actions.component';
|
||||||
|
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
@Component({
|
@Component({
|
||||||
@ -13,16 +15,22 @@ import { Schema } from './types/schema';
|
|||||||
})
|
})
|
||||||
export class SimpleTableComponent<T> implements OnInit {
|
export class SimpleTableComponent<T> implements OnInit {
|
||||||
@Input() data: T[];
|
@Input() data: T[];
|
||||||
@Input() schema: Schema<T>;
|
@Input() columns: MtxGridColumn[];
|
||||||
|
@Input() cellTemplate?: MtxGrid['cellTemplate'];
|
||||||
|
@Input() trackBy?: MtxGrid['trackBy'];
|
||||||
|
@Input() @coerceBoolean loading = false;
|
||||||
|
|
||||||
|
@Input() @coerceBoolean rowSelectable = false;
|
||||||
|
@Output() rowSelectionChange = new EventEmitter<T[]>();
|
||||||
|
|
||||||
@Input() @coerceBoolean hasMore = false;
|
@Input() @coerceBoolean hasMore = false;
|
||||||
@Input() @coerceBoolean inProgress = false;
|
|
||||||
@Input() @coerceBoolean noUpdate = false;
|
@Input() @coerceBoolean noUpdate = false;
|
||||||
|
|
||||||
@Output() size = new EventEmitter<number>();
|
@Output() size = new EventEmitter<number>();
|
||||||
@Output() update = new EventEmitter<{ size?: number }>();
|
@Output() update = new EventEmitter<{ size?: number }>();
|
||||||
@Output() fetchMore = new EventEmitter<{ size?: number }>();
|
@Output() fetchMore = new EventEmitter<{ size?: number }>();
|
||||||
|
|
||||||
|
@ContentChild(SimpleTableActionsComponent) actions: SimpleTableActionsComponent;
|
||||||
|
|
||||||
size$ = new BehaviorSubject<undefined | number>(25);
|
size$ = new BehaviorSubject<undefined | number>(25);
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -9,10 +9,15 @@ import { MatMenuModule } from '@angular/material/menu';
|
|||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { MtxGridModule } from '@ng-matero/extensions/grid';
|
||||||
|
import { ActionsModule } from '@vality/ng-core';
|
||||||
|
|
||||||
import { TableModule } from '@cc/components/table';
|
import { TableModule } from '@cc/components/table';
|
||||||
|
|
||||||
import { EmptySearchResultModule } from '../empty-search-result';
|
import { EmptySearchResultModule } from '../empty-search-result';
|
||||||
|
import { SimpleTableActionsComponent } from './components/simple-table-actions.component';
|
||||||
|
import { SimpleTableTooltipCellTemplateComponent } from './components/simple-table-tooltip-cell-template.component';
|
||||||
import { SimpleTableComponent } from './simple-table.component';
|
import { SimpleTableComponent } from './simple-table.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -29,8 +34,19 @@ import { SimpleTableComponent } from './simple-table.component';
|
|||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
|
ActionsModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MtxGridModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
SimpleTableComponent,
|
||||||
|
SimpleTableActionsComponent,
|
||||||
|
SimpleTableTooltipCellTemplateComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
SimpleTableComponent,
|
||||||
|
SimpleTableActionsComponent,
|
||||||
|
SimpleTableTooltipCellTemplateComponent,
|
||||||
],
|
],
|
||||||
declarations: [SimpleTableComponent],
|
|
||||||
exports: [SimpleTableComponent],
|
|
||||||
})
|
})
|
||||||
export class SimpleTableModule {}
|
export class SimpleTableModule {}
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
import startCase from 'lodash-es/startCase';
|
|
||||||
import { Overwrite } from 'utility-types';
|
|
||||||
|
|
||||||
export type Path<T> = ((p: T) => string) | keyof T;
|
|
||||||
|
|
||||||
export interface BaseParam<T> {
|
|
||||||
def: string;
|
|
||||||
label: string;
|
|
||||||
value: (p: T) => string;
|
|
||||||
description?: (p: T) => string;
|
|
||||||
type?: 'datetime';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Param<T> = Overwrite<
|
|
||||||
Omit<BaseParam<T>, 'def'>,
|
|
||||||
{
|
|
||||||
label?: string;
|
|
||||||
value: Path<T>;
|
|
||||||
description?: Path<T>;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
function createGetValueFn<T>(v: ((d: T) => string) | keyof T): (d: T) => string {
|
|
||||||
if (typeof v === 'function') return v;
|
|
||||||
return (d) => d[v as any];
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLabel(value: unknown): string {
|
|
||||||
return startCase(String(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Schema<T> {
|
|
||||||
params: BaseParam<T>[];
|
|
||||||
|
|
||||||
get list() {
|
|
||||||
return this.params.map((p) => p.def);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(params: (Param<T> | keyof T)[]) {
|
|
||||||
this.params = params.map((p) => {
|
|
||||||
if (typeof p === 'object')
|
|
||||||
return {
|
|
||||||
def: p.label ?? String(p.value),
|
|
||||||
label: p.label ?? createLabel(p.value),
|
|
||||||
value: createGetValueFn(p.value),
|
|
||||||
description: p.description ? createGetValueFn(p.description) : null,
|
|
||||||
type: p?.type,
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
def: String(p),
|
|
||||||
label: createLabel(p),
|
|
||||||
value: createGetValueFn(p),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
56
src/components/simple-table/utils/create-grid-columns.ts
Normal file
56
src/components/simple-table/utils/create-grid-columns.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { formatDate } from '@angular/common';
|
||||||
|
import { MtxGridColumn } from '@ng-matero/extensions/grid';
|
||||||
|
import isObject from 'lodash-es/isObject';
|
||||||
|
import startCase from 'lodash-es/startCase';
|
||||||
|
import { Overwrite } from 'utility-types';
|
||||||
|
|
||||||
|
export type GridColumn<T> =
|
||||||
|
| (Overwrite<
|
||||||
|
MtxGridColumn,
|
||||||
|
{
|
||||||
|
formatter?: (rowData: T, colDef?: MtxGridColumn) => string;
|
||||||
|
}
|
||||||
|
> & {
|
||||||
|
_data?: any;
|
||||||
|
})
|
||||||
|
| keyof T;
|
||||||
|
|
||||||
|
export function createGridColumn<T>(col: GridColumn<T>) {
|
||||||
|
if (!isObject(col))
|
||||||
|
col = {
|
||||||
|
field: col as string,
|
||||||
|
};
|
||||||
|
if (!col.header) col.header = startCase(String(col.field).toLowerCase());
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createGridColumns<T>(columns: GridColumn<T>[]): MtxGridColumn[] {
|
||||||
|
return columns.map((col) => createGridColumn(col));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDescriptionFormattedColumn<T>(
|
||||||
|
field: string,
|
||||||
|
getDescriptionOrDescriptionField: ((data: T) => string) | string,
|
||||||
|
getValue?: (data: T) => string
|
||||||
|
): MtxGridColumn {
|
||||||
|
return {
|
||||||
|
field,
|
||||||
|
formatter: (data: T) => {
|
||||||
|
const desc =
|
||||||
|
typeof getDescriptionOrDescriptionField === 'function'
|
||||||
|
? getDescriptionOrDescriptionField(data)
|
||||||
|
: String(data[getDescriptionOrDescriptionField]);
|
||||||
|
const value = getValue ? getValue(data) : String(data[field]);
|
||||||
|
return value + (desc ? `<div class="mat-caption cc-secondary-text">${desc}</div>` : '');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDatetimeFormattedColumn<T>(field: string): MtxGridColumn {
|
||||||
|
return {
|
||||||
|
field,
|
||||||
|
formatter: (data: T) => formatDate(data[field], 'dd.MM.yyyy HH:mm:ss', 'en'),
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user