Add Assets page

This commit is contained in:
Igor Kulikov 2019-08-16 21:22:54 +03:00
parent bd8a104b9f
commit e33c9d08cb
17 changed files with 888 additions and 7 deletions

View File

@ -71,7 +71,8 @@ export class AddEntitiesToCustomerDialogComponent extends PageComponent implemen
this.assignToCustomerText = 'device.assign-device-to-customer-text';
break;
case EntityType.ASSET:
// TODO:
this.assignToCustomerTitle = 'asset.assign-asset-to-customer';
this.assignToCustomerText = 'asset.assign-asset-to-customer-text';
break;
case EntityType.ENTITY_VIEW:
// TODO:

View File

@ -70,7 +70,8 @@ export class AssignToCustomerDialogComponent extends PageComponent implements On
this.assignToCustomerText = 'device.assign-to-customer-text';
break;
case EntityType.ASSET:
// TODO:
this.assignToCustomerTitle = 'asset.assign-asset-to-customer';
this.assignToCustomerText = 'asset.assign-to-customer-text';
break;
case EntityType.ENTITY_VIEW:
// TODO:

View File

@ -0,0 +1,50 @@
///
/// Copyright © 2016-2019 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 {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {EntitiesTableComponent} from '@shared/components/entity/entities-table.component';
import {Authority} from '@shared/models/authority.enum';
import {AssetsTableConfigResolver} from './assets-table-config.resolver';
const routes: Routes = [
{
path: 'assets',
component: EntitiesTableComponent,
data: {
auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
title: 'asset.assets',
assetsType: 'tenant',
breadcrumb: {
label: 'asset.assets',
icon: 'domain'
}
},
resolve: {
entitiesTableConfig: AssetsTableConfigResolver
}
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [
AssetsTableConfigResolver
]
})
export class AssetRoutingModule { }

View File

@ -0,0 +1,23 @@
<!--
Copyright © 2016-2019 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.
-->
<tb-entity-subtype-select
[showLabel]="true"
[entityType]="entityType.ASSET"
[ngModel]="entitiesTableConfig.componentsData.assetType"
(ngModelChange)="assetTypeChanged($event)">
</tb-entity-subtype-select>

View File

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2019 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 {
flex: 1;
display: flex;
justify-content: flex-start;
}
:host ::ng-deep {
tb-entity-subtype-select {
mat-form-field {
font-size: 16px;
.mat-form-field-wrapper {
padding-bottom: 0;
}
.mat-form-field-underline {
bottom: 0;
}
}
}
}

View File

@ -0,0 +1,42 @@
///
/// Copyright © 2016-2019 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 {Component} from '@angular/core';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {EntityTableHeaderComponent} from '@shared/components/entity/entity-table-header.component';
import {EntityType} from '@shared/models/entity-type.models';
import {AssetInfo} from '@shared/models/asset.models';
@Component({
selector: 'tb-asset-table-header',
templateUrl: './asset-table-header.component.html',
styleUrls: ['./asset-table-header.component.scss']
})
export class AssetTableHeaderComponent extends EntityTableHeaderComponent<AssetInfo> {
entityType = EntityType;
constructor(protected store: Store<AppState>) {
super(store);
}
assetTypeChanged(assetType: string) {
this.entitiesTableConfig.componentsData.assetType = assetType;
this.entitiesTableConfig.table.updateData();
}
}

View File

@ -0,0 +1,88 @@
<!--
Copyright © 2016-2019 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-details-buttons">
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'makePublic')"
[fxShow]="!isEdit && assetScope === 'tenant' && !isAssignedToCustomer(entity) && !entity?.customerIsPublic">
{{'asset.make-public' | translate }}
</button>
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'assignToCustomer')"
[fxShow]="!isEdit && assetScope === 'tenant' && !isAssignedToCustomer(entity)">
{{'asset.assign-to-customer' | translate }}
</button>
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'unassignFromCustomer')"
[fxShow]="!isEdit && (assetScope === 'customer' || assetScope === 'tenant') && isAssignedToCustomer(entity)">
{{ (entity?.customerIsPublic ? 'asset.make-private' : 'asset.unassign-from-customer') | translate }}
</button>
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')"
[fxShow]="!hideDelete() && !isEdit">
{{'asset.delete' | translate }}
</button>
<div fxLayout="row">
<button mat-raised-button
ngxClipboard
(cbOnSuccess)="onAssetIdCopied($event)"
[cbContent]="entity?.id?.id"
[fxShow]="!isEdit">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span translate>asset.copyId</span>
</button>
</div>
</div>
<div class="mat-padding" fxLayout="column">
<mat-form-field class="mat-block"
[fxShow]="!isEdit && isAssignedToCustomer(entity)
&& !entity?.customerIsPublic && assetScope === 'tenant'">
<mat-label translate>asset.assignedToCustomer</mat-label>
<input matInput disabled [ngModel]="entity?.customerTitle">
</mat-form-field>
<div class="tb-small" style="padding-bottom: 10px; padding-left: 2px;"
[fxShow]="!isEdit && entity?.customerIsPublic && (assetScope === 'customer' || assetScope === 'tenant')">
{{ 'asset.asset-public' | translate }}
</div>
<form #entityNgForm="ngForm" [formGroup]="entityForm">
<fieldset [disabled]="(isLoading$ | async) || !isEdit">
<mat-form-field class="mat-block">
<mat-label translate>asset.name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'asset.name-required' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-subtype-autocomplete
formControlName="type"
[required]="true"
[entityType]="entityType.ASSET"
>
</tb-entity-subtype-autocomplete>
<div formGroupName="additionalInfo">
<mat-form-field class="mat-block">
<mat-label translate>asset.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>
</mat-form-field>
</div>
</fieldset>
</form>
</div>

View File

@ -0,0 +1,19 @@
/**
* Copyright © 2016-2019 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 {
}

View File

@ -0,0 +1,93 @@
///
/// Copyright © 2016-2019 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 {Component} from '@angular/core';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {EntityComponent} from '@shared/components/entity/entity.component';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {EntityType} from '@shared/models/entity-type.models';
import {NULL_UUID} from '@shared/models/id/has-uuid';
import {ActionNotificationShow} from '@core/notification/notification.actions';
import {TranslateService} from '@ngx-translate/core';
import {AssetInfo} from '@app/shared/models/asset.models';
@Component({
selector: 'tb-asset',
templateUrl: './asset.component.html',
styleUrls: ['./asset.component.scss']
})
export class AssetComponent extends EntityComponent<AssetInfo> {
entityType = EntityType;
assetScope: 'tenant' | 'customer' | 'customer_user';
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
public fb: FormBuilder) {
super(store);
}
ngOnInit() {
this.assetScope = this.entitiesTableConfig.componentsData.assetScope;
super.ngOnInit();
}
hideDelete() {
if (this.entitiesTableConfig) {
return !this.entitiesTableConfig.deleteEnabled(this.entity);
} else {
return false;
}
}
isAssignedToCustomer(entity: AssetInfo): boolean {
return entity && entity.customerId && entity.customerId.id !== NULL_UUID;
}
buildForm(entity: AssetInfo): FormGroup {
return this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
type: [entity ? entity.type : null, [Validators.required]],
additionalInfo: this.fb.group(
{
description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''],
}
)
}
);
}
updateForm(entity: AssetInfo) {
this.entityForm.patchValue({name: entity.name});
this.entityForm.patchValue({type: entity.type});
this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}});
}
onAssetIdCopied($event) {
this.store.dispatch(new ActionNotificationShow(
{
message: this.translate.instant('asset.idCopiedMessage'),
type: 'success',
duration: 750,
verticalPosition: 'bottom',
horizontalPosition: 'right'
}));
}
}

View File

@ -0,0 +1,41 @@
///
/// Copyright © 2016-2019 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 {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {SharedModule} from '@shared/shared.module';
import {HomeDialogsModule} from '../../dialogs/home-dialogs.module';
import {AssetComponent} from './asset.component';
import {AssetTableHeaderComponent} from './asset-table-header.component';
import {AssetRoutingModule} from './asset-routing.module';
@NgModule({
entryComponents: [
AssetComponent,
AssetTableHeaderComponent
],
declarations: [
AssetComponent,
AssetTableHeaderComponent
],
imports: [
CommonModule,
SharedModule,
HomeDialogsModule,
AssetRoutingModule
]
})
export class AssetModule { }

View File

@ -0,0 +1,420 @@
///
/// Copyright © 2016-2019 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 {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, Resolve, Router} from '@angular/router';
import {
CellActionDescriptor,
checkBoxCell,
DateEntityTableColumn,
EntityTableColumn,
EntityTableConfig,
GroupActionDescriptor,
HeaderActionDescriptor
} from '@shared/components/entity/entities-table-config.models';
import {TranslateService} from '@ngx-translate/core';
import {DatePipe} from '@angular/common';
import {EntityType, entityTypeResources, entityTypeTranslations} from '@shared/models/entity-type.models';
import {EntityAction} from '@shared/components/entity/entity-component.models';
import {forkJoin, Observable, of} from 'rxjs';
import {select, Store} from '@ngrx/store';
import {selectAuthUser} from '@core/auth/auth.selectors';
import {map, mergeMap, take, tap} from 'rxjs/operators';
import {AppState} from '@core/core.state';
import {Authority} from '@app/shared/models/authority.enum';
import {CustomerService} from '@core/http/customer.service';
import {Customer} from '@app/shared/models/customer.model';
import {NULL_UUID} from '@shared/models/id/has-uuid';
import {BroadcastService} from '@core/services/broadcast.service';
import {MatDialog} from '@angular/material';
import {DialogService} from '@core/services/dialog.service';
import {
AssignToCustomerDialogComponent,
AssignToCustomerDialogData
} from '@modules/home/dialogs/assign-to-customer-dialog.component';
import {
AddEntitiesToCustomerDialogComponent,
AddEntitiesToCustomerDialogData
} from '../../dialogs/add-entities-to-customer-dialog.component';
import {Asset, AssetInfo} from '@app/shared/models/asset.models';
import {AssetService} from '@app/core/http/asset.service';
import {AssetComponent} from '@modules/home/pages/asset/asset.component';
import {AssetTableHeaderComponent} from '@modules/home/pages/asset/asset-table-header.component';
import {AssetId} from '@app/shared/models/id/asset-id';
@Injectable()
export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<AssetInfo>> {
private readonly config: EntityTableConfig<AssetInfo> = new EntityTableConfig<AssetInfo>();
private customerId: string;
constructor(private store: Store<AppState>,
private broadcast: BroadcastService,
private assetService: AssetService,
private customerService: CustomerService,
private dialogService: DialogService,
private translate: TranslateService,
private datePipe: DatePipe,
private router: Router,
private dialog: MatDialog) {
this.config.entityType = EntityType.ASSET;
this.config.entityComponent = AssetComponent;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.ASSET);
this.config.entityResources = entityTypeResources.get(EntityType.ASSET);
this.config.deleteEntityTitle = asset => this.translate.instant('asset.delete-asset-title', { assetName: asset.name });
this.config.deleteEntityContent = () => this.translate.instant('asset.delete-asset-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('asset.delete-assets-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('asset.delete-assets-text');
this.config.loadEntity = id => this.assetService.getAssetInfo(id.id);
this.config.saveEntity = asset => {
return this.assetService.saveAsset(asset).pipe(
tap(() => {
this.broadcast.broadcast('assetSaved');
}),
mergeMap((savedAsset) => this.assetService.getAssetInfo(savedAsset.id.id)
));
};
this.config.onEntityAction = action => this.onAssetAction(action);
this.config.headerComponent = AssetTableHeaderComponent;
}
resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<AssetInfo>> {
const routeParams = route.params;
this.config.componentsData = {
assetScope: route.data.assetsType,
assetType: ''
};
this.customerId = routeParams.customerId;
return this.store.pipe(select(selectAuthUser), take(1)).pipe(
tap((authUser) => {
if (authUser.authority === Authority.CUSTOMER_USER) {
this.config.componentsData.assetScope = 'customer_user';
this.customerId = authUser.customerId;
}
}),
mergeMap(() =>
this.customerId ? this.customerService.getCustomer(this.customerId) : of(null as Customer)
),
map((parentCustomer) => {
if (parentCustomer) {
if (parentCustomer.additionalInfo && parentCustomer.additionalInfo.isPublic) {
this.config.tableTitle = this.translate.instant('customer.public-assets');
} else {
this.config.tableTitle = parentCustomer.title + ': ' + this.translate.instant('asset.assets');
}
} else {
this.config.tableTitle = this.translate.instant('asset.assets');
}
this.config.columns = this.configureColumns(this.config.componentsData.assetScope);
this.configureEntityFunctions(this.config.componentsData.assetScope);
this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.assetScope);
this.config.groupActionDescriptors = this.configureGroupActions(this.config.componentsData.assetScope);
this.config.addActionDescriptors = this.configureAddActions(this.config.componentsData.assetScope);
this.config.addEnabled = this.config.componentsData.assetScope !== 'customer_user';
this.config.entitiesDeleteEnabled = this.config.componentsData.assetScope === 'tenant';
this.config.deleteEnabled = () => this.config.componentsData.assetScope === 'tenant';
return this.config;
})
);
}
configureColumns(assetScope: string): Array<EntityTableColumn<AssetInfo>> {
const columns: Array<EntityTableColumn<AssetInfo>> = [
new DateEntityTableColumn<AssetInfo>('createdTime', 'asset.created-time', this.datePipe, '150px'),
new EntityTableColumn<AssetInfo>('name', 'asset.name'),
new EntityTableColumn<AssetInfo>('type', 'asset.asset-type'),
];
if (assetScope === 'tenant') {
columns.push(
new EntityTableColumn<AssetInfo>('customerTitle', 'customer.customer'),
new EntityTableColumn<AssetInfo>('customerIsPublic', 'asset.public', '60px',
entity => {
return checkBoxCell(entity.customerIsPublic);
}, () => ({}), false),
);
}
return columns;
}
configureEntityFunctions(assetScope: string): void {
if (assetScope === 'tenant') {
this.config.entitiesFetchFunction = pageLink =>
this.assetService.getTenantAssetInfos(pageLink, this.config.componentsData.assetType);
this.config.deleteEntity = id => this.assetService.deleteAsset(id.id);
} else {
this.config.entitiesFetchFunction = pageLink =>
this.assetService.getCustomerAssetInfos(this.customerId, pageLink, this.config.componentsData.assetType);
this.config.deleteEntity = id => this.assetService.unassignAssetFromCustomer(id.id);
}
}
configureCellActions(assetScope: string): Array<CellActionDescriptor<AssetInfo>> {
const actions: Array<CellActionDescriptor<AssetInfo>> = [];
if (assetScope === 'tenant') {
actions.push(
{
name: this.translate.instant('asset.make-public'),
icon: 'share',
isEnabled: (entity) => (!entity.customerId || entity.customerId.id === NULL_UUID),
onAction: ($event, entity) => this.makePublic($event, entity)
},
{
name: this.translate.instant('asset.assign-to-customer'),
icon: 'assignment_ind',
isEnabled: (entity) => (!entity.customerId || entity.customerId.id === NULL_UUID),
onAction: ($event, entity) => this.assignToCustomer($event, [entity.id])
},
{
name: this.translate.instant('asset.unassign-from-customer'),
icon: 'assignment_return',
isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && !entity.customerIsPublic),
onAction: ($event, entity) => this.unassignFromCustomer($event, entity)
},
{
name: this.translate.instant('asset.make-private'),
icon: 'reply',
isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && entity.customerIsPublic),
onAction: ($event, entity) => this.unassignFromCustomer($event, entity)
}
);
}
if (assetScope === 'customer') {
actions.push(
{
name: this.translate.instant('asset.unassign-from-customer'),
icon: 'assignment_return',
isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && !entity.customerIsPublic),
onAction: ($event, entity) => this.unassignFromCustomer($event, entity)
},
{
name: this.translate.instant('asset.make-private'),
icon: 'reply',
isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && entity.customerIsPublic),
onAction: ($event, entity) => this.unassignFromCustomer($event, entity)
}
);
}
return actions;
}
configureGroupActions(assetScope: string): Array<GroupActionDescriptor<AssetInfo>> {
const actions: Array<GroupActionDescriptor<AssetInfo>> = [];
if (assetScope === 'tenant') {
actions.push(
{
name: this.translate.instant('asset.assign-assets'),
icon: 'assignment_ind',
isEnabled: true,
onAction: ($event, entities) => this.assignToCustomer($event, entities.map((entity) => entity.id))
}
);
}
if (assetScope === 'customer') {
actions.push(
{
name: this.translate.instant('asset.unassign-assets'),
icon: 'assignment_return',
isEnabled: true,
onAction: ($event, entities) => this.unassignAssetsFromCustomer($event, entities)
}
);
}
return actions;
}
configureAddActions(assetScope: string): Array<HeaderActionDescriptor> {
const actions: Array<HeaderActionDescriptor> = [];
if (assetScope === 'tenant') {
actions.push(
{
name: this.translate.instant('asset.add-asset-text'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
},
{
name: this.translate.instant('asset.import'),
icon: 'file_upload',
isEnabled: () => true,
onAction: ($event) => this.importAssets($event)
}
);
}
if (assetScope === 'customer') {
actions.push(
{
name: this.translate.instant('asset.assign-new-asset'),
icon: 'add',
isEnabled: () => true,
onAction: ($event) => this.addAssetsToCustomer($event)
}
);
}
return actions;
}
importAssets($event: Event) {
if ($event) {
$event.stopPropagation();
}
// TODO:
}
addAssetsToCustomer($event: Event) {
if ($event) {
$event.stopPropagation();
}
this.dialog.open<AddEntitiesToCustomerDialogComponent, AddEntitiesToCustomerDialogData,
boolean>(AddEntitiesToCustomerDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
customerId: this.customerId,
entityType: EntityType.ASSET
}
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
}
});
}
makePublic($event: Event, asset: Asset) {
if ($event) {
$event.stopPropagation();
}
this.dialogService.confirm(
this.translate.instant('asset.make-public-asset-title', {assetName: asset.name}),
this.translate.instant('asset.make-public-asset-text'),
this.translate.instant('action.no'),
this.translate.instant('action.yes'),
true
).subscribe((res) => {
if (res) {
this.assetService.makeAssetPublic(asset.id.id).subscribe(
() => {
this.config.table.updateData();
}
);
}
}
);
}
assignToCustomer($event: Event, assetIds: Array<AssetId>) {
if ($event) {
$event.stopPropagation();
}
this.dialog.open<AssignToCustomerDialogComponent, AssignToCustomerDialogData,
boolean>(AssignToCustomerDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
entityIds: assetIds,
entityType: EntityType.ASSET
}
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
}
});
}
unassignFromCustomer($event: Event, asset: AssetInfo) {
if ($event) {
$event.stopPropagation();
}
const isPublic = asset.customerIsPublic;
let title;
let content;
if (isPublic) {
title = this.translate.instant('asset.make-private-asset-title', {assetName: asset.name});
content = this.translate.instant('asset.make-private-asset-text');
} else {
title = this.translate.instant('asset.unassign-asset-title', {assetName: asset.name});
content = this.translate.instant('asset.unassign-asset-text');
}
this.dialogService.confirm(
title,
content,
this.translate.instant('action.no'),
this.translate.instant('action.yes'),
true
).subscribe((res) => {
if (res) {
this.assetService.unassignAssetFromCustomer(asset.id.id).subscribe(
() => {
this.config.table.updateData();
}
);
}
}
);
}
unassignAssetsFromCustomer($event: Event, assets: Array<AssetInfo>) {
if ($event) {
$event.stopPropagation();
}
this.dialogService.confirm(
this.translate.instant('asset.unassign-assets-title', {count: assets.length}),
this.translate.instant('asset.unassign-assets-text'),
this.translate.instant('action.no'),
this.translate.instant('action.yes'),
true
).subscribe((res) => {
if (res) {
const tasks: Observable<any>[] = [];
assets.forEach(
(asset) => {
tasks.push(this.assetService.unassignAssetFromCustomer(asset.id.id));
}
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
}
);
}
}
);
}
onAssetAction(action: EntityAction<AssetInfo>): boolean {
switch (action.action) {
case 'makePublic':
this.makePublic(action.event, action.entity);
return true;
case 'assignToCustomer':
this.assignToCustomer(action.event, [action.entity.id]);
return true;
case 'unassignFromCustomer':
this.unassignFromCustomer(action.event, action.entity);
return true;
}
return false;
}
}

View File

@ -22,6 +22,7 @@ import {Authority} from '@shared/models/authority.enum';
import {UsersTableConfigResolver} from '../user/users-table-config.resolver';
import {CustomersTableConfigResolver} from './customers-table-config.resolver';
import {DevicesTableConfigResolver} from '@modules/home/pages/device/devices-table-config.resolver';
import {AssetsTableConfigResolver} from '../asset/assets-table-config.resolver';
const routes: Routes = [
{
@ -74,6 +75,22 @@ const routes: Routes = [
resolve: {
entitiesTableConfig: DevicesTableConfigResolver
}
},
{
path: ':customerId/assets',
component: EntitiesTableComponent,
data: {
auth: [Authority.TENANT_ADMIN],
title: 'customer.assets',
assetsType: 'customer',
breadcrumb: {
label: 'customer.assets',
icon: 'domain'
}
},
resolve: {
entitiesTableConfig: AssetsTableConfigResolver
}
}
]
}

View File

@ -141,8 +141,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
);
}
configureColumns(deviceScope: string): Array<EntityTableColumn<Device | DeviceInfo>> {
const columns: Array<EntityTableColumn<Device | DeviceInfo>> = [
configureColumns(deviceScope: string): Array<EntityTableColumn<DeviceInfo>> {
const columns: Array<EntityTableColumn<DeviceInfo>> = [
new DateEntityTableColumn<DeviceInfo>('createdTime', 'device.created-time', this.datePipe, '150px'),
new EntityTableColumn<DeviceInfo>('name', 'device.name'),
new EntityTableColumn<DeviceInfo>('type', 'device.device-type'),

View File

@ -24,6 +24,7 @@ import { CustomerModule } from '@modules/home/pages/customer/customer.module';
// import { AuditLogModule } from '@modules/home/pages/audit-log/audit-log.module';
import { UserModule } from '@modules/home/pages/user/user.module';
import {DeviceModule} from '@modules/home/pages/device/device.module';
import {AssetModule} from '@modules/home/pages/asset/asset.module';
@NgModule({
exports: [
@ -32,6 +33,7 @@ import {DeviceModule} from '@modules/home/pages/device/device.module';
ProfileModule,
TenantModule,
DeviceModule,
AssetModule,
CustomerModule,
// AuditLogModule,
UserModule

View File

@ -60,7 +60,9 @@ export const HelpLinks = {
tenants: helpBaseUrl + '/docs/user-guide/ui/tenants',
customers: helpBaseUrl + '/docs/user-guide/customers',
users: helpBaseUrl + '/docs/user-guide/ui/users',
devices: helpBaseUrl + '/docs/user-guide/ui/devices'
devices: helpBaseUrl + '/docs/user-guide/ui/devices',
assets: helpBaseUrl + '/docs/user-guide/ui/assets',
entityViews: helpBaseUrl + '/docs/user-guide/ui/entity-views'
}
};

View File

@ -108,6 +108,34 @@ export const entityTypeTranslations = new Map<EntityType, EntityTypeTranslation>
search: 'device.search',
selectedEntities: 'device.selected-devices'
}
],
[
EntityType.ASSET,
{
type: 'entity.type-asset',
typePlural: 'entity.type-assets',
list: 'entity.list-of-assets',
nameStartsWith: 'entity.asset-name-starts-with',
details: 'asset.asset-details',
add: 'asset.add',
noEntities: 'asset.no-assets-text',
search: 'asset.search',
selectedEntities: 'asset.selected-assets'
}
],
[
EntityType.ENTITY_VIEW,
{
type: 'entity.type-entity-view',
typePlural: 'entity.type-entity-views',
list: 'entity.list-of-entity-views',
nameStartsWith: 'entity.entity-view-name-starts-with',
details: 'entity-view.entity-view-details',
add: 'entity-view.add',
noEntities: 'entity-view.no-entity-views-text',
search: 'entity-view.search',
selectedEntities: 'entity-view.selected-entity-views'
}
]
]
);
@ -137,6 +165,18 @@ export const entityTypeResources = new Map<EntityType, EntityTypeResource>(
{
helpLinkId: 'devices'
}
],
[
EntityType.ASSET,
{
helpLinkId: 'assets'
}
],
[
EntityType.ENTITY_VIEW,
{
helpLinkId: 'entityViews'
}
]
]
);

View File

@ -228,6 +228,7 @@
"no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
"asset-type-list-empty": "No asset types selected.",
"asset-types": "Asset types",
"created-time": "Created time",
"name": "Name",
"name-required": "Name is required.",
"description": "Description",
@ -264,7 +265,9 @@
"asset-required": "Asset is required",
"name-starts-with": "Asset name starts with",
"import": "Import assets",
"asset-file": "Asset file"
"asset-file": "Asset file",
"search": "Search assets",
"selected-assets": "{ count, plural, 1 {1 asset} other {# assets} } selected"
},
"attribute": {
"attributes": "Attributes",
@ -867,6 +870,7 @@
"no-entity-view-types-matching": "No entity view types matching '{{entitySubtype}}' were found.",
"entity-view-type-list-empty": "No entity view types selected.",
"entity-view-types": "Entity View types",
"created-time": "Created time",
"name": "Name",
"name-required": "Name is required.",
"description": "Description",
@ -900,7 +904,9 @@
"make-public-entity-view-title": "Are you sure you want to make the entity view '{{entityViewName}}' public?",
"make-public-entity-view-text": "After the confirmation the entity view and all its data will be made public and accessible by others.",
"make-private-entity-view-title": "Are you sure you want to make the entity view '{{entityViewName}}' private?",
"make-private-entity-view-text": "After the confirmation the entity view and all its data will be made private and won't be accessible by others."
"make-private-entity-view-text": "After the confirmation the entity view and all its data will be made private and won't be accessible by others.",
"search": "Search entity views",
"selected-entity-views": "{ count, plural, 1 {1 entity view} other {# entity views} } selected"
},
"event": {
"event-type": "Event type",