From fea3c368a360c8314290b20c405d008471241f72 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 22 Aug 2019 18:44:48 +0300 Subject: [PATCH] Implement Events table --- .../server/controller/EventController.java | 20 +- .../server/dao/sql/event/EventRepository.java | 36 ++- .../server/dao/sql/event/JpaBaseEventDao.java | 37 +-- ui-ngx/src/app/core/http/event.service.ts | 41 ++++ .../entity/entities-table.component.html | 32 ++- .../entity/entities-table.component.ts | 80 +++++-- .../entity/entity-tabs.component.ts | 8 + .../components/event/event-table-config.ts | 212 ++++++++++++++++++ .../event/event-table-header.component.html | 27 +++ .../event/event-table-header.component.scss | 35 +++ .../event/event-table-header.component.ts | 45 ++++ .../event/event-table.component.html | 18 ++ .../event/event-table.component.scss | 22 ++ .../components/event/event-table.component.ts | 112 +++++++++ .../home/components/home-components.module.ts | 12 +- .../models/datasource/entity-datasource.ts | 7 + .../entity/entities-table-config.models.ts | 31 ++- .../pages/asset/asset-tabs.component.html | 5 + .../customer/customer-tabs.component.html | 5 + .../pages/device/device-tabs.component.html | 5 + .../entity-view-tabs.component.html | 5 + .../rulechain/rulechain-tabs.component.html | 8 + .../pages/tenant/tenant-tabs.component.html | 22 ++ .../pages/tenant/tenant-tabs.component.ts | 38 ++++ .../home/pages/tenant/tenant.module.ts | 7 +- .../tenant/tenants-table-config.resolver.ts | 2 + .../home/pages/user/user-tabs.component.html | 21 ++ .../home/pages/user/user-tabs.component.ts | 38 ++++ .../modules/home/pages/user/user.module.ts | 3 + .../pages/user/users-table-config.resolver.ts | 2 + ui-ngx/src/app/shared/models/event.models.ts | 89 ++++++++ ui-ngx/src/app/shared/models/id/event-id.ts | 24 ++ .../src/app/shared/models/rule-node.models.ts | 6 + ui-ngx/src/theme.scss | 2 +- 34 files changed, 990 insertions(+), 67 deletions(-) create mode 100644 ui-ngx/src/app/core/http/event.service.ts create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table-config.ts create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table-header.component.html create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table-header.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table-header.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table.component.html create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/event/event-table.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/user/user-tabs.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/user/user-tabs.component.ts create mode 100644 ui-ngx/src/app/shared/models/event.models.ts create mode 100644 ui-ngx/src/app/shared/models/id/event-id.ts diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index 0754b97a5c..803b9a294a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -53,10 +53,11 @@ public class EventController extends BaseController { @RequestParam("tenantId") String strTenantId, @RequestParam int pageSize, @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, @RequestParam(required = false) Long startTime, - @RequestParam(required = false) Long endTime, - @RequestParam(required = false, defaultValue = "false") boolean ascOrder - ) throws ThingsboardException { + @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); try { @@ -64,8 +65,7 @@ public class EventController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); checkEntityId(entityId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, "", - "createdTime", ascOrder ? "asc" : "desc", startTime, endTime); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); return checkNotNull(eventService.findEvents(tenantId, entityId, eventType, pageLink)); } catch (Exception e) { throw handleException(e); @@ -81,10 +81,11 @@ public class EventController extends BaseController { @RequestParam("tenantId") String strTenantId, @RequestParam int pageSize, @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, @RequestParam(required = false) Long startTime, - @RequestParam(required = false) Long endTime, - @RequestParam(required = false, defaultValue = "false") boolean ascOrder - ) throws ThingsboardException { + @RequestParam(required = false) Long endTime) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); try { @@ -93,8 +94,7 @@ public class EventController extends BaseController { EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); checkEntityId(entityId, Operation.READ); - TimePageLink pageLink = createTimePageLink(pageSize, page, "", - "createdTime", ascOrder ? "asc" : "desc", startTime, endTime); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); return checkNotNull(eventService.findEvents(tenantId, entityId, pageLink)); } catch (Exception e) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java index f55501fecc..82c1b96112 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.dao.sql.event; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.dao.model.sql.EventEntity; @@ -30,7 +30,7 @@ import java.util.List; * Created by Valerii Sosliuk on 5/3/2017. */ @SqlDao -public interface EventRepository extends CrudRepository, JpaSpecificationExecutor { +public interface EventRepository extends PagingAndSortingRepository { EventEntity findByTenantIdAndEntityTypeAndEntityIdAndEventTypeAndEventUid(String tenantId, EntityType entityType, @@ -51,4 +51,34 @@ public interface EventRepository extends CrudRepository, Jp @Param("eventType") String eventType, Pageable pageable); + @Query("SELECT e FROM EventEntity e WHERE " + + "e.tenantId = :tenantId " + + "AND e.entityType = :entityType AND e.entityId = :entityId " + + "AND (:startId IS NULL OR e.id >= :startId) " + + "AND (:endId IS NULL OR e.id <= :endId) " + + "AND LOWER(e.eventType) LIKE LOWER(CONCAT(:textSearch, '%'))" + ) + Page findEventsByTenantIdAndEntityId(@Param("tenantId") String tenantId, + @Param("entityType") EntityType entityType, + @Param("entityId") String entityId, + @Param("textSearch") String textSearch, + @Param("startId") String startId, + @Param("endId") String endId, + Pageable pageable); + + @Query("SELECT e FROM EventEntity e WHERE " + + "e.tenantId = :tenantId " + + "AND e.entityType = :entityType AND e.entityId = :entityId " + + "AND e.eventType = :eventType " + + "AND (:startId IS NULL OR e.id >= :startId) " + + "AND (:endId IS NULL OR e.id <= :endId)" + ) + Page findEventsByTenantIdAndEntityIdAndEventType(@Param("tenantId") String tenantId, + @Param("entityType") EntityType entityType, + @Param("entityId") String entityId, + @Param("eventType") String eventType, + @Param("startId") String startId, + @Param("endId") String endId, + Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java index 9c7e94243d..1421a5cbec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java @@ -21,8 +21,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; @@ -40,13 +38,11 @@ import org.thingsboard.server.dao.sql.JpaAbstractSearchTimeDao; import org.thingsboard.server.dao.util.SqlDao; import javax.persistence.criteria.Predicate; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; -import static org.springframework.data.jpa.domain.Specifications.where; -import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; +import static org.thingsboard.server.dao.DaoUtil.endTimeToId; +import static org.thingsboard.server.dao.DaoUtil.startTimeToId; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; /** @@ -109,15 +105,30 @@ public class JpaBaseEventDao extends JpaAbstractSearchTimeDao findEvents(UUID tenantId, EntityId entityId, TimePageLink pageLink) { - return findEvents(tenantId, entityId, null, pageLink); + return DaoUtil.toPageData( + eventRepository + .findEventsByTenantIdAndEntityId( + fromTimeUUID(tenantId), + entityId.getEntityType(), + fromTimeUUID(entityId.getId()), + Objects.toString(pageLink.getTextSearch(), ""), + startTimeToId(pageLink.getStartTime()), + endTimeToId(pageLink.getEndTime()), + DaoUtil.toPageable(pageLink))); } @Override public PageData findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink) { - Specification timeSearchSpec = JpaAbstractSearchTimeDao.getTimeSearchPageSpec(pageLink, "id"); - Specification fieldsSpec = getEntityFieldsSpec(tenantId, entityId, eventType); - Pageable pageable = DaoUtil.toPageable(pageLink); - return DaoUtil.toPageData(eventRepository.findAll(where(timeSearchSpec).and(fieldsSpec), pageable)); + return DaoUtil.toPageData( + eventRepository + .findEventsByTenantIdAndEntityIdAndEventType( + fromTimeUUID(tenantId), + entityId.getEntityType(), + fromTimeUUID(entityId.getId()), + eventType, + startTimeToId(pageLink.getStartTime()), + endTimeToId(pageLink.getEndTime()), + DaoUtil.toPageable(pageLink))); } @Override diff --git a/ui-ngx/src/app/core/http/event.service.ts b/ui-ngx/src/app/core/http/event.service.ts new file mode 100644 index 0000000000..fa1a519546 --- /dev/null +++ b/ui-ngx/src/app/core/http/event.service.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 { Injectable } from '@angular/core'; +import { defaultHttpOptions } from './http-utils'; +import { Observable } from 'rxjs/index'; +import { HttpClient } from '@angular/common/http'; +import { TimePageLink } from '@shared/models/page/page-link'; +import { PageData } from '@shared/models/page/page-data'; +import { EntityId } from '@shared/models/id/entity-id'; +import { DebugEventType, Event, EventType } from '@shared/models/event.models'; + +@Injectable({ + providedIn: 'root' +}) +export class EventService { + + constructor( + private http: HttpClient + ) { } + + public getEvents(entityId: EntityId, eventType: EventType | DebugEventType, tenantId: string, pageLink: TimePageLink, + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable> { + return this.http.get>(`/api/events/${entityId.entityType}/${entityId.id}/${eventType}` + + `${pageLink.toQuery()}&tenantId=${tenantId}`, + defaultHttpOptions(ignoreLoading, ignoreErrors)); + } +} diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index adfe8f00a7..075f34411d 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -37,14 +37,15 @@
{{ entitiesTableConfig.tableTitle }} + - - +
- + diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 9e08976d00..f49fa9afca 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -42,7 +42,8 @@ import { EntityTableColumn, EntityTableConfig, GroupActionDescriptor, - HeaderActionDescriptor + HeaderActionDescriptor, + EntityColumn } from '@home/models/entity/entities-table-config.models'; import { EntityTypeTranslation } from '@shared/models/entity-type.models'; import { DialogService } from '@core/services/dialog.service'; @@ -72,8 +73,10 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn groupActionDescriptors: Array>>; cellActionDescriptors: Array>>; - columns: Array>>; - displayedColumns: string[] = []; + columns: Array>>; + displayedColumns: string[]; + + headerCellStyleCache: Array = []; cellContentCache: Array = []; @@ -143,22 +146,12 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn } ); - this.columns = [...this.entitiesTableConfig.columns]; - const enabledGroupActionDescriptors = this.groupActionDescriptors.filter((descriptor) => descriptor.isEnabled); this.selectionEnabled = this.entitiesTableConfig.selectionEnabled && enabledGroupActionDescriptors.length; - if (this.selectionEnabled) { - this.displayedColumns.push('select'); - } - this.columns.forEach( - (column) => { - this.displayedColumns.push(column.key); - } - ); - this.displayedColumns.push('actions'); + this.columnsUpdated(); const sortOrder: SortOrder = { property: this.entitiesTableConfig.defaultSortOrder.property, direction: this.entitiesTableConfig.defaultSortOrder.direction }; @@ -235,6 +228,7 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn } private dataLoaded() { + this.headerCellStyleCache.length = 0; this.cellContentCache.length = 0; this.cellStyleCache.length = 0; } @@ -365,21 +359,65 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn } } - cellContent(entity: BaseData, column: EntityTableColumn>, row: number, col: number) { - const index = row * this.columns.length + col; - let res = this.cellContentCache[index]; + columnsUpdated(resetData: boolean = false) { + this.columns = [...this.entitiesTableConfig.columns]; + + this.displayedColumns = []; + + if (this.selectionEnabled) { + this.displayedColumns.push('select'); + } + this.columns.forEach( + (column) => { + this.displayedColumns.push(column.key); + } + ); + this.displayedColumns.push('actions'); + this.headerCellStyleCache.length = 0; + this.cellContentCache.length = 0; + this.cellStyleCache.length = 0; + if (resetData) { + this.dataSource.reset(); + } + } + + headerCellStyle(column: EntityColumn>, col: number) { + const index = col; + let res = this.headerCellStyleCache[index]; if (!res) { - res = this.domSanitizer.bypassSecurityTrustHtml(column.cellContentFunction(entity, column.key)); - this.cellContentCache[index] = res; + if (column instanceof EntityTableColumn) { + res = {...column.headerCellStyleFunction(column.key), ...{maxWidth: column.maxWidth}}; + } else { + res = {maxWidth: column.maxWidth}; + } + this.headerCellStyleCache[index] = res; } return res; } - cellStyle(entity: BaseData, column: EntityTableColumn>, row: number, col: number) { + cellContent(entity: BaseData, column: EntityColumn>, row: number, col: number) { + if (column instanceof EntityTableColumn) { + const index = row * this.columns.length + col; + let res = this.cellContentCache[index]; + if (!res) { + res = this.domSanitizer.bypassSecurityTrustHtml(column.cellContentFunction(entity, column.key)); + this.cellContentCache[index] = res; + } + return res; + } else { + return null; + } + } + + cellStyle(entity: BaseData, column: EntityColumn>, row: number, col: number) { const index = row * this.columns.length + col; let res = this.cellStyleCache[index]; if (!res) { - res = {...column.cellStyleFunction(entity, column.key), ...{maxWidth: column.maxWidth}}; + if (column instanceof EntityTableColumn) { + res = {...column.cellStyleFunction(entity, column.key), ...{maxWidth: column.maxWidth}}; + } else { + res = {maxWidth: column.maxWidth}; + } this.cellStyleCache[index] = res; } return res; diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts index a9639e0923..dd8a02c8bc 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts @@ -37,6 +37,8 @@ import { selectAuthUser, getCurrentAuthUser } from '@core/auth/auth.selectors'; import { AuthUser } from '@shared/models/user.model'; import { EntityType } from '@shared/models/entity-type.models'; import { AuditLogMode } from '@shared/models/audit-log.models'; +import { DebugEventType, EventType } from '@shared/models/event.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; export abstract class EntityTabsComponent> extends PageComponent implements OnInit, AfterViewInit { @@ -46,8 +48,14 @@ export abstract class EntityTabsComponent> extends Pag auditLogModes = AuditLogMode; + eventTypes = EventType; + + debugEventTypes = DebugEventType; + authUser: AuthUser; + nullUid = NULL_UUID; + entityValue: T; @ViewChildren(MatTab) entityTabs: QueryList; diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts new file mode 100644 index 0000000000..9e2973a264 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -0,0 +1,212 @@ +/// +/// 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 { + DateEntityTableColumn, + EntityActionTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { DebugEventType, Event, EventType } from '@shared/models/event.models'; +import { TimePageLink } from '@shared/models/page/page-link'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { MatDialog } from '@angular/material/dialog'; +import { EntityId } from '@shared/models/id/entity-id'; +import { EventService } from '@app/core/http/event.service'; +import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component'; +import { EntityTypeResource } from '@shared/models/entity-type.models'; +import { Observable } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { Direction } from '@shared/models/page/sort-order'; +import { MsgDataType } from '@shared/models/rule-node.models'; +import { DialogService } from '@core/services/dialog.service'; + +export class EventTableConfig extends EntityTableConfig { + + eventTypeValue: EventType | DebugEventType; + + set eventType(eventType: EventType | DebugEventType) { + if (this.eventTypeValue !== eventType) { + this.eventTypeValue = eventType; + this.updateColumns(true); + } + } + + get eventType(): EventType | DebugEventType { + return this.eventTypeValue; + } + + eventTypes: Array; + + constructor(private eventService: EventService, + private dialogService: DialogService, + private translate: TranslateService, + private datePipe: DatePipe, + private dialog: MatDialog, + public entityId: EntityId, + public tenantId: string, + private defaultEventType: EventType | DebugEventType, + private disabledEventTypes: Array = null, + private debugEventTypes: Array = null) { + super(); + this.loadDataOnInit = false; + this.tableTitle = ''; + this.useTimePageLink = true; + this.detailsPanelEnabled = false; + this.selectionEnabled = false; + this.searchEnabled = false; + this.addEnabled = false; + this.entitiesDeleteEnabled = false; + + this.headerComponent = EventTableHeaderComponent; + + this.eventTypes = Object.keys(EventType).map(type => EventType[type]); + + if (disabledEventTypes && disabledEventTypes.length) { + this.eventTypes = this.eventTypes.filter(type => disabledEventTypes.indexOf(type) === -1); + } + + if (debugEventTypes && debugEventTypes.length) { + this.eventTypes = [...this.eventTypes, ...debugEventTypes]; + } + + this.eventTypeValue = defaultEventType; + + this.entityTranslations = { + noEntities: 'event.no-events-prompt' + }; + this.entityResources = { + } as EntityTypeResource; + this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink); + + this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; + + this.updateColumns(); + } + + fetchEvents(pageLink: TimePageLink): Observable> { + return this.eventService.getEvents(this.entityId, this.eventType, this.tenantId, pageLink); + } + + updateColumns(updateTableColumns: boolean = false): void { + this.columns = []; + this.columns.push( + new DateEntityTableColumn('createdTime', 'event.event-time', this.datePipe, '150px'), + new EntityTableColumn('server', 'event.server', '150px', + (entity) => entity.body.server, entity => ({}), false)); + switch (this.eventType) { + case EventType.ERROR: + this.columns.push( + new EntityTableColumn('method', 'event.method', '100%', + (entity) => entity.body.method, entity => ({}), false), + new EntityActionTableColumn('error', 'event.error', + { + name: this.translate.instant('action.view'), + icon: 'more_horiz', + isEnabled: (entity) => entity.body.error && entity.body.error.length > 0, + onAction: ($event, entity) => this.showContent($event, entity.body.error, 'event.error') + }, + '100px') + ); + break; + case EventType.LC_EVENT: + this.columns.push( + new EntityTableColumn('method', 'event.event', '100%', + (entity) => entity.body.event, entity => ({}), false), + new EntityTableColumn('status', 'event.status', '100%', + (entity) => + this.translate.instant(entity.body.success ? 'event.success' : 'event.failed'), entity => ({}), false), + new EntityActionTableColumn('error', 'event.error', + { + name: this.translate.instant('action.view'), + icon: 'more_horiz', + isEnabled: (entity) => entity.body.error && entity.body.error.length > 0, + onAction: ($event, entity) => this.showContent($event, entity.body.error, 'event.error') + }, + '100px') + ); + break; + case EventType.STATS: + this.columns.push( + new EntityTableColumn('messagesProcessed', 'event.messages-processed', '100%', + (entity) => entity.body.messagesProcessed + '', + entity => ({justifyContent: 'flex-end'}), + false, + key => ({justifyContent: 'flex-end'})), + new EntityTableColumn('errorsOccurred', 'event.errors-occurred', '100%', + (entity) => entity.body.errorsOccurred + '', + entity => ({justifyContent: 'flex-end'}), + false, + key => ({justifyContent: 'flex-end'})) + ); + break; + case DebugEventType.DEBUG_RULE_NODE: + case DebugEventType.DEBUG_RULE_CHAIN: + this.columns.push( + new EntityTableColumn('type', 'event.type', '100%', + (entity) => entity.body.type, entity => ({}), false), + new EntityTableColumn('entity', 'event.entity', '100%', + (entity) => entity.body.entityName, entity => ({}), false), + new EntityTableColumn('msgId', 'event.message-id', '100%', + (entity) => entity.body.msgId, entity => ({}), false), + new EntityTableColumn('msgType', 'event.message-type', '100%', + (entity) => entity.body.msgType, entity => ({}), false), + new EntityTableColumn('relationType', 'event.relation-type', '100%', + (entity) => entity.body.relationType, entity => ({}), false), + new EntityActionTableColumn('data', 'event.data', + { + name: this.translate.instant('action.view'), + icon: 'more_horiz', + isEnabled: (entity) => entity.body.data && entity.body.data.length > 0, + onAction: ($event, entity) => this.showContent($event, entity.body.data, + 'event.data', entity.body.dataType) + }, + '60px'), + new EntityActionTableColumn('metadata', 'event.metadata', + { + name: this.translate.instant('action.view'), + icon: 'more_horiz', + isEnabled: (entity) => entity.body.metadata && entity.body.metadata.length > 0, + onAction: ($event, entity) => this.showContent($event, entity.body.metadata, + 'event.metadata', MsgDataType.JSON) + }, + '60px'), + new EntityActionTableColumn('error', 'event.error', + { + name: this.translate.instant('action.view'), + icon: 'more_horiz', + isEnabled: (entity) => entity.body.error && entity.body.error.length > 0, + onAction: ($event, entity) => this.showContent($event, entity.body.error, + 'event.error') + }, + '60px') + ); + break; + } + if (updateTableColumns) { + this.table.columnsUpdated(true); + } + } + + showContent($event: MouseEvent, content: string, title: string, contentType: MsgDataType = null): void { + if ($event) { + $event.stopPropagation(); + } + // TODO: + this.dialogService.todo(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-header.component.html b/ui-ngx/src/app/modules/home/components/event/event-table-header.component.html new file mode 100644 index 0000000000..f73c30be7d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table-header.component.html @@ -0,0 +1,27 @@ + + + + event.event-type + + + {{ eventTypeTranslationsMap.get(type) | translate }} + + + diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-header.component.scss b/ui-ngx/src/app/modules/home/components/event/event-table-header.component.scss new file mode 100644 index 0000000000..d1a828bd74 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table-header.component.scss @@ -0,0 +1,35 @@ +/** + * 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; */ + padding-right: 8px; +} + +:host ::ng-deep { + mat-form-field { + font-size: 16px; + + .mat-form-field-wrapper { + padding-bottom: 0; + } + + .mat-form-field-underline { + bottom: 0; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-header.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table-header.component.ts new file mode 100644 index 0000000000..fd2ae23183 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table-header.component.ts @@ -0,0 +1,45 @@ +/// +/// 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 '../../components/entity/entity-table-header.component'; +import { DebugEventType, Event, EventType, eventTypeTranslations } from '@app/shared/models/event.models'; +import { EventTableConfig } from '@home/components/event/event-table-config'; + +@Component({ + selector: 'tb-event-table-header', + templateUrl: './event-table-header.component.html', + styleUrls: ['./event-table-header.component.scss'] +}) +export class EventTableHeaderComponent extends EntityTableHeaderComponent { + + eventTypeTranslationsMap = eventTypeTranslations; + + get eventTableConfig(): EventTableConfig { + return this.entitiesTableConfig as EventTableConfig; + } + + constructor(protected store: Store) { + super(store); + } + + eventTypeChanged(eventType: EventType | DebugEventType) { + this.eventTableConfig.eventType = eventType; + this.eventTableConfig.table.updateData(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.html b/ui-ngx/src/app/modules/home/components/event/event-table.component.html new file mode 100644 index 0000000000..79966651a8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.html @@ -0,0 +1,18 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.scss b/ui-ngx/src/app/modules/home/components/event/event-table.component.scss new file mode 100644 index 0000000000..0d3cc92455 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.scss @@ -0,0 +1,22 @@ +/** + * 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 ::ng-deep { + tb-entities-table { + .mat-drawer-container { + background-color: white; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts new file mode 100644 index 0000000000..c881686a6e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts @@ -0,0 +1,112 @@ +/// +/// 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, Input, OnInit, ViewChild } from '@angular/core'; +import { AuditLogService } from '@core/http/audit-log.service'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { MatDialog } from '@angular/material'; +import { AuditLogMode } from '@shared/models/audit-log.models'; +import { EntityId } from '@shared/models/id/entity-id'; +import { UserId } from '@shared/models/id/user-id'; +import { CustomerId } from '@shared/models/id/customer-id'; +import { AuditLogTableConfig } from '@home/components/audit-log/audit-log-table-config'; +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Authority } from '@shared/models/authority.enum'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { EventTableConfig } from './event-table-config'; +import { EventService } from '@core/http/event.service'; +import { DialogService } from '@core/services/dialog.service'; +import { DebugEventType, EventType } from '@shared/models/event.models'; + +@Component({ + selector: 'tb-event-table', + templateUrl: './event-table.component.html', + styleUrls: ['./event-table.component.scss'] +}) +export class EventTableComponent implements OnInit { + + @Input() + tenantId: string; + + @Input() + defaultEventType: EventType | DebugEventType; + + @Input() + disabledEventTypes: Array; + + @Input() + debugEventTypes: Array; + + activeValue = false; + dirtyValue = false; + entityIdValue: EntityId; + + @Input() + set active(active: boolean) { + if (this.activeValue !== active) { + this.activeValue = active; + if (this.activeValue && this.dirtyValue) { + this.dirtyValue = false; + this.entitiesTable.updateData(); + } + } + } + + @Input() + set entityId(entityId: EntityId) { + this.entityIdValue = entityId; + if (this.eventTableConfig && this.eventTableConfig.entityId !== entityId) { + this.eventTableConfig.eventType = this.defaultEventType; + this.eventTableConfig.entityId = entityId; + this.entitiesTable.resetSortAndFilter(this.activeValue); + if (!this.activeValue) { + this.dirtyValue = true; + } + } + } + + @ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; + + eventTableConfig: EventTableConfig; + + constructor(private eventService: EventService, + private dialogService: DialogService, + private translate: TranslateService, + private datePipe: DatePipe, + private dialog: MatDialog, + private store: Store) { + } + + ngOnInit() { + this.dirtyValue = !this.activeValue; + this.eventTableConfig = new EventTableConfig( + this.eventService, + this.dialogService, + this.translate, + this.datePipe, + this.dialog, + this.entityIdValue, + this.tenantId, + this.defaultEventType, + this.disabledEventTypes, + this.debugEventTypes + ); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index ff81170814..d2e7f1e4b8 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -24,11 +24,14 @@ import {EntityDetailsPanelComponent} from './entity/entity-details-panel.compone import {ContactComponent} from './contact.component'; import { AuditLogDetailsDialogComponent } from './audit-log/audit-log-details-dialog.component'; import { AuditLogTableComponent } from './audit-log/audit-log-table.component'; +import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component'; +import { EventTableComponent } from '@home/components/event/event-table.component'; @NgModule({ entryComponents: [ AddEntityDialogComponent, - AuditLogDetailsDialogComponent + AuditLogDetailsDialogComponent, + EventTableHeaderComponent ], declarations: [ @@ -38,7 +41,9 @@ import { AuditLogTableComponent } from './audit-log/audit-log-table.component'; EntityDetailsPanelComponent, ContactComponent, AuditLogTableComponent, - AuditLogDetailsDialogComponent + AuditLogDetailsDialogComponent, + EventTableHeaderComponent, + EventTableComponent ], imports: [ CommonModule, @@ -50,7 +55,8 @@ import { AuditLogTableComponent } from './audit-log/audit-log-table.component'; DetailsPanelComponent, EntityDetailsPanelComponent, ContactComponent, - AuditLogTableComponent + AuditLogTableComponent, + EventTableComponent ] }) export class HomeComponentsModule { } diff --git a/ui-ngx/src/app/modules/home/models/datasource/entity-datasource.ts b/ui-ngx/src/app/modules/home/models/datasource/entity-datasource.ts index 19ece9066d..d0ab2bec71 100644 --- a/ui-ngx/src/app/modules/home/models/datasource/entity-datasource.ts +++ b/ui-ngx/src/app/modules/home/models/datasource/entity-datasource.ts @@ -50,6 +50,13 @@ export class EntitiesDataSource, P extends PageLink = this.pageDataSubject.complete(); } + reset() { + const pageData = emptyPageData(); + this.entitiesSubject.next(pageData.data); + this.pageDataSubject.next(pageData); + this.dataLoadedFunction(); + } + loadEntities(pageLink: P): Observable> { const result = new ReplaySubject>(); this.fetchFunction(pageLink).pipe( diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index 2065941254..18236ca35b 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -41,6 +41,7 @@ export type EntityActionFunction> = (action: EntityAct export type CreateEntityOperation> = () => Observable; export type CellContentFunction> = (entity: T, key: string) => string; +export type HeaderCellStyleFunction> = (key: string) => object; export type CellStyleFunction> = (entity: T, key: string) => object; export interface CellActionDescriptor> { @@ -67,13 +68,35 @@ export interface HeaderActionDescriptor { onAction: ($event: MouseEvent) => void; } -export class EntityTableColumn> { +export type EntityTableColumnType = 'content' | 'action'; + +export class BaseEntityTableColumn> { + constructor(public type: EntityTableColumnType, + public key: string, + public title: string, + public maxWidth: string = '100%', + public sortable: boolean = true) { + } +} + +export class EntityTableColumn> extends BaseEntityTableColumn { constructor(public key: string, public title: string, public maxWidth: string = '100%', public cellContentFunction: CellContentFunction = (entity, property) => entity[property], public cellStyleFunction: CellStyleFunction = () => ({}), - public sortable: boolean = true) { + public sortable: boolean = true, + public headerCellStyleFunction: HeaderCellStyleFunction = () => ({})) { + super('content', key, title, maxWidth, sortable); + } +} + +export class EntityActionTableColumn> extends BaseEntityTableColumn { + constructor(public key: string, + public title: string, + public actionDescriptor: CellActionDescriptor, + public maxWidth: string = '100%') { + super('action', key, title, maxWidth, false); } } @@ -92,6 +115,8 @@ export class DateEntityTableColumn> extends EntityTabl } } +export type EntityColumn> = EntityTableColumn | EntityActionTableColumn; + export class EntityTableConfig, P extends PageLink = PageLink> { constructor() {} @@ -116,7 +141,7 @@ export class EntityTableConfig, P extends PageLink = P entityTabsComponent: Type>; addDialogStyle = {}; defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.ASC}; - columns: Array> = []; + columns: Array> = []; cellActionDescriptors: Array> = []; groupActionDescriptors: Array> = []; headerActionDescriptors: Array = []; diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html b/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html index ed78ad1b29..0e7b3001be 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/asset/asset-tabs.component.html @@ -15,6 +15,11 @@ limitations under the License. --> + + + diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html b/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html index 81c1ddbc3d..a6a4106d61 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/customer/customer-tabs.component.html @@ -15,6 +15,11 @@ limitations under the License. --> + + + diff --git a/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html b/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html index ed78ad1b29..1b68496f6f 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device-tabs.component.html @@ -15,6 +15,11 @@ limitations under the License. --> + + + diff --git a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html index ed78ad1b29..d1795cdc63 100644 --- a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view-tabs.component.html @@ -15,6 +15,11 @@ limitations under the License. --> + + + diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html index ed78ad1b29..7941b41e8c 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-tabs.component.html @@ -15,6 +15,14 @@ limitations under the License. --> + + + diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html new file mode 100644 index 0000000000..062bf3147d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.html @@ -0,0 +1,22 @@ + + + + diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.ts new file mode 100644 index 0000000000..0cbfd65d05 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant-tabs.component.ts @@ -0,0 +1,38 @@ +/// +/// 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 { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { Tenant } from '@shared/models/tenant.model'; + +@Component({ + selector: 'tb-tenant-tabs', + templateUrl: './tenant-tabs.component.html', + styleUrls: [] +}) +export class TenantTabsComponent extends EntityTabsComponent { + + constructor(protected store: Store) { + super(store); + } + + ngOnInit() { + super.ngOnInit(); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.module.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant.module.ts index f7cc6876be..eacf54c4b5 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.module.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.module.ts @@ -20,13 +20,16 @@ import { SharedModule } from '@shared/shared.module'; import {TenantComponent} from '@modules/home/pages/tenant/tenant.component'; import {TenantRoutingModule} from '@modules/home/pages/tenant/tenant-routing.module'; import {HomeComponentsModule} from '@modules/home/components/home-components.module'; +import { TenantTabsComponent } from '@home/pages/tenant/tenant-tabs.component'; @NgModule({ entryComponents: [ - TenantComponent + TenantComponent, + TenantTabsComponent ], declarations: [ - TenantComponent + TenantComponent, + TenantTabsComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenants-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenants-table-config.resolver.ts index cfaafcd417..0ccc7e20d9 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenants-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenants-table-config.resolver.ts @@ -35,6 +35,7 @@ import { import { TenantComponent } from '@modules/home/pages/tenant/tenant.component'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { User } from '@shared/models/user.model'; +import { TenantTabsComponent } from '@home/pages/tenant/tenant-tabs.component'; @Injectable() export class TenantsTableConfigResolver implements Resolve> { @@ -48,6 +49,7 @@ export class TenantsTableConfigResolver implements Resolve + + + diff --git a/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.ts new file mode 100644 index 0000000000..d14ddbed3a --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/user/user-tabs.component.ts @@ -0,0 +1,38 @@ +/// +/// 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 { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { User } from '@app/shared/models/user.model'; + +@Component({ + selector: 'tb-user-tabs', + templateUrl: './user-tabs.component.html', + styleUrls: [] +}) +export class UserTabsComponent extends EntityTabsComponent { + + constructor(protected store: Store) { + super(store); + } + + ngOnInit() { + super.ngOnInit(); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/user/user.module.ts b/ui-ngx/src/app/modules/home/pages/user/user.module.ts index d9e20921ec..5e198a651c 100644 --- a/ui-ngx/src/app/modules/home/pages/user/user.module.ts +++ b/ui-ngx/src/app/modules/home/pages/user/user.module.ts @@ -22,15 +22,18 @@ import { UserRoutingModule } from '@modules/home/pages/user/user-routing.module' import { AddUserDialogComponent } from '@modules/home/pages/user/add-user-dialog.component'; import { ActivationLinkDialogComponent } from '@modules/home/pages/user/activation-link-dialog.component'; import {HomeComponentsModule} from '@modules/home/components/home-components.module'; +import { UserTabsComponent } from '@home/pages/user/user-tabs.component'; @NgModule({ entryComponents: [ UserComponent, + UserTabsComponent, AddUserDialogComponent, ActivationLinkDialogComponent ], declarations: [ UserComponent, + UserTabsComponent, AddUserDialogComponent, ActivationLinkDialogComponent ], diff --git a/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts index a85f445182..e075d93a1a 100644 --- a/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts @@ -57,6 +57,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; import { Customer } from '@shared/models/customer.model'; import {TenantService} from '@app/core/http/tenant.service'; import {TenantId} from '@app/shared/models/id/tenant-id'; +import { UserTabsComponent } from '@home/pages/user/user-tabs.component'; export interface UsersTableRouteData { authority: Authority; @@ -83,6 +84,7 @@ export class UsersTableConfigResolver implements Resolve this.config.entityType = EntityType.USER; this.config.entityComponent = UserComponent; + this.config.entityTabsComponent = UserTabsComponent; this.config.entityTranslations = entityTypeTranslations.get(EntityType.USER); this.config.entityResources = entityTypeResources.get(EntityType.USER); diff --git a/ui-ngx/src/app/shared/models/event.models.ts b/ui-ngx/src/app/shared/models/event.models.ts new file mode 100644 index 0000000000..a8f8927a99 --- /dev/null +++ b/ui-ngx/src/app/shared/models/event.models.ts @@ -0,0 +1,89 @@ +/// +/// 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 { ActionStatus, ActionType } from '@shared/models/audit-log.models'; +import { BaseData } from '@shared/models/base-data'; +import { AuditLogId } from '@shared/models/id/audit-log-id'; +import { TenantId } from '@shared/models/id/tenant-id'; +import { CustomerId } from '@shared/models/id/customer-id'; +import { EntityId } from '@shared/models/id/entity-id'; +import { UserId } from '@shared/models/id/user-id'; +import { EventId } from './id/event-id'; +import { MsgDataType } from './rule-node.models'; + +export enum EventType { + ERROR = 'ERROR', + LC_EVENT = 'LC_EVENT', + STATS = 'STATS' +} + +export enum DebugEventType { + DEBUG_RULE_NODE = 'DEBUG_RULE_NODE', + DEBUG_RULE_CHAIN = 'DEBUG_RULE_CHAIN' +} + +export const eventTypeTranslations = new Map( + [ + [EventType.ERROR, 'event.type-error'], + [EventType.LC_EVENT, 'event.type-lc-event'], + [EventType.STATS, 'event.type-stats'], + [DebugEventType.DEBUG_RULE_NODE, 'event.type-debug-rule-node'], + [DebugEventType.DEBUG_RULE_CHAIN, 'event.type-debug-rule-chain'], + ] +); + +export interface BaseEventBody { + server: string; +} + +export interface ErrorEventBody extends BaseEventBody { + method: string; + error: string; +} + +export interface LcEventEventBody extends BaseEventBody { + event: string; + success: boolean; + error: string; +} + +export interface StatsEventBody extends BaseEventBody { + messagesProcessed: number; + errorsOccurred: number; +} + +export interface DebugRuleNodeEventBody extends BaseEventBody { + type: string; + entityId: string; + entityName: string; + msgId: string; + msgType: string; + relationType: string; + dataType: MsgDataType; + data: string; + metadata: string; + error: string; +} + +export type EventBody = ErrorEventBody & LcEventEventBody & StatsEventBody & DebugRuleNodeEventBody; + +export interface Event extends BaseData { + tenantId: TenantId; + entityId: EntityId; + type: string; + uid: string; + body: EventBody; +} diff --git a/ui-ngx/src/app/shared/models/id/event-id.ts b/ui-ngx/src/app/shared/models/id/event-id.ts new file mode 100644 index 0000000000..f7cfe7dd61 --- /dev/null +++ b/ui-ngx/src/app/shared/models/id/event-id.ts @@ -0,0 +1,24 @@ +/// +/// 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 { HasUUID } from '@shared/models/id/has-uuid'; + +export class EventId implements HasUUID { + id: string; + constructor(id: string) { + this.id = id; + } +} diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index 5448bd21af..360c8099dc 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -21,6 +21,12 @@ import {CustomerId} from '@shared/models/id/customer-id'; import {RuleChainId} from '@shared/models/id/rule-chain-id'; import {RuleNodeId} from '@shared/models/id/rule-node-id'; +export enum MsgDataType { + JSON = 'JSON', + TEXT = 'TEXT', + BINARY = 'BINARY' +} + export interface RuleNodeConfiguration { todo: Array; // TODO: diff --git a/ui-ngx/src/theme.scss b/ui-ngx/src/theme.scss index 662ae1632a..861b998f35 100644 --- a/ui-ngx/src/theme.scss +++ b/ui-ngx/src/theme.scss @@ -308,7 +308,7 @@ $tb-dark-theme: get-tb-dark-theme( } .mat-cell, .mat-header-cell { - min-width: 80px; + min-width: 40px; &:last-child { padding: 0 12px 0 0; }