attribute-table.directive.js

452 lines | 18.254 kB Blame History Raw Download
/*
 * Copyright © 2016-2018 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 'angular-material-data-table/dist/md-data-table.min.css';
import './attribute-table.scss';

/* eslint-disable import/no-unresolved, import/default */

import attributeTableTemplate from './attribute-table.tpl.html';
import addAttributeDialogTemplate from './add-attribute-dialog.tpl.html';
import addWidgetToDashboardDialogTemplate from './add-widget-to-dashboard-dialog.tpl.html';
import editAttributeValueTemplate from './edit-attribute-value.tpl.html';

/* eslint-enable import/no-unresolved, import/default */

import EditAttributeValueController from './edit-attribute-value.controller';
import AliasController from '../../api/alias-controller';

/*@ngInject*/
export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog,
                                                $mdUtil, $document, $translate, $filter, $timeout, utils, types, dashboardUtils,
                                                entityService, attributeService, widgetService) {

    var linker = function (scope, element, attrs) {

        var template = $templateCache.get(attributeTableTemplate);

        element.html(template);

        var getAttributeScopeByValue = function(attributeScopeValue) {
            if (scope.types.latestTelemetry.value === attributeScopeValue) {
                return scope.types.latestTelemetry;
            }
            for (var attrScope in scope.attributeScopes) {
                if (scope.attributeScopes[attrScope].value === attributeScopeValue) {
                    return scope.attributeScopes[attrScope];
                }
            }
        }

        scope.types = types;

        scope.entityType = attrs.entityType;

        if (scope.entityType === types.entityType.device || scope.entityType === types.entityType.entityView) {
            scope.attributeScopes = types.attributesScope;
            scope.attributeScopeSelectionReadonly = false;
        } else {
            scope.attributeScopes = {};
            scope.attributeScopes.server = types.attributesScope.server;
            scope.attributeScopeSelectionReadonly = true;
        }

        scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);

        if (scope.entityType != types.entityType.device) {
            if (scope.attributeScope != types.latestTelemetry) {
                scope.attributeScope = scope.attributeScopes.server;
            }
        }

        scope.attributes = {
            count: 0,
            data: []
        };

        scope.selectedAttributes = [];
        scope.mode = 'default'; // 'widget'
        scope.subscriptionId = null;

        scope.query = {
            order: 'key',
            limit: 5,
            page: 1,
            search: null
        };

        scope.$watch("entityId", function(newVal) {
            if (newVal) {
                scope.resetFilter();
                scope.getEntityAttributes(false, true);
            }
        });

        scope.$watch("attributeScope", function(newVal, prevVal) {
            if (newVal && !angular.equals(newVal, prevVal)) {
                scope.mode = 'default';
                scope.query.search = null;
                scope.selectedAttributes = [];
                scope.getEntityAttributes(false, true);
            }
        });

        scope.resetFilter = function() {
            scope.mode = 'default';
            scope.query.search = null;
            scope.selectedAttributes = [];
            scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);
        }

        scope.enterFilterMode = function(event) {
            let $button = angular.element(event.currentTarget);
            let $toolbarsContainer = $button.closest('.toolbarsContainer');

            scope.query.search = '';

            $timeout(()=>{
                $toolbarsContainer.find('.searchInput').focus();
            })
        }

        scope.exitFilterMode = function() {
            scope.query.search = null;
            scope.getEntityAttributes();
        }

        scope.$watch("query.search", function(newVal, prevVal) {
            if (!angular.equals(newVal, prevVal) && scope.query.search != null) {
                scope.getEntityAttributes();
            }
        });

        function success(attributes, update, apply) {
            scope.attributes = attributes;
            if (!update) {
                scope.selectedAttributes = [];
            }
            if (apply) {
                scope.$digest();
            }
        }

        scope.onReorder = function() {
            scope.getEntityAttributes(false, false);
        }

        scope.onPaginate = function() {
            scope.getEntityAttributes(false, false);
        }

        scope.getEntityAttributes = function(forceUpdate, reset) {
            if (scope.attributesDeferred) {
                scope.attributesDeferred.resolve();
            }
            if (scope.entityId && scope.entityType && scope.attributeScope) {
                if (reset) {
                    scope.attributes = {
                        count: 0,
                        data: []
                    };
                }
                scope.checkSubscription();
                scope.attributesDeferred = attributeService.getEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value,
                    scope.query, function(attributes, update, apply) {
                        success(attributes, update || forceUpdate, apply);
                    }
                );
            } else {
                var deferred = $q.defer();
                scope.attributesDeferred = deferred;
                success({
                    count: 0,
                    data: []
                });
                deferred.resolve();
            }
        }

        scope.checkSubscription = function() {
            var newSubscriptionId = null;
            if (scope.entityId && scope.entityType && scope.attributeScope.clientSide && scope.mode != 'widget') {
                newSubscriptionId = attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value);
            }
            if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {
                attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
            }
            scope.subscriptionId = newSubscriptionId;
        }

        scope.$on('$destroy', function() {
            if (scope.subscriptionId) {
                attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
            }
        });

        scope.editAttribute = function($event, attribute) {
            if (!scope.attributeScope.clientSide) {
                $event.stopPropagation();
                $mdEditDialog.show({
                    controller: EditAttributeValueController,
                    templateUrl: editAttributeValueTemplate,
                    locals: {attributeValue: attribute.value,
                             save: function (model) {
                                var updatedAttribute = angular.copy(attribute);
                                updatedAttribute.value = model.value;
                                attributeService.saveEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, [updatedAttribute]).then(
                                    function success() {
                                        scope.getEntityAttributes();
                                    }
                                );
                            }},
                    targetEvent: $event
                });
            }
        }

        scope.addAttribute = function($event) {
            if (!scope.attributeScope.clientSide) {
                $event.stopPropagation();
                $mdDialog.show({
                    controller: 'AddAttributeDialogController',
                    controllerAs: 'vm',
                    templateUrl: addAttributeDialogTemplate,
                    parent: angular.element($document[0].body),
                    locals: {entityType: scope.entityType, entityId: scope.entityId, attributeScope: scope.attributeScope.value},
                    fullscreen: true,
                    targetEvent: $event
                }).then(function () {
                    scope.getEntityAttributes();
                });
            }
        }

        scope.deleteAttributes = function($event) {
            if (!scope.attributeScope.clientSide) {
                $event.stopPropagation();
                var confirm = $mdDialog.confirm()
                    .targetEvent($event)
                    .title($translate.instant('attribute.delete-attributes-title', {count: scope.selectedAttributes.length}, 'messageformat'))
                    .htmlContent($translate.instant('attribute.delete-attributes-text'))
                    .ariaLabel($translate.instant('attribute.delete-attributes'))
                    .cancel($translate.instant('action.no'))
                    .ok($translate.instant('action.yes'));
                $mdDialog.show(confirm).then(function () {
                        attributeService.deleteEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, scope.selectedAttributes).then(
                            function success() {
                                scope.selectedAttributes = [];
                                scope.getEntityAttributes();
                            }
                        )
                });
            }
        }

        scope.nextWidget = function() {
            $mdUtil.nextTick(function () {
                if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) {
                    scope.widgetsCarousel.index++;
                }
            });
        }

        scope.prevWidget = function() {
            $mdUtil.nextTick(function () {
                if (scope.widgetsCarousel.index > 0) {
                    scope.widgetsCarousel.index--;
                }
            });
        }

        scope.enterWidgetMode = function() {

            if (scope.widgetsIndexWatch) {
                scope.widgetsIndexWatch();
                scope.widgetsIndexWatch = null;
            }

            if (scope.widgetsBundleWatch) {
                scope.widgetsBundleWatch();
                scope.widgetsBundleWatch = null;
            }

            scope.mode = 'widget';
            scope.checkSubscription();
            scope.widgetsList = [];
            scope.widgetsListCache = [];
            scope.widgetsLoaded = false;
            scope.widgetsCarousel = {
                index: 0
            }
            scope.widgetsBundle = null;
            scope.firstBundle = true;
            scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards;

            var entityAlias = {
                id: utils.guid(),
                alias: scope.entityName,
                filter: dashboardUtils.createSingleEntityFilter(scope.entityType, scope.entityId)
            };
            var entitiAliases = {};
            entitiAliases[entityAlias.id] = entityAlias;

            var stateController = {
                getStateParams: function() {
                    return {};
                }
            };
            scope.aliasController = new AliasController(scope, $q, $filter, utils,
                types, entityService, stateController, entitiAliases);

            var dataKeyType = scope.attributeScope === types.latestTelemetry ?
                types.dataKeyType.timeseries : types.dataKeyType.attribute;

            var datasource = {
                type: types.datasourceType.entity,
                entityAliasId: entityAlias.id,
                dataKeys: []
            }
            var i = 0;
            for (var attr =0; attr < scope.selectedAttributes.length;attr++) {
                var attribute = scope.selectedAttributes[attr];
                var dataKey = {
                    name: attribute.key,
                    label: attribute.key,
                    type: dataKeyType,
                    color: utils.getMaterialColor(i),
                    settings: {},
                    _hash: Math.random()
                }
                datasource.dataKeys.push(dataKey);
                i++;
            }

            scope.widgetsIndexWatch = scope.$watch('widgetsCarousel.index', function(newVal, prevVal) {
                if (scope.mode === 'widget' && (newVal != prevVal)) {
                    var index = scope.widgetsCarousel.index;
                    for (var i = 0; i < scope.widgetsList.length; i++) {
                        scope.widgetsList[i].splice(0, scope.widgetsList[i].length);
                        if (i === index) {
                            scope.widgetsList[i].push(scope.widgetsListCache[i][0]);
                        }
                    }
                }
            });

            scope.widgetsBundleWatch = scope.$watch('widgetsBundle', function(newVal, prevVal) {
                if (scope.mode === 'widget' && (scope.firstBundle === true || newVal != prevVal)) {
                    scope.widgetsList = [];
                    scope.widgetsListCache = [];
                    scope.widgetsCarousel.index = 0;
                    scope.firstBundle = false;
                    if (scope.widgetsBundle) {
                        scope.widgetsLoaded = false;
                        var bundleAlias = scope.widgetsBundle.alias;
                        var isSystem = scope.widgetsBundle.tenantId.id === types.id.nullUid;
                        widgetService.getBundleWidgetTypes(scope.widgetsBundle.alias, isSystem).then(
                            function success(widgetTypes) {

                                widgetTypes = $filter('orderBy')(widgetTypes, ['-descriptor.type','-createdTime']);

                                for (var i = 0; i < widgetTypes.length; i++) {
                                    var widgetType = widgetTypes[i];
                                    var widgetInfo = widgetService.toWidgetInfo(widgetType);
                                    if (widgetInfo.type !== types.widgetType.static.value) {
                                        var sizeX = widgetInfo.sizeX * 2;
                                        var sizeY = widgetInfo.sizeY * 2;
                                        var col = Math.floor(Math.max(0, (20 - sizeX) / 2));
                                        var widget = {
                                            isSystemType: isSystem,
                                            bundleAlias: bundleAlias,
                                            typeAlias: widgetInfo.alias,
                                            type: widgetInfo.type,
                                            title: widgetInfo.widgetName,
                                            sizeX: sizeX,
                                            sizeY: sizeY,
                                            row: 0,
                                            col: col,
                                            config: angular.fromJson(widgetInfo.defaultConfig)
                                        };

                                        widget.config.title = widgetInfo.widgetName;
                                        widget.config.datasources = [datasource];
                                        var length;
                                        if (scope.attributeScope === types.latestTelemetry && widgetInfo.type !== types.widgetType.rpc.value) {
                                            length = scope.widgetsListCache.push([widget]);
                                            scope.widgetsList.push(length === 1 ? [widget] : []);
                                        } else if (widgetInfo.type === types.widgetType.latest.value) {
                                            length = scope.widgetsListCache.push([widget]);
                                            scope.widgetsList.push(length === 1 ? [widget] : []);
                                        }
                                    }
                                }
                                scope.widgetsLoaded = true;
                            }
                        );
                    }
                }
            });
        }

        scope.exitWidgetMode = function() {
            if (scope.widgetsBundleWatch) {
                scope.widgetsBundleWatch();
                scope.widgetsBundleWatch = null;
            }
            if (scope.widgetsIndexWatch) {
                scope.widgetsIndexWatch();
                scope.widgetsIndexWatch = null;
            }
            scope.selectedWidgetsBundleAlias = null;
            scope.mode = 'default';
            scope.getEntityAttributes(true);
        }

        scope.addWidgetToDashboard = function($event) {
            if (scope.mode === 'widget' && scope.widgetsListCache.length > 0) {
                var widget = scope.widgetsListCache[scope.widgetsCarousel.index][0];
                $event.stopPropagation();
                $mdDialog.show({
                    controller: 'AddWidgetToDashboardDialogController',
                    controllerAs: 'vm',
                    templateUrl: addWidgetToDashboardDialogTemplate,
                    parent: angular.element($document[0].body),
                    locals: {entityId: scope.entityId, entityType: scope.entityType, entityName: scope.entityName, widget: angular.copy(widget)},
                    fullscreen: true,
                    targetEvent: $event
                }).then(function () {

                });
            }
        }

        scope.loading = function() {
            return $rootScope.loading;
        }

        $compile(element.contents())(scope);
    }

    return {
        restrict: "E",
        link: linker,
        scope: {
            entityId: '=',
            entityName: '=',
            disableAttributeScopeSelection: '@?'
        }
    };
}