16 changed files with 710 additions and 16 deletions
@ -0,0 +1,106 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2023 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div class="tb-card-content" fxLayout="column" fxLayoutGap="8px"> |
||||
|
<div class="tb-card-header"> |
||||
|
<a class="tb-title-link" routerLink="/dashboards">{{ 'widgets.recent-dashboards.title' | translate }}</a> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-toggle-header #dashboardsToggle [value]="toggleValue" name="usageToggle" (valueChange)="toggleValueChange($event)" [options]="[ |
||||
|
{ |
||||
|
name: ctx.translate.instant('widgets.recent-dashboards.last'), |
||||
|
value: 'last' |
||||
|
}, |
||||
|
{ |
||||
|
name: ctx.translate.instant('widgets.recent-dashboards.starred'), |
||||
|
value: 'starred' |
||||
|
} |
||||
|
]"> |
||||
|
</tb-toggle-header> |
||||
|
<a *ngIf="authUser.authority === authority.TENANT_ADMIN" fxHide.md |
||||
|
mat-flat-button color="primary" routerLink="/dashboards" [queryParams]="{action: 'add'}">{{ 'dashboard.add' | translate }}</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
<ng-container *ngIf="userDashboardsInfo; else loading" [ngSwitch]="dashboardsToggle.value"> |
||||
|
<ng-template [ngSwitchCase]="'last'"> |
||||
|
<div *ngIf="hasLastVisitedDashboards(); else noLastVisitedDashboards" style="overflow-y: auto;"> |
||||
|
<table mat-table |
||||
|
[dataSource]="lastVisitedDashboardsDataSource" matSort |
||||
|
[matSortActive]="lastVisitedDashboardsPageLink.sortOrder?.property" |
||||
|
[matSortDirection]="lastVisitedDashboardsPageLink.sortDirection()" matSortDisableClear> |
||||
|
<ng-container matColumnDef="starred"> |
||||
|
<mat-header-cell class="star-cell" *matHeaderCellDef> |
||||
|
</mat-header-cell> |
||||
|
<mat-cell class="star-cell" *matCellDef="let lastVisitedDashboard"> |
||||
|
<mat-icon (click)="toggleDashboardStar(lastVisitedDashboard)" |
||||
|
class="star" [ngClass]="{'starred': lastVisitedDashboard.starred}">{{ lastVisitedDashboard.starred ? 'star' : 'star_border' }}</mat-icon> |
||||
|
</mat-cell> |
||||
|
</ng-container> |
||||
|
<ng-container matColumnDef="title"> |
||||
|
<mat-header-cell class="title" *matHeaderCellDef mat-sort-header> |
||||
|
{{ 'widgets.recent-dashboards.name' | translate }} |
||||
|
</mat-header-cell> |
||||
|
<mat-cell class="title" *matCellDef="let lastVisitedDashboard"> |
||||
|
<a routerLink="/dashboards/{{lastVisitedDashboard.id}}">{{ lastVisitedDashboard.title }}</a> |
||||
|
</mat-cell> |
||||
|
</ng-container> |
||||
|
<ng-container matColumnDef="lastVisited"> |
||||
|
<mat-header-cell class="last-visited" *matHeaderCellDef mat-sort-header> |
||||
|
{{ 'widgets.recent-dashboards.last-viewed' | translate }} |
||||
|
</mat-header-cell> |
||||
|
<mat-cell class="last-visited" *matCellDef="let lastVisitedDashboard"> |
||||
|
{{ lastVisitedDashboard.lastVisited | dateAgo:{applyAgo: true} }} |
||||
|
</mat-cell> |
||||
|
</ng-container> |
||||
|
<mat-header-row *matHeaderRowDef="lastVisitedDashboardsColumns; sticky: true"></mat-header-row> |
||||
|
<mat-row *matRowDef="let lastVisitedDashboard; columns: lastVisitedDashboardsColumns;"></mat-row> |
||||
|
</table> |
||||
|
</div> |
||||
|
<ng-template #noLastVisitedDashboards> |
||||
|
<div class="tb-no-dashboards"> |
||||
|
<div class="tb-no-last-visited-bg"></div> |
||||
|
<div class="tb-no-last-visited-dashboards-text">{{ 'widgets.recent-dashboards.no-last-viewed-dashboards' | translate }}</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</ng-template> |
||||
|
<ng-template [ngSwitchCase]="'starred'"> |
||||
|
<div style="overflow-y: auto;"> |
||||
|
<div class="tb-starred-dashboard-row" *ngFor="let dashboard of userDashboardsInfo?.starred"> |
||||
|
<div class="tb-cell star-cell"> |
||||
|
<mat-icon (click)="toggleDashboardStar(dashboard)" |
||||
|
class="star" [ngClass]="{'starred': dashboard.starred}">{{ dashboard.starred ? 'star' : 'star_border' }}</mat-icon> |
||||
|
</div> |
||||
|
<div class="tb-cell title"> |
||||
|
<a routerLink="/dashboards/{{dashboard.id}}">{{ dashboard.title }}</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<tb-dashboard-autocomplete class="tb-star-dashboard-autocomplete" |
||||
|
#starDashboardAutocomplete |
||||
|
subscriptSizing="dynamic" |
||||
|
appearance="outline" |
||||
|
[useIdValue]="false" |
||||
|
label="" |
||||
|
placeholder="{{ 'dashboard.select-dashboard' | translate }}" |
||||
|
[(ngModel)]="starredDashboardValue" (ngModelChange)="onStarDashboard($event)"></tb-dashboard-autocomplete> |
||||
|
</ng-template> |
||||
|
</ng-container> |
||||
|
<ng-template #loading> |
||||
|
<div class="tb-no-dashboards"> |
||||
|
<mat-spinner [diameter]="50" mode="indeterminate"></mat-spinner> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</div> |
||||
@ -0,0 +1,235 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
@import "../../../../../../../scss/constants"; |
||||
|
|
||||
|
:host { |
||||
|
.tb-card-header { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.tb-no-dashboards { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.tb-no-last-visited-bg { |
||||
|
width: 60px; |
||||
|
height: 60px; |
||||
|
margin: 10px; |
||||
|
position: relative; |
||||
|
&:before { |
||||
|
content: ""; |
||||
|
border-radius: 8px; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background: #305680; |
||||
|
-webkit-mask-image: url(/assets/home/no_data_bg.svg); |
||||
|
-webkit-mask-repeat: no-repeat; |
||||
|
mask-image: url(/assets/home/no_data_bg.svg); |
||||
|
mask-repeat: no-repeat; |
||||
|
} |
||||
|
} |
||||
|
.tb-no-last-visited-dashboards-text { |
||||
|
font-weight: 500; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
letter-spacing: 0.25px; |
||||
|
color: rgba(0, 0, 0, 0.54); |
||||
|
} |
||||
|
|
||||
|
.tb-starred-dashboard-row { |
||||
|
height: 40px; |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
@media #{$mat-md-lg} { |
||||
|
height: 34px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-cell { |
||||
|
box-sizing: content-box; |
||||
|
&:first-child { |
||||
|
padding: 0 12px; |
||||
|
} |
||||
|
&:nth-child(n+2):nth-last-child(n+2) { |
||||
|
padding: 0 28px 0 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-star-dashboard-autocomplete { |
||||
|
margin-left: 64px; |
||||
|
margin-right: 64px; |
||||
|
@media #{$mat-md-lg} { |
||||
|
margin-left: 48px; |
||||
|
margin-right: 48px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
:host ::ng-deep { |
||||
|
.mat-mdc-cell, .mat-mdc-header-cell, .mdc-data-table__row:last-child .mdc-data-table__cell { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.mat-mdc-table { |
||||
|
.mdc-data-table__row, .mdc-data-table__header-row { |
||||
|
height: 40px; |
||||
|
@media #{$mat-md-lg} { |
||||
|
height: 34px; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: inherit; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-cell:nth-child(n+2):nth-last-child(n+2) { |
||||
|
padding: 0 28px 0 0; |
||||
|
} |
||||
|
|
||||
|
.mat-mdc-row:not(.mat-row-select) .mat-mdc-cell:nth-child(n+2):nth-last-child(n+2) { |
||||
|
@media #{$mat-md-lg} { |
||||
|
padding: 0 12px 0 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.mat-mdc-header-cell, .mat-mdc-cell, .tb-cell { |
||||
|
&.star-cell { |
||||
|
width: 40px; |
||||
|
min-width: 40px; |
||||
|
@media #{$mat-md-lg} { |
||||
|
width: 24px; |
||||
|
min-width: 24px; |
||||
|
max-width: 24px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.title { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
&.last-visited { |
||||
|
width: 80px; |
||||
|
@media #{$mat-md-lg} { |
||||
|
width: 50px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.mat-mdc-header-cell { |
||||
|
font-weight: 400; |
||||
|
font-size: 12px; |
||||
|
line-height: 16px; |
||||
|
letter-spacing: 0.25px; |
||||
|
color: rgba(0, 0, 0, 0.38); |
||||
|
@media #{$mat-md-lg} { |
||||
|
font-size: 11px; |
||||
|
} |
||||
|
} |
||||
|
.mat-mdc-cell.title { |
||||
|
max-width: 0; |
||||
|
} |
||||
|
.mat-mdc-cell, .tb-cell { |
||||
|
font-weight: 400; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
letter-spacing: 0.2px; |
||||
|
@media #{$mat-md-lg} { |
||||
|
font-size: 11px; |
||||
|
line-height: 16px; |
||||
|
} |
||||
|
&.title { |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
a { |
||||
|
color: rgba(0, 0, 0, 0.87); |
||||
|
border-bottom: none; |
||||
|
&:hover, &:focus { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
&.last-visited { |
||||
|
cursor: default; |
||||
|
color: rgba(0, 0, 0, 0.54); |
||||
|
} |
||||
|
.star { |
||||
|
cursor: pointer; |
||||
|
vertical-align: middle; |
||||
|
color: rgba(0, 0, 0, 0.12); |
||||
|
&:hover { |
||||
|
color: rgba(0, 0, 0, 0.18); |
||||
|
} |
||||
|
&.starred { |
||||
|
color: #FAE205; |
||||
|
&:hover { |
||||
|
color: #CDBD2C; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.tb-star-dashboard-autocomplete { |
||||
|
.mat-mdc-form-field { |
||||
|
font-weight: 400; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
letter-spacing: 0.2px; |
||||
|
color: rgba(0, 0, 0, 0.54); |
||||
|
@media #{$mat-md-lg} { |
||||
|
font-size: 11px; |
||||
|
line-height: 16px; |
||||
|
} |
||||
|
} |
||||
|
.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix { |
||||
|
padding-top: 4px; |
||||
|
padding-bottom: 4px; |
||||
|
min-height: 28px; |
||||
|
@media #{$mat-md-lg} { |
||||
|
min-height: 24px; |
||||
|
} |
||||
|
} |
||||
|
.mat-mdc-form-field-hint-wrapper { |
||||
|
height: 0; |
||||
|
} |
||||
|
.mdc-text-field--outlined .mdc-notched-outline { |
||||
|
.mdc-notched-outline__leading, .mdc-notched-outline__trailing { |
||||
|
border-top-style: dashed; |
||||
|
border-bottom-style: dashed; |
||||
|
} |
||||
|
.mdc-notched-outline__leading { |
||||
|
border-left-style: dashed; |
||||
|
} |
||||
|
.mdc-notched-outline__trailing { |
||||
|
border-right-style: dashed; |
||||
|
} |
||||
|
} |
||||
|
.mat-mdc-icon-button.mat-mdc-button-base { |
||||
|
width: 28px; |
||||
|
height: 28px; |
||||
|
padding: 2px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,222 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { |
||||
|
AfterViewInit, |
||||
|
ChangeDetectorRef, |
||||
|
Component, |
||||
|
Input, |
||||
|
OnDestroy, |
||||
|
OnInit, |
||||
|
QueryList, ViewChild, |
||||
|
ViewChildren |
||||
|
} from '@angular/core'; |
||||
|
import { PageComponent } from '@shared/components/page.component'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { Authority } from '@shared/models/authority.enum'; |
||||
|
import { BehaviorSubject, Observable, of } from 'rxjs'; |
||||
|
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; |
||||
|
import { WidgetContext } from '@home/models/widget-component.models'; |
||||
|
import { |
||||
|
AbstractUserDashboardInfo, |
||||
|
LastVisitedDashboardInfo, StarredDashboardInfo, |
||||
|
UserDashboardAction, |
||||
|
UserDashboardsInfo |
||||
|
} from '@shared/models/user-settings.models'; |
||||
|
import { UserSettingsService } from '@core/http/user-settings.service'; |
||||
|
import { CollectionViewer, DataSource } from '@angular/cdk/collections'; |
||||
|
import { emptyPageData, PageData } from '@shared/models/page/page-data'; |
||||
|
import { MAX_SAFE_PAGE_SIZE, PageLink } from '@shared/models/page/page-link'; |
||||
|
import { map, share } from 'rxjs/operators'; |
||||
|
import { Direction, SortOrder } from '@shared/models/page/sort-order'; |
||||
|
import { MatSort } from '@angular/material/sort'; |
||||
|
import { DashboardInfo } from '@shared/models/dashboard.models'; |
||||
|
import { DashboardAutocompleteComponent } from '@shared/components/dashboard-autocomplete.component'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-recent-dashboards-widget', |
||||
|
templateUrl: './recent-dashboards-widget.component.html', |
||||
|
styleUrls: ['./home-page-widget.scss', './recent-dashboards-widget.component.scss'] |
||||
|
}) |
||||
|
export class RecentDashboardsWidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy { |
||||
|
|
||||
|
@Input() |
||||
|
ctx: WidgetContext; |
||||
|
|
||||
|
@ViewChildren(MatSort) lastVisitedDashboardsSort: QueryList<MatSort>; |
||||
|
|
||||
|
@ViewChild('starDashboardAutocomplete', {static: false}) |
||||
|
starDashboardAutocomplete: DashboardAutocompleteComponent; |
||||
|
|
||||
|
authority = Authority; |
||||
|
|
||||
|
userDashboardsInfo: UserDashboardsInfo; |
||||
|
authUser = getCurrentAuthUser(this.store); |
||||
|
|
||||
|
toggleValue: 'last' | 'starred' = 'last'; |
||||
|
|
||||
|
lastVisitedDashboardsColumns = ['starred', 'title', 'lastVisited']; |
||||
|
lastVisitedDashboardsDataSource: LastVisitedDashboardsDataSource; |
||||
|
lastVisitedDashboardsPageLink: PageLink; |
||||
|
|
||||
|
starredDashboardValue = null; |
||||
|
|
||||
|
dirty = false; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private cd: ChangeDetectorRef, |
||||
|
private userSettingService: UserSettingsService) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.reload(); |
||||
|
} |
||||
|
|
||||
|
reload() { |
||||
|
this.userDashboardsInfo = null; |
||||
|
this.cd.markForCheck(); |
||||
|
(this.authUser.authority !== Authority.SYS_ADMIN ? |
||||
|
this.userSettingService.getUserDashboardsInfo() : of({last: [], starred: []})).subscribe( |
||||
|
(userDashboardsInfo) => { |
||||
|
this.userDashboardsInfo = userDashboardsInfo; |
||||
|
for (const starredDashboard of this.userDashboardsInfo?.starred) { |
||||
|
starredDashboard.starred = true; |
||||
|
} |
||||
|
this.userDashboardsInfo?.starred.sort((a, b) => a.starredAt - b.starredAt); |
||||
|
if (this.hasLastVisitedDashboards()) { |
||||
|
this.initLastVisitedDashboardsDataSource(); |
||||
|
} |
||||
|
this.cd.markForCheck(); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
toggleValueChange(value: 'last' | 'starred') { |
||||
|
this.toggleValue = value; |
||||
|
if (this.dirty) { |
||||
|
this.dirty = false; |
||||
|
this.reload(); |
||||
|
} else { |
||||
|
if (value === 'last' && this.hasLastVisitedDashboards()) { |
||||
|
this.initLastVisitedDashboardsDataSource(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private initLastVisitedDashboardsDataSource() { |
||||
|
this.lastVisitedDashboardsDataSource = new LastVisitedDashboardsDataSource(this.userDashboardsInfo.last); |
||||
|
const sortOrder: SortOrder = { |
||||
|
property: 'lastVisited', |
||||
|
direction: Direction.DESC |
||||
|
}; |
||||
|
this.lastVisitedDashboardsPageLink = new PageLink(MAX_SAFE_PAGE_SIZE, 0, null, sortOrder); |
||||
|
this.lastVisitedDashboardsDataSource.loadData(this.lastVisitedDashboardsPageLink); |
||||
|
} |
||||
|
|
||||
|
ngAfterViewInit() { |
||||
|
this.lastVisitedDashboardsSort.changes.subscribe(() => { |
||||
|
if (this.lastVisitedDashboardsSort.length) { |
||||
|
this.lastVisitedDashboardsSort.get(0).sortChange.subscribe(() => |
||||
|
this.updateLastVisitedDashboardsData(this.lastVisitedDashboardsSort.get(0))); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
updateLastVisitedDashboardsData(sort: MatSort) { |
||||
|
this.lastVisitedDashboardsPageLink.sortOrder.property = sort.active; |
||||
|
this.lastVisitedDashboardsPageLink.sortOrder.direction = Direction[sort.direction.toUpperCase()]; |
||||
|
this.lastVisitedDashboardsDataSource.loadData(this.lastVisitedDashboardsPageLink); |
||||
|
} |
||||
|
|
||||
|
hasLastVisitedDashboards(): boolean { |
||||
|
return !!(this.userDashboardsInfo && this.userDashboardsInfo.last && this.userDashboardsInfo.last.length); |
||||
|
} |
||||
|
|
||||
|
toggleDashboardStar(dashboard: AbstractUserDashboardInfo): void { |
||||
|
const action: UserDashboardAction = dashboard.starred ? UserDashboardAction.UNSTAR : UserDashboardAction.STAR; |
||||
|
dashboard.starred = !dashboard.starred; |
||||
|
this.userSettingService.reportUserDashboardAction(dashboard.id, action, {ignoreLoading: true}).subscribe(); |
||||
|
this.dirty = true; |
||||
|
if (this.toggleValue === 'starred') { |
||||
|
const index = this.userDashboardsInfo.starred.findIndex((d) => d.id === dashboard.id); |
||||
|
if (index > -1) { |
||||
|
this.userDashboardsInfo.starred.splice(index, 1); |
||||
|
this.cd.markForCheck(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onStarDashboard(dashboard: DashboardInfo) { |
||||
|
if (dashboard) { |
||||
|
this.starDashboardAutocomplete.clear(); |
||||
|
const index = this.userDashboardsInfo.starred.findIndex((d) => d.id === dashboard.id.id); |
||||
|
if (index === -1) { |
||||
|
const starredDashboard: StarredDashboardInfo = { |
||||
|
starredAt: Date.now(), |
||||
|
id: dashboard.id.id, |
||||
|
starred: true, |
||||
|
title: dashboard.title |
||||
|
}; |
||||
|
this.userDashboardsInfo.starred.push(starredDashboard); |
||||
|
this.userSettingService.reportUserDashboardAction(dashboard.id.id, UserDashboardAction.STAR, {ignoreLoading: true}).subscribe(); |
||||
|
} |
||||
|
this.cd.markForCheck(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
export class LastVisitedDashboardsDataSource implements DataSource<LastVisitedDashboardInfo> { |
||||
|
|
||||
|
private lastVisitedDashboardsSubject = new BehaviorSubject<LastVisitedDashboardInfo[]>([]); |
||||
|
private pageDataSubject = new BehaviorSubject<PageData<LastVisitedDashboardInfo>>(emptyPageData<LastVisitedDashboardInfo>()); |
||||
|
|
||||
|
public pageData$ = this.pageDataSubject.asObservable(); |
||||
|
|
||||
|
constructor(private lastVisitedDashboards: Array<LastVisitedDashboardInfo>) { |
||||
|
} |
||||
|
|
||||
|
connect(collectionViewer: CollectionViewer): Observable<LastVisitedDashboardInfo[] | ReadonlyArray<LastVisitedDashboardInfo>> { |
||||
|
return this.lastVisitedDashboardsSubject.asObservable(); |
||||
|
} |
||||
|
|
||||
|
disconnect(collectionViewer: CollectionViewer): void { |
||||
|
this.lastVisitedDashboardsSubject.complete(); |
||||
|
this.pageDataSubject.complete(); |
||||
|
} |
||||
|
|
||||
|
loadData(pageLink: PageLink): void { |
||||
|
const result = pageLink.filterData(this.lastVisitedDashboards); |
||||
|
this.lastVisitedDashboardsSubject.next(result.data); |
||||
|
this.pageDataSubject.next(result); |
||||
|
} |
||||
|
|
||||
|
isEmpty(): Observable<boolean> { |
||||
|
return this.lastVisitedDashboardsSubject.pipe( |
||||
|
map((entities) => !entities.length), |
||||
|
share() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
total(): Observable<number> { |
||||
|
return this.pageDataSubject.pipe( |
||||
|
map((pageData) => pageData.totalElements), |
||||
|
share() |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 1.8 KiB |
Loading…
Reference in new issue