60 changed files with 1271 additions and 118 deletions
@ -0,0 +1,57 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2021 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. |
|||
|
|||
--> |
|||
<mat-card class="settings-card"> |
|||
<mat-toolbar class="details-toolbar"> |
|||
<div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center"> |
|||
<div class="tb-details-title-header" fxLayout="column" fxLayoutAlign="center start"> |
|||
<div class="tb-details-title tb-ellipsis">{{ headerTitle }}</div> |
|||
<div class="tb-details-subtitle tb-ellipsis">{{ headerSubtitle }}</div> |
|||
</div> |
|||
<div class="tb-help" [tb-help]="helpLinkId()"></div> |
|||
<span fxFlex></span> |
|||
<section *ngIf="!isReadOnly" fxLayout="row" class="tb-header-button" fxLayoutGap="8px"> |
|||
<button [disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty" |
|||
mat-fab |
|||
matTooltip="{{ 'action.apply-changes' | translate }}" |
|||
matTooltipPosition="above" |
|||
color="accent" class="tb-btn-header" |
|||
[ngClass]="{'tb-hide': !isEdit}" |
|||
(click)="onApplyDetails()"> |
|||
<mat-icon class="material-icons">done</mat-icon> |
|||
</button> |
|||
<button [disabled]="(isLoading$ | async)" |
|||
mat-fab |
|||
matTooltip="{{ 'action.decline-changes' | translate }}" |
|||
matTooltipPosition="above" |
|||
color="accent" class="tb-btn-header" |
|||
(click)="onToggleDetailsEditMode()"> |
|||
<mat-icon class="material-icons">{{isEdit ? 'close' : 'edit'}}</mat-icon> |
|||
</button> |
|||
</section> |
|||
</div> |
|||
</mat-toolbar> |
|||
<mat-card-content fxFlex="100"> |
|||
<mat-tab-group class="tb-absolute-fill" [ngClass]="{'tb-headless': hideDetailsTabs()}" [(selectedIndex)]="selectedTab" fxFill> |
|||
<mat-tab label="{{ 'details.details' | translate }}"> |
|||
<tb-anchor #entityDetailsForm></tb-anchor> |
|||
</mat-tab> |
|||
<tb-anchor #entityTabs></tb-anchor> |
|||
</mat-tab-group> |
|||
</mat-card-content> |
|||
</mat-card> |
|||
|
|||
@ -0,0 +1,119 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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 { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
overflow: hidden; |
|||
|
|||
.settings-card { |
|||
margin: 8px; |
|||
padding: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.details-toolbar { |
|||
height: 84px; |
|||
min-height: 84px; |
|||
border-radius: 4px 4px 0 0; |
|||
background: #fff; |
|||
border-bottom: 1px solid rgba(0, 0, 0, 0.12); |
|||
|
|||
.mat-toolbar-tools { |
|||
padding: 0 8px; |
|||
} |
|||
|
|||
.tb-details-title-header { |
|||
min-width: 0; |
|||
width: auto; |
|||
} |
|||
|
|||
.tb-details-title { |
|||
font-size: 1rem; |
|||
font-weight: 500; |
|||
|
|||
@media #{$mat-gt-sm} { |
|||
font-size: 1.2rem; |
|||
} |
|||
} |
|||
|
|||
.tb-details-subtitle { |
|||
font-size: 0.9rem; |
|||
opacity: .8; |
|||
} |
|||
|
|||
.tb-ellipsis { |
|||
width: 100%; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
} |
|||
|
|||
@media #{$mat-md} { |
|||
width: 80%; |
|||
} |
|||
|
|||
@media #{$mat-gt-md} { |
|||
width: 60%; |
|||
} |
|||
|
|||
.tb-header-button { |
|||
.tb-btn-header { |
|||
position: relative !important; |
|||
display: inline-block !important; |
|||
animation: tbMoveFromTopFade .3s ease both; |
|||
|
|||
&.tb-hide { |
|||
animation: tbMoveToTopFade .3s ease both; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
:host ::ng-deep { |
|||
.tb-help { |
|||
.mat-icon-button.mat-primary { |
|||
color: rgba(0, 0, 0, 0.52); |
|||
} |
|||
} |
|||
|
|||
.mat-card-content { |
|||
position: relative; |
|||
overflow: hidden; |
|||
|
|||
> .mat-tab-group { |
|||
> .mat-tab-body-wrapper { |
|||
position: absolute; |
|||
top: 49px; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
} |
|||
> .mat-tab-header { |
|||
.mat-tab-label { |
|||
min-width: 40px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,154 @@ |
|||
///
|
|||
/// Copyright © 2016-2021 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 { |
|||
ChangeDetectionStrategy, |
|||
ChangeDetectorRef, |
|||
Component, |
|||
ComponentFactoryResolver, |
|||
HostBinding, |
|||
Injector, |
|||
OnDestroy, |
|||
OnInit |
|||
} from '@angular/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
|||
import { BaseData, HasId } from '@shared/models/base-data'; |
|||
import { ActivatedRoute, Router } from '@angular/router'; |
|||
import { FormGroup } from '@angular/forms'; |
|||
import { AssetId } from '@shared/models/id/asset-id'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { deepClone, mergeDeep } from '@core/utils'; |
|||
import { BroadcastService } from '@core/services/broadcast.service'; |
|||
import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-entity-details-page', |
|||
templateUrl: './entity-details-page.component.html', |
|||
styleUrls: ['./entity-details-page.component.scss'], |
|||
changeDetection: ChangeDetectionStrategy.OnPush |
|||
}) |
|||
export class EntityDetailsPageComponent extends EntityDetailsPanelComponent implements OnInit, OnDestroy { |
|||
|
|||
headerTitle: string; |
|||
headerSubtitle: string; |
|||
|
|||
isReadOnly = false; |
|||
|
|||
set entitiesTableConfig(entitiesTableConfig: EntityTableConfig<BaseData<HasId>>) { |
|||
if (this.entitiesTableConfigValue !== entitiesTableConfig) { |
|||
this.entitiesTableConfigValue = entitiesTableConfig; |
|||
if (this.entitiesTableConfigValue) { |
|||
this.isEdit = false; |
|||
this.entity = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
get entitiesTableConfig(): EntityTableConfig<BaseData<HasId>> { |
|||
return this.entitiesTableConfigValue; |
|||
} |
|||
|
|||
@HostBinding('class') 'tb-absolute-fill'; |
|||
|
|||
constructor(private route: ActivatedRoute, |
|||
private router: Router, |
|||
protected injector: Injector, |
|||
protected cd: ChangeDetectorRef, |
|||
protected componentFactoryResolver: ComponentFactoryResolver, |
|||
private broadcast: BroadcastService, |
|||
private translate: TranslateService, |
|||
protected store: Store<AppState>) { |
|||
super(store, injector, cd, componentFactoryResolver); |
|||
this.entitiesTableConfig = this.route.snapshot.data.entitiesTableConfig; |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.headerSubtitle = ''; |
|||
this.route.paramMap.subscribe( paramMap => { |
|||
this.entityId = new AssetId(paramMap.get('entityId')); |
|||
}); |
|||
this.headerSubtitle = this.translate.instant(this.entitiesTableConfig.entityTranslations.details); |
|||
super.init(); |
|||
this.entityComponent.isDetailsPage = true; |
|||
} |
|||
|
|||
ngOnDestroy() { |
|||
super.ngOnDestroy(); |
|||
} |
|||
|
|||
reload(): void { |
|||
this.isEdit = false; |
|||
this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe( |
|||
(entity) => { |
|||
this.entity = entity; |
|||
this.broadcast.broadcast('updateBreadcrumb'); |
|||
this.isReadOnly = this.entitiesTableConfig.detailsReadonly(entity); |
|||
this.headerTitle = this.entitiesTableConfig.entityTitle(entity); |
|||
this.entityComponent.entity = entity; |
|||
this.entityComponent.isEdit = false; |
|||
if (this.entityTabsComponent) { |
|||
this.entityTabsComponent.entity = entity; |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
onToggleDetailsEditMode() { |
|||
if (this.isEdit) { |
|||
this.entityComponent.entity = this.entity; |
|||
if (this.entityTabsComponent) { |
|||
this.entityTabsComponent.entity = this.entity; |
|||
} |
|||
this.isEdit = !this.isEdit; |
|||
} else { |
|||
this.isEdit = !this.isEdit; |
|||
this.editingEntity = deepClone(this.entity); |
|||
this.entityComponent.entity = this.editingEntity; |
|||
if (this.entityTabsComponent) { |
|||
this.entityTabsComponent.entity = this.editingEntity; |
|||
} |
|||
if (this.entitiesTableConfig.hideDetailsTabsOnEdit) { |
|||
this.selectedTab = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
onApplyDetails() { |
|||
if (this.detailsForm && this.detailsForm.valid) { |
|||
const editingEntity = {...this.editingEntity, ...this.detailsForm.getRawValue()}; |
|||
if (this.detailsForm.hasOwnProperty('additionalInfo')) { |
|||
editingEntity.additionalInfo = |
|||
mergeDeep((this.editingEntity as any).additionalInfo, this.detailsForm.getRawValue()?.additionalInfo); |
|||
} |
|||
this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe( |
|||
(entity) => { |
|||
this.entity = entity; |
|||
this.entityComponent.entity = entity; |
|||
if (this.entityTabsComponent) { |
|||
this.entityTabsComponent.entity = entity; |
|||
} |
|||
this.isEdit = false; |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
confirmForm(): FormGroup { |
|||
return this.detailsForm; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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. |
|||
*/ |
|||
:host{ |
|||
.mat-icon-button a { |
|||
border-bottom: none; |
|||
color: inherit; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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. |
|||
*/ |
|||
:host{ |
|||
.mat-icon-button a { |
|||
border-bottom: none; |
|||
color: inherit; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/** |
|||
* Copyright © 2016-2021 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. |
|||
*/ |
|||
:host{ |
|||
.mat-icon-button a { |
|||
border-bottom: none; |
|||
color: inherit; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue