Browse Source

Add Assets page

pull/1954/head
Igor Kulikov 7 years ago
parent
commit
e33c9d08cb
  1. 3
      ui-ngx/src/app/modules/home/dialogs/add-entities-to-customer-dialog.component.ts
  2. 3
      ui-ngx/src/app/modules/home/dialogs/assign-to-customer-dialog.component.ts
  3. 50
      ui-ngx/src/app/modules/home/pages/asset/asset-routing.module.ts
  4. 23
      ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.html
  5. 36
      ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.scss
  6. 42
      ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.ts
  7. 88
      ui-ngx/src/app/modules/home/pages/asset/asset.component.html
  8. 19
      ui-ngx/src/app/modules/home/pages/asset/asset.component.scss
  9. 93
      ui-ngx/src/app/modules/home/pages/asset/asset.component.ts
  10. 41
      ui-ngx/src/app/modules/home/pages/asset/asset.module.ts
  11. 420
      ui-ngx/src/app/modules/home/pages/asset/assets-table-config.resolver.ts
  12. 17
      ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts
  13. 4
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  14. 2
      ui-ngx/src/app/modules/home/pages/home-pages.module.ts
  15. 4
      ui-ngx/src/app/shared/models/constants.ts
  16. 40
      ui-ngx/src/app/shared/models/entity-type.models.ts
  17. 10
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
ui-ngx/src/app/modules/home/dialogs/add-entities-to-customer-dialog.component.ts

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

3
ui-ngx/src/app/modules/home/dialogs/assign-to-customer-dialog.component.ts

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

50
ui-ngx/src/app/modules/home/pages/asset/asset-routing.module.ts

@ -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 { }

23
ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.html

@ -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>

36
ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.scss

@ -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;
}
}
}
}

42
ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.ts

@ -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();
}
}

88
ui-ngx/src/app/modules/home/pages/asset/asset.component.html

@ -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>

19
ui-ngx/src/app/modules/home/pages/asset/asset.component.scss

@ -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 {
}

93
ui-ngx/src/app/modules/home/pages/asset/asset.component.ts

@ -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'
}));
}
}

41
ui-ngx/src/app/modules/home/pages/asset/asset.module.ts

@ -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 { }

420
ui-ngx/src/app/modules/home/pages/asset/assets-table-config.resolver.ts

@ -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;
}
}

17
ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts

@ -22,6 +22,7 @@ import {Authority} from '@shared/models/authority.enum';
import {UsersTableConfigResolver} from '../user/users-table-config.resolver'; import {UsersTableConfigResolver} from '../user/users-table-config.resolver';
import {CustomersTableConfigResolver} from './customers-table-config.resolver'; import {CustomersTableConfigResolver} from './customers-table-config.resolver';
import {DevicesTableConfigResolver} from '@modules/home/pages/device/devices-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 = [ const routes: Routes = [
{ {
@ -74,6 +75,22 @@ const routes: Routes = [
resolve: { resolve: {
entitiesTableConfig: DevicesTableConfigResolver 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
}
} }
] ]
} }

4
ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts

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

2
ui-ngx/src/app/modules/home/pages/home-pages.module.ts

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

4
ui-ngx/src/app/shared/models/constants.ts

@ -60,7 +60,9 @@ export const HelpLinks = {
tenants: helpBaseUrl + '/docs/user-guide/ui/tenants', tenants: helpBaseUrl + '/docs/user-guide/ui/tenants',
customers: helpBaseUrl + '/docs/user-guide/customers', customers: helpBaseUrl + '/docs/user-guide/customers',
users: helpBaseUrl + '/docs/user-guide/ui/users', 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'
} }
}; };

40
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -108,6 +108,34 @@ export const entityTypeTranslations = new Map<EntityType, EntityTypeTranslation>
search: 'device.search', search: 'device.search',
selectedEntities: 'device.selected-devices' 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' helpLinkId: 'devices'
} }
],
[
EntityType.ASSET,
{
helpLinkId: 'assets'
}
],
[
EntityType.ENTITY_VIEW,
{
helpLinkId: 'entityViews'
}
] ]
] ]
); );

10
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -228,6 +228,7 @@
"no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.", "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
"asset-type-list-empty": "No asset types selected.", "asset-type-list-empty": "No asset types selected.",
"asset-types": "Asset types", "asset-types": "Asset types",
"created-time": "Created time",
"name": "Name", "name": "Name",
"name-required": "Name is required.", "name-required": "Name is required.",
"description": "Description", "description": "Description",
@ -264,7 +265,9 @@
"asset-required": "Asset is required", "asset-required": "Asset is required",
"name-starts-with": "Asset name starts with", "name-starts-with": "Asset name starts with",
"import": "Import assets", "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": { "attribute": {
"attributes": "Attributes", "attributes": "Attributes",
@ -867,6 +870,7 @@
"no-entity-view-types-matching": "No entity view types matching '{{entitySubtype}}' were found.", "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-type-list-empty": "No entity view types selected.",
"entity-view-types": "Entity View types", "entity-view-types": "Entity View types",
"created-time": "Created time",
"name": "Name", "name": "Name",
"name-required": "Name is required.", "name-required": "Name is required.",
"description": "Description", "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-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-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-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": {
"event-type": "Event type", "event-type": "Event type",

Loading…
Cancel
Save