committed by
GitHub
60 changed files with 1301 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,182 @@ |
|||||
|
///
|
||||
|
/// 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'; |
||||
|
import { DialogService } from '@core/services/dialog.service'; |
||||
|
|
||||
|
@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, |
||||
|
private dialogService: DialogService, |
||||
|
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; |
||||
|
this.subscriptions.push(this.entityAction.subscribe((action) => { |
||||
|
if (action.action === 'delete') { |
||||
|
this.deleteEntity(action.event, action.entity); |
||||
|
} |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
private deleteEntity($event: Event, entity: BaseData<HasId>) { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
this.dialogService.confirm( |
||||
|
this.entitiesTableConfig.deleteEntityTitle(entity), |
||||
|
this.entitiesTableConfig.deleteEntityContent(entity), |
||||
|
this.translate.instant('action.no'), |
||||
|
this.translate.instant('action.yes'), |
||||
|
true |
||||
|
).subscribe((result) => { |
||||
|
if (result) { |
||||
|
this.entitiesTableConfig.deleteEntity(entity.id).subscribe( |
||||
|
() => { |
||||
|
this.router.navigate(['../'], {relativeTo: this.route}); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -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