Commit 88bb7f31b0f8b78a335c541df2ed6f4bc71c0065

Authored by Igor Kulikov
1 parent 207642e8

UI: Ability to set default dashboard for customer user

@@ -22,8 +22,9 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin, @@ -22,8 +22,9 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
22 .name; 22 .name;
23 23
24 /*@ngInject*/ 24 /*@ngInject*/
25 -function UserService($http, $q, $rootScope, store, jwtHelper, $translate) { 25 +function UserService($http, $q, $rootScope, store, jwtHelper, $translate, $state) {
26 var currentUser = null, 26 var currentUser = null,
  27 + currentUserDetails = null,
27 userLoaded = false; 28 userLoaded = false;
28 29
29 var refreshTokenQueue = []; 30 var refreshTokenQueue = [];
@@ -47,6 +48,8 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) { @@ -47,6 +48,8 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) {
47 refreshJwtToken: refreshJwtToken, 48 refreshJwtToken: refreshJwtToken,
48 refreshTokenPending: refreshTokenPending, 49 refreshTokenPending: refreshTokenPending,
49 updateAuthorizationHeader: updateAuthorizationHeader, 50 updateAuthorizationHeader: updateAuthorizationHeader,
  51 + gotoDefaultPlace: gotoDefaultPlace,
  52 + forceDefaultPlace: forceDefaultPlace,
50 logout: logout 53 logout: logout
51 } 54 }
52 55
@@ -86,6 +89,7 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) { @@ -86,6 +89,7 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) {
86 89
87 function setUserFromJwtToken(jwtToken, refreshToken, notify, doLogout) { 90 function setUserFromJwtToken(jwtToken, refreshToken, notify, doLogout) {
88 currentUser = null; 91 currentUser = null;
  92 + currentUserDetails = null;
89 if (!jwtToken) { 93 if (!jwtToken) {
90 clearTokenData(); 94 clearTokenData();
91 if (notify) { 95 if (notify) {
@@ -222,7 +226,25 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) { @@ -222,7 +226,25 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) {
222 } else if (currentUser) { 226 } else if (currentUser) {
223 currentUser.authority = "ANONYMOUS"; 227 currentUser.authority = "ANONYMOUS";
224 } 228 }
225 - deferred.resolve(); 229 + if (currentUser.userId) {
  230 + getUser(currentUser.userId).then(
  231 + function success(user) {
  232 + currentUserDetails = user;
  233 + if (currentUserDetails.additionalInfo &&
  234 + currentUserDetails.additionalInfo.defaultDashboardFullscreen) {
  235 + $rootScope.forceFullscreen = currentUserDetails.additionalInfo.defaultDashboardFullscreen === true;
  236 + } else {
  237 + $rootScope.forceFullscreen = false;
  238 + }
  239 + deferred.resolve();
  240 + },
  241 + function fail() {
  242 + deferred.reject();
  243 + }
  244 + )
  245 + } else {
  246 + deferred.reject();
  247 + }
226 }, function fail() { 248 }, function fail() {
227 deferred.reject(); 249 deferred.reject();
228 }); 250 });
@@ -331,4 +353,40 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) { @@ -331,4 +353,40 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate) {
331 return deferred.promise; 353 return deferred.promise;
332 } 354 }
333 355
  356 + function forceDefaultPlace(to, params) {
  357 + if (currentUser && isAuthenticated()) {
  358 + if (currentUser.authority === 'CUSTOMER_USER') {
  359 + if (currentUserDetails &&
  360 + currentUserDetails.additionalInfo &&
  361 + currentUserDetails.additionalInfo.defaultDashboardId) {
  362 + if ($rootScope.forceFullscreen) {
  363 + if (to.name === 'home.profile') {
  364 + return false;
  365 + } else if (to.name !== 'home.dashboards.dashboard' && params.dashboardId !== currentUserDetails.additionalInfo.defaultDashboardId) {
  366 + return true;
  367 + }
  368 + }
  369 + }
  370 + }
  371 + }
  372 + return false;
  373 + }
  374 +
  375 + function gotoDefaultPlace(params) {
  376 + if (currentUser && isAuthenticated()) {
  377 + var place = 'home.links';
  378 + if (currentUser.authority === 'CUSTOMER_USER') {
  379 + if (currentUserDetails &&
  380 + currentUserDetails.additionalInfo &&
  381 + currentUserDetails.additionalInfo.defaultDashboardId) {
  382 + place = 'home.dashboards.dashboard';
  383 + params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId};
  384 + }
  385 + }
  386 + $state.go(place, params);
  387 + } else {
  388 + $state.go('login', params);
  389 + }
  390 + }
  391 +
334 } 392 }
@@ -38,7 +38,7 @@ export default function AppConfig($provide, @@ -38,7 +38,7 @@ export default function AppConfig($provide,
38 38
39 injectTapEventPlugin(); 39 injectTapEventPlugin();
40 $locationProvider.html5Mode(true); 40 $locationProvider.html5Mode(true);
41 - $urlRouterProvider.otherwise('/home'); 41 + //$urlRouterProvider.otherwise('/home');
42 storeProvider.setCaching(false); 42 storeProvider.setCaching(false);
43 43
44 $translateProvider.useSanitizeValueStrategy('sanitize'); 44 $translateProvider.useSanitizeValueStrategy('sanitize');
@@ -36,6 +36,8 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi @@ -36,6 +36,8 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi
36 36
37 initWatchers(); 37 initWatchers();
38 38
  39 + checkCurrentState();
  40 +
39 function initWatchers() { 41 function initWatchers() {
40 $rootScope.unauthenticatedHandle = $rootScope.$on('unauthenticated', function (event, doLogout) { 42 $rootScope.unauthenticatedHandle = $rootScope.$on('unauthenticated', function (event, doLogout) {
41 if (doLogout) { 43 if (doLogout) {
@@ -56,22 +58,27 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi @@ -56,22 +58,27 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi
56 $rootScope.stateChangeStartHandle = $rootScope.$on('$stateChangeStart', function (evt, to, params) { 58 $rootScope.stateChangeStartHandle = $rootScope.$on('$stateChangeStart', function (evt, to, params) {
57 if (userService.isUserLoaded() === true) { 59 if (userService.isUserLoaded() === true) {
58 if (userService.isAuthenticated()) { 60 if (userService.isAuthenticated()) {
59 - var authority = userService.getAuthority();  
60 - if (to.module === 'public') {  
61 - evt.preventDefault();  
62 - $state.go('home', params);  
63 - } else if (angular.isDefined(to.auth) &&  
64 - to.auth.indexOf(authority) === -1) { 61 + if (userService.forceDefaultPlace(to, params)) {
65 evt.preventDefault(); 62 evt.preventDefault();
66 - showForbiddenDialog();  
67 - } else if (to.redirectTo) {  
68 - evt.preventDefault();  
69 - $state.go(to.redirectTo, params) 63 + gotoDefaultPlace(params);
  64 + } else {
  65 + var authority = userService.getAuthority();
  66 + if (to.module === 'public') {
  67 + evt.preventDefault();
  68 + gotoDefaultPlace(params);
  69 + } else if (angular.isDefined(to.auth) &&
  70 + to.auth.indexOf(authority) === -1) {
  71 + evt.preventDefault();
  72 + showForbiddenDialog();
  73 + } else if (to.redirectTo) {
  74 + evt.preventDefault();
  75 + $state.go(to.redirectTo, params)
  76 + }
70 } 77 }
71 } else { 78 } else {
72 if (to.module === 'private') { 79 if (to.module === 'private') {
73 evt.preventDefault(); 80 evt.preventDefault();
74 - if (to.url === '/home') { 81 + if (to.url === '/home' || to.url === '/') {
75 $state.go('login', params); 82 $state.go('login', params);
76 } else { 83 } else {
77 showUnauthorizedDialog(); 84 showUnauthorizedDialog();
@@ -80,6 +87,9 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi @@ -80,6 +87,9 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi
80 } 87 }
81 } else { 88 } else {
82 evt.preventDefault(); 89 evt.preventDefault();
  90 + if ($rootScope.userLoadedHandle) {
  91 + $rootScope.userLoadedHandle();
  92 + }
83 $rootScope.userLoadedHandle = $rootScope.$on('userLoaded', function () { 93 $rootScope.userLoadedHandle = $rootScope.$on('userLoaded', function () {
84 $rootScope.userLoadedHandle(); 94 $rootScope.userLoadedHandle();
85 $state.go(to.name, params); 95 $state.go(to.name, params);
@@ -102,23 +112,26 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi @@ -102,23 +112,26 @@ export default function AppRun($rootScope, $window, $log, $state, $mdDialog, $fi
102 112
103 function checkCurrentState() { 113 function checkCurrentState() {
104 if (userService.isUserLoaded() === true) { 114 if (userService.isUserLoaded() === true) {
105 - var module = $state.$current.module;  
106 if (userService.isAuthenticated()) { 115 if (userService.isAuthenticated()) {
107 - if ($state.$current.module === 'public') {  
108 - $state.go('home');  
109 - } 116 + gotoDefaultPlace();
110 } else { 117 } else {
111 - if (angular.isUndefined(module) || !module) {  
112 - //$state.go('login');  
113 - } else if ($state.$current.module === 'private') {  
114 - showUnauthorizedDialog();  
115 - } 118 + $state.go('login');
116 } 119 }
117 } else { 120 } else {
118 - showUnauthorizedDialog(); 121 + if ($rootScope.userLoadedHandle) {
  122 + $rootScope.userLoadedHandle();
  123 + }
  124 + $rootScope.userLoadedHandle = $rootScope.$on('userLoaded', function () {
  125 + $rootScope.userLoadedHandle();
  126 + checkCurrentState();
  127 + });
119 } 128 }
120 } 129 }
121 130
  131 + function gotoDefaultPlace(params) {
  132 + userService.gotoDefaultPlace(params);
  133 + }
  134 +
122 function showUnauthorizedDialog() { 135 function showUnauthorizedDialog() {
123 if (unauthorizedDialog === null) { 136 if (unauthorizedDialog === null) {
124 $translate(['access.unauthorized-access', 137 $translate(['access.unauthorized-access',
@@ -40,27 +40,19 @@ function DashboardSelect($compile, $templateCache, $q, dashboardService, userSer @@ -40,27 +40,19 @@ function DashboardSelect($compile, $templateCache, $q, dashboardService, userSer
40 scope.dashboard = null; 40 scope.dashboard = null;
41 scope.dashboardSearchText = ''; 41 scope.dashboardSearchText = '';
42 42
43 - scope.dashboardFetchFunction = dashboardService.getTenantDashboards;  
44 - if (angular.isDefined(scope.dashboardsScope)) {  
45 - if (scope.dashboardsScope === 'customer') {  
46 - scope.dashboardFetchFunction = dashboardService.getCustomerDashboards;  
47 - } else {  
48 - scope.dashboardFetchFunction = dashboardService.getTenantDashboards;  
49 - }  
50 - } else {  
51 - if (userService.getAuthority() === 'TENANT_ADMIN') {  
52 - scope.dashboardFetchFunction = dashboardService.getTenantDashboards;  
53 - } else if (userService.getAuthority() === 'CUSTOMER_USER') {  
54 - scope.dashboardFetchFunction = dashboardService.getCustomerDashboards;  
55 - }  
56 - }  
57 -  
58 scope.fetchDashboards = function(searchText) { 43 scope.fetchDashboards = function(searchText) {
59 var pageLink = {limit: 10, textSearch: searchText}; 44 var pageLink = {limit: 10, textSearch: searchText};
60 45
61 var deferred = $q.defer(); 46 var deferred = $q.defer();
62 47
63 - scope.dashboardFetchFunction(pageLink).then(function success(result) { 48 + var promise;
  49 + if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
  50 + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink);
  51 + } else {
  52 + promise = dashboardService.getTenantDashboards(pageLink);
  53 + }
  54 +
  55 + promise.then(function success(result) {
64 deferred.resolve(result.data); 56 deferred.resolve(result.data);
65 }, function fail() { 57 }, function fail() {
66 deferred.reject(); 58 deferred.reject();
@@ -79,6 +71,8 @@ function DashboardSelect($compile, $templateCache, $q, dashboardService, userSer @@ -79,6 +71,8 @@ function DashboardSelect($compile, $templateCache, $q, dashboardService, userSer
79 ngModelCtrl.$render = function () { 71 ngModelCtrl.$render = function () {
80 if (ngModelCtrl.$viewValue) { 72 if (ngModelCtrl.$viewValue) {
81 scope.dashboard = ngModelCtrl.$viewValue; 73 scope.dashboard = ngModelCtrl.$viewValue;
  74 + } else {
  75 + scope.dashboard = null;
82 } 76 }
83 } 77 }
84 78
@@ -106,6 +100,7 @@ function DashboardSelect($compile, $templateCache, $q, dashboardService, userSer @@ -106,6 +100,7 @@ function DashboardSelect($compile, $templateCache, $q, dashboardService, userSer
106 link: linker, 100 link: linker,
107 scope: { 101 scope: {
108 dashboardsScope: '@', 102 dashboardsScope: '@',
  103 + customerId: '=',
109 theForm: '=?', 104 theForm: '=?',
110 tbRequired: '=?', 105 tbRequired: '=?',
111 selectFirstDashboard: '=' 106 selectFirstDashboard: '='
@@ -108,10 +108,10 @@ section.tb-dashboard-toolbar { @@ -108,10 +108,10 @@ section.tb-dashboard-toolbar {
108 } 108 }
109 } 109 }
110 .md-fab-toolbar-wrapper { 110 .md-fab-toolbar-wrapper {
111 - height: 40px; 111 + height: 50px;
112 md-toolbar { 112 md-toolbar {
113 - min-height: 36px;  
114 - height: 36px; 113 + min-height: 46px;
  114 + height: 46px;
115 md-fab-actions { 115 md-fab-actions {
116 font-size: 16px; 116 font-size: 16px;
117 margin-top: 0px; 117 margin-top: 0px;
@@ -126,7 +126,7 @@ section.tb-dashboard-toolbar { @@ -126,7 +126,7 @@ section.tb-dashboard-toolbar {
126 126
127 .tb-dashboard-container { 127 .tb-dashboard-container {
128 &.tb-dashboard-toolbar-opened { 128 &.tb-dashboard-toolbar-opened {
129 - margin-top: 40px; 129 + margin-top: 50px;
130 @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2)); 130 @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
131 } 131 }
132 &.tb-dashboard-toolbar-closed { 132 &.tb-dashboard-toolbar-closed {
@@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode" expand-button-id="dashboard-expand-button"  
19 - hide-expand-button="vm.widgetEditMode || vm.iframeMode" expand-tooltip-direction="bottom" 18 +<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
  19 + hide-expand-button="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-tooltip-direction="bottom"
20 ng-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor, 20 ng-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor,
21 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')', 21 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')',
22 'background-repeat': 'no-repeat', 22 'background-repeat': 'no-repeat',
@@ -47,6 +47,8 @@ @@ -47,6 +47,8 @@
47 aria-label="{{ 'fullscreen.fullscreen' | translate }}" 47 aria-label="{{ 'fullscreen.fullscreen' | translate }}"
48 class="md-icon-button"> 48 class="md-icon-button">
49 </md-button> 49 </md-button>
  50 + <tb-user-menu ng-show="forceFullscreen" display-user-info="true">
  51 + </tb-user-menu>
50 <tb-timewindow direction="left" tooltip-direction="bottom" aggregation ng-model="vm.dashboardConfiguration.timewindow"> 52 <tb-timewindow direction="left" tooltip-direction="bottom" aggregation ng-model="vm.dashboardConfiguration.timewindow">
51 </tb-timewindow> 53 </tb-timewindow>
52 <tb-aliases-device-select ng-show="!vm.isEdit" 54 <tb-aliases-device-select ng-show="!vm.isEdit"
@@ -26,9 +26,7 @@ import logoSvg from '../../svg/logo_title_white.svg'; @@ -26,9 +26,7 @@ import logoSvg from '../../svg/logo_title_white.svg';
26 26
27 /*@ngInject*/ 27 /*@ngInject*/
28 export default function HomeController(loginService, userService, deviceService, Fullscreen, $scope, $element, $rootScope, $document, $state, 28 export default function HomeController(loginService, userService, deviceService, Fullscreen, $scope, $element, $rootScope, $document, $state,
29 - $log, $mdMedia, $animate, $timeout, $translate) {  
30 -  
31 - var dashboardUser = userService.getCurrentUser(); 29 + $log, $mdMedia, $animate, $timeout) {
32 30
33 var siteSideNav = $('.tb-site-sidenav', $element); 31 var siteSideNav = $('.tb-site-sidenav', $element);
34 32
@@ -48,15 +46,11 @@ export default function HomeController(loginService, userService, deviceService, @@ -48,15 +46,11 @@ export default function HomeController(loginService, userService, deviceService,
48 vm.isShowSidenav = false; 46 vm.isShowSidenav = false;
49 vm.isLockSidenav = false; 47 vm.isLockSidenav = false;
50 48
51 - vm.authorityName = authorityName;  
52 vm.displaySearchMode = displaySearchMode; 49 vm.displaySearchMode = displaySearchMode;
53 - vm.logout = logout;  
54 - vm.openProfile = openProfile;  
55 vm.openSidenav = openSidenav; 50 vm.openSidenav = openSidenav;
56 vm.searchTextUpdated = searchTextUpdated; 51 vm.searchTextUpdated = searchTextUpdated;
57 vm.sidenavClicked = sidenavClicked; 52 vm.sidenavClicked = sidenavClicked;
58 vm.toggleFullscreen = toggleFullscreen; 53 vm.toggleFullscreen = toggleFullscreen;
59 - vm.userDisplayName = userDisplayName;  
60 54
61 $scope.$on('$stateChangeSuccess', function (evt, to, toParams, from) { 55 $scope.$on('$stateChangeSuccess', function (evt, to, toParams, from) {
62 if (angular.isDefined(to.data.searchEnabled)) { 56 if (angular.isDefined(to.data.searchEnabled)) {
@@ -106,50 +100,6 @@ export default function HomeController(loginService, userService, deviceService, @@ -106,50 +100,6 @@ export default function HomeController(loginService, userService, deviceService,
106 $scope.$broadcast('searchTextUpdated'); 100 $scope.$broadcast('searchTextUpdated');
107 } 101 }
108 102
109 - function authorityName() {  
110 - var name = "user.anonymous";  
111 - if (dashboardUser) {  
112 - var authority = dashboardUser.authority;  
113 - if (authority === 'SYS_ADMIN') {  
114 - name = 'user.sys-admin';  
115 - } else if (authority === 'TENANT_ADMIN') {  
116 - name = 'user.tenant-admin';  
117 - } else if (authority === 'CUSTOMER_USER') {  
118 - name = 'user.customer';  
119 - }  
120 - }  
121 - return $translate.instant(name);  
122 - }  
123 -  
124 - function userDisplayName() {  
125 - var name = "";  
126 - if (dashboardUser) {  
127 - if ((dashboardUser.firstName && dashboardUser.firstName.length > 0) ||  
128 - (dashboardUser.lastName && dashboardUser.lastName.length > 0)) {  
129 - if (dashboardUser.firstName) {  
130 - name += dashboardUser.firstName;  
131 - }  
132 - if (dashboardUser.lastName) {  
133 - if (name.length > 0) {  
134 - name += " ";  
135 - }  
136 - name += dashboardUser.lastName;  
137 - }  
138 - } else {  
139 - name = dashboardUser.email;  
140 - }  
141 - }  
142 - return name;  
143 - }  
144 -  
145 - function openProfile() {  
146 - $state.go('home.profile');  
147 - }  
148 -  
149 - function logout() {  
150 - userService.logout();  
151 - }  
152 -  
153 function openSidenav() { 103 function openSidenav() {
154 vm.isShowSidenav = true; 104 vm.isShowSidenav = true;
155 } 105 }
@@ -53,37 +53,11 @@ md-sidenav.tb-site-sidenav { @@ -53,37 +53,11 @@ md-sidenav.tb-site-sidenav {
53 width: 250px; 53 width: 250px;
54 } 54 }
55 55
56 -md-icon.tb-mini-avatar {  
57 - margin: auto 8px;  
58 - font-size: 36px;  
59 - height: 36px;  
60 - width: 36px;  
61 -}  
62 -  
63 md-icon.tb-logo-title { 56 md-icon.tb-logo-title {
64 height: 36px; 57 height: 36px;
65 width: 200px; 58 width: 200px;
66 } 59 }
67 60
68 -div.tb-user-info {  
69 - line-height: 1.5;  
70 - span {  
71 - text-transform: none;  
72 - text-align: left;  
73 - }  
74 - span.tb-user-display-name {  
75 - font-size: 0.800rem;  
76 - font-weight: 300;  
77 - letter-spacing: 0.008em;  
78 - }  
79 - span.tb-user-authority {  
80 - font-size: 0.800rem;  
81 - font-weight: 300;  
82 - letter-spacing: 0.005em;  
83 - opacity: 0.8;  
84 - }  
85 -}  
86 -  
87 .tb-nav-header { 61 .tb-nav-header {
88 flex-shrink: 0; 62 flex-shrink: 0;
89 z-index: 2; 63 z-index: 2;
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 16
17 --> 17 -->
18 <md-sidenav class="tb-site-sidenav md-sidenav-left md-whiteframe-z2" 18 <md-sidenav class="tb-site-sidenav md-sidenav-left md-whiteframe-z2"
  19 + ng-show="!forceFullscreen"
19 hide-print="" 20 hide-print=""
20 md-component-id="left" 21 md-component-id="left"
21 aria-label="Toggle Nav" 22 aria-label="Toggle Nav"
@@ -59,33 +60,10 @@ @@ -59,33 +60,10 @@
59 </md-button> 60 </md-button>
60 <md-button ng-show="!vm.displaySearchMode()" hide-xs hide-sm class="md-icon-button" ng-click="vm.toggleFullscreen()" aria-label="{{ 'fullscreen.toggle' | translate }}"> 61 <md-button ng-show="!vm.displaySearchMode()" hide-xs hide-sm class="md-icon-button" ng-click="vm.toggleFullscreen()" aria-label="{{ 'fullscreen.toggle' | translate }}">
61 <ng-md-icon icon="{{vm.Fullscreen.isEnabled() ? 'fullscreen_exit' : 'fullscreen'}}" options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon> 62 <ng-md-icon icon="{{vm.Fullscreen.isEnabled() ? 'fullscreen_exit' : 'fullscreen'}}" options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon>
62 - </md-button>  
63 - <div hide-xs hide-sm ng-show="!vm.displaySearchMode()" class="tb-user-info" layout="row">  
64 - <md-icon aria-label="{{ 'home.avatar' | translate }}" class="material-icons tb-mini-avatar">account_circle</md-icon>  
65 - <div layout="column">  
66 - <span class="tb-user-display-name">{{vm.userDisplayName()}}</span>  
67 - <span class="tb-user-authority">{{vm.authorityName()}}</span>  
68 - </div>  
69 - </div>  
70 - <md-menu md-position-mode="target-right target">  
71 - <md-button class="md-icon-button" aria-label="{{ 'home.open-user-menu' | translate }}" ng-click="$mdOpenMenu($event)">  
72 - <md-icon md-menu-origin aria-label="{{ 'home.open-user-menu' | translate }}" class="material-icons">more_vert</md-icon>  
73 - </md-button>  
74 - <md-menu-content width="4">  
75 - <md-menu-item>  
76 - <md-button ng-click="vm.openProfile()">  
77 - <md-icon md-menu-align-target aria-label="{{ 'home.profile' | translate }}" class="material-icons">account_circle</md-icon>  
78 - <span translate>home.profile</span>  
79 - </md-button>  
80 - </md-menu-item>  
81 - <md-menu-item>  
82 - <md-button ng-click="vm.logout()">  
83 - <md-icon md-menu-align-target aria-label="{{ 'home.logout' | translate }}" class="material-icons">exit_to_app</md-icon>  
84 - <span translate>home.logout</span>  
85 - </md-button>  
86 - </md-menu-item>  
87 - </md-menu-content>  
88 - </md-menu> 63 + </md-button>
  64 + <tb-user-menu
  65 + display-user-info="!vm.displaySearchMode()">
  66 + </tb-user-menu>
89 </div> 67 </div>
90 </md-toolbar> 68 </md-toolbar>
91 <md-progress-linear class="md-warn" style="z-index: 10; max-height: 0px; width: 100%;" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> 69 <md-progress-linear class="md-warn" style="z-index: 10; max-height: 0px; width: 100%;" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
@@ -28,6 +28,8 @@ import thingsboardApiUser from '../api/user.service'; @@ -28,6 +28,8 @@ import thingsboardApiUser from '../api/user.service';
28 import thingsboardNoAnimate from '../components/no-animate.directive'; 28 import thingsboardNoAnimate from '../components/no-animate.directive';
29 import thingsboardSideMenu from '../components/side-menu.directive'; 29 import thingsboardSideMenu from '../components/side-menu.directive';
30 30
  31 +import thingsboardUserMenu from './user-menu.directive';
  32 +
31 import thingsboardTenant from '../tenant'; 33 import thingsboardTenant from '../tenant';
32 import thingsboardCustomer from '../customer'; 34 import thingsboardCustomer from '../customer';
33 import thingsboardUser from '../user'; 35 import thingsboardUser from '../user';
@@ -54,6 +56,7 @@ export default angular.module('thingsboard.home', [ @@ -54,6 +56,7 @@ export default angular.module('thingsboard.home', [
54 'ncy-angular-breadcrumb', 56 'ncy-angular-breadcrumb',
55 thingsboardMenu, 57 thingsboardMenu,
56 thingsboardHomeLinks, 58 thingsboardHomeLinks,
  59 + thingsboardUserMenu,
57 thingsboardTenant, 60 thingsboardTenant,
58 thingsboardCustomer, 61 thingsboardCustomer,
59 thingsboardUser, 62 thingsboardUser,
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import './user-menu.scss';
  18 +
  19 +/* eslint-disable import/no-unresolved, import/default */
  20 +
  21 +import userMenuTemplate from './user-menu.tpl.html';
  22 +
  23 +/* eslint-enable import/no-unresolved, import/default */
  24 +
  25 +export default angular.module('thingsboard.directives.usermenu', [])
  26 + .directive('tbUserMenu', UserMenu)
  27 + .name;
  28 +
  29 +/*@ngInject*/
  30 +function UserMenu() {
  31 + return {
  32 + restrict: "E",
  33 + scope: true,
  34 + bindToController: {
  35 + displayUserInfo: '=',
  36 + },
  37 + controller: UserMenuController,
  38 + controllerAs: 'vm',
  39 + templateUrl: userMenuTemplate
  40 + };
  41 +}
  42 +
  43 +/*@ngInject*/
  44 +function UserMenuController($scope, userService, $translate, $state) {
  45 +
  46 + var vm = this;
  47 +
  48 + var dashboardUser = userService.getCurrentUser();
  49 +
  50 + vm.authorityName = authorityName;
  51 + vm.displaySearchMode = displaySearchMode;
  52 + vm.logout = logout;
  53 + vm.openProfile = openProfile;
  54 + vm.userDisplayName = userDisplayName;
  55 +
  56 + function displaySearchMode() {
  57 + return $scope.searchConfig.searchEnabled &&
  58 + $scope.searchConfig.showSearch;
  59 + }
  60 +
  61 + function authorityName() {
  62 + var name = "user.anonymous";
  63 + if (dashboardUser) {
  64 + var authority = dashboardUser.authority;
  65 + if (authority === 'SYS_ADMIN') {
  66 + name = 'user.sys-admin';
  67 + } else if (authority === 'TENANT_ADMIN') {
  68 + name = 'user.tenant-admin';
  69 + } else if (authority === 'CUSTOMER_USER') {
  70 + name = 'user.customer';
  71 + }
  72 + }
  73 + return $translate.instant(name);
  74 + }
  75 +
  76 + function userDisplayName() {
  77 + var name = "";
  78 + if (dashboardUser) {
  79 + if ((dashboardUser.firstName && dashboardUser.firstName.length > 0) ||
  80 + (dashboardUser.lastName && dashboardUser.lastName.length > 0)) {
  81 + if (dashboardUser.firstName) {
  82 + name += dashboardUser.firstName;
  83 + }
  84 + if (dashboardUser.lastName) {
  85 + if (name.length > 0) {
  86 + name += " ";
  87 + }
  88 + name += dashboardUser.lastName;
  89 + }
  90 + } else {
  91 + name = dashboardUser.email;
  92 + }
  93 + }
  94 + return name;
  95 + }
  96 +
  97 + function openProfile() {
  98 + $state.go('home.profile');
  99 + }
  100 +
  101 + function logout() {
  102 + userService.logout();
  103 + }
  104 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +div.tb-user-info {
  18 + line-height: 1.5;
  19 + span {
  20 + text-transform: none;
  21 + text-align: left;
  22 + }
  23 + span.tb-user-display-name {
  24 + font-size: 0.800rem;
  25 + font-weight: 300;
  26 + letter-spacing: 0.008em;
  27 + }
  28 + span.tb-user-authority {
  29 + font-size: 0.800rem;
  30 + font-weight: 300;
  31 + letter-spacing: 0.005em;
  32 + opacity: 0.8;
  33 + }
  34 +}
  35 +
  36 +md-icon.tb-mini-avatar {
  37 + margin: auto 8px;
  38 + font-size: 36px;
  39 + height: 36px;
  40 + width: 36px;
  41 +}
  1 +<section layout="row">
  2 + <div hide-xs hide-sm ng-show="vm.displayUserInfo" class="tb-user-info" layout="row">
  3 + <md-icon aria-label="{{ 'home.avatar' | translate }}" class="material-icons tb-mini-avatar">account_circle</md-icon>
  4 + <div layout="column">
  5 + <span class="tb-user-display-name">{{vm.userDisplayName()}}</span>
  6 + <span class="tb-user-authority">{{vm.authorityName()}}</span>
  7 + </div>
  8 + </div>
  9 + <md-menu md-position-mode="target-right target">
  10 + <md-button class="md-icon-button" aria-label="{{ 'home.open-user-menu' | translate }}" ng-click="$mdOpenMenu($event)">
  11 + <md-icon md-menu-origin aria-label="{{ 'home.open-user-menu' | translate }}" class="material-icons">more_vert</md-icon>
  12 + </md-button>
  13 + <md-menu-content width="4">
  14 + <md-menu-item>
  15 + <md-button ng-click="vm.openProfile()">
  16 + <md-icon md-menu-align-target aria-label="{{ 'home.profile' | translate }}" class="material-icons">account_circle</md-icon>
  17 + <span translate>home.profile</span>
  18 + </md-button>
  19 + </md-menu-item>
  20 + <md-menu-item>
  21 + <md-button ng-click="vm.logout()">
  22 + <md-icon md-menu-align-target aria-label="{{ 'home.logout' | translate }}" class="material-icons">exit_to_app</md-icon>
  23 + <span translate>home.logout</span>
  24 + </md-button>
  25 + </md-menu-item>
  26 + </md-menu-content>
  27 + </md-menu>
  28 +</section>
@@ -632,7 +632,9 @@ export default angular.module('thingsboard.locale', []) @@ -632,7 +632,9 @@ export default angular.module('thingsboard.locale', [])
632 "email-required": "Email is required.", 632 "email-required": "Email is required.",
633 "first-name": "First Name", 633 "first-name": "First Name",
634 "last-name": "Last Name", 634 "last-name": "Last Name",
635 - "description": "Description" 635 + "description": "Description",
  636 + "default-dashboard": "Default dashboard",
  637 + "always-fullscreen": "Always fullscreen"
636 }, 638 },
637 "value": { 639 "value": {
638 "type": "Value type", 640 "type": "Value type",
@@ -63,7 +63,7 @@ function Menu(userService, $state, $rootScope) { @@ -63,7 +63,7 @@ function Menu(userService, $state, $rootScope) {
63 { 63 {
64 name: 'home.home', 64 name: 'home.home',
65 type: 'link', 65 type: 'link',
66 - state: 'home', 66 + state: 'home.links',
67 icon: 'home' 67 icon: 'home'
68 }, 68 },
69 { 69 {
@@ -167,7 +167,7 @@ function Menu(userService, $state, $rootScope) { @@ -167,7 +167,7 @@ function Menu(userService, $state, $rootScope) {
167 { 167 {
168 name: 'home.home', 168 name: 'home.home',
169 type: 'link', 169 type: 'link',
170 - state: 'home', 170 + state: 'home.links',
171 icon: 'home' 171 icon: 'home'
172 }, 172 },
173 { 173 {
@@ -264,7 +264,7 @@ function Menu(userService, $state, $rootScope) { @@ -264,7 +264,7 @@ function Menu(userService, $state, $rootScope) {
264 { 264 {
265 name: 'home.home', 265 name: 'home.home',
266 type: 'link', 266 type: 'link',
267 - state: 'home', 267 + state: 'home.links',
268 icon: 'home' 268 icon: 'home'
269 }, 269 },
270 { 270 {
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +@import '../../scss/constants';
  18 +
  19 +.tb-default-dashboard {
  20 + .tb-default-dashboard-label {
  21 + padding-bottom: 8px;
  22 + }
  23 + tb-dashboard-select {
  24 + @media (min-width: $layout-breakpoint-gt-sm) {
  25 + padding-right: 12px;
  26 + }
  27 + @media (max-width: $layout-breakpoint-gt-sm) {
  28 + padding-bottom: 12px;
  29 + }
  30 + }
  31 +}
@@ -43,5 +43,20 @@ @@ -43,5 +43,20 @@
43 <label translate>user.description</label> 43 <label translate>user.description</label>
44 <textarea ng-model="user.additionalInfo.description" rows="2"></textarea> 44 <textarea ng-model="user.additionalInfo.description" rows="2"></textarea>
45 </md-input-container> 45 </md-input-container>
  46 + <section class="tb-default-dashboard" flex layout="column"ng-show="isCustomerUser()">
  47 + <span class="tb-default-dashboard-label" ng-class="{'tb-disabled-label': loading || !isEdit}" translate>user.default-dashboard</span>
  48 + <section flex layout="column" layout-gt-sm="row">
  49 + <tb-dashboard-select flex
  50 + the-form="theForm"
  51 + ng-model="defaultDashboard"
  52 + dashboards-scope="customer"
  53 + customer-id="user.customerId.id"
  54 + select-first-dashboard="false">
  55 + </tb-dashboard-select>
  56 + <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'user.always-fullscreen' | translate }}"
  57 + ng-model="user.additionalInfo.defaultDashboardFullscreen">{{ 'user.always-fullscreen' | translate }}
  58 + </md-checkbox>
  59 + </section>
  60 + </section>
46 </fieldset> 61 </fieldset>
47 </md-content> 62 </md-content>
@@ -13,6 +13,9 @@ @@ -13,6 +13,9 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
  16 +
  17 +import './user-fieldset.scss';
  18 +
16 /* eslint-disable import/no-unresolved, import/default */ 19 /* eslint-disable import/no-unresolved, import/default */
17 20
18 import userFieldsetTemplate from './user-fieldset.tpl.html'; 21 import userFieldsetTemplate from './user-fieldset.tpl.html';
@@ -20,10 +23,40 @@ import userFieldsetTemplate from './user-fieldset.tpl.html'; @@ -20,10 +23,40 @@ import userFieldsetTemplate from './user-fieldset.tpl.html';
20 /* eslint-enable import/no-unresolved, import/default */ 23 /* eslint-enable import/no-unresolved, import/default */
21 24
22 /*@ngInject*/ 25 /*@ngInject*/
23 -export default function UserDirective($compile, $templateCache) { 26 +export default function UserDirective($compile, $templateCache, dashboardService) {
24 var linker = function (scope, element) { 27 var linker = function (scope, element) {
25 var template = $templateCache.get(userFieldsetTemplate); 28 var template = $templateCache.get(userFieldsetTemplate);
26 element.html(template); 29 element.html(template);
  30 +
  31 + scope.isCustomerUser = function() {
  32 + return scope.user && scope.user.authority === 'CUSTOMER_USER';
  33 + }
  34 +
  35 + scope.$watch('user', function(newUser, prevUser) {
  36 + if (!angular.equals(newUser, prevUser) && newUser) {
  37 + scope.defaultDashboard = null;
  38 + if (scope.isCustomerUser() && scope.user.additionalInfo &&
  39 + scope.user.additionalInfo.defaultDashboardId) {
  40 + dashboardService.getDashboard(scope.user.additionalInfo.defaultDashboardId).then(
  41 + function(dashboard) {
  42 + scope.defaultDashboard = dashboard;
  43 + }
  44 + )
  45 + }
  46 + }
  47 + });
  48 +
  49 + scope.$watch('defaultDashboard', function(newDashboard, prevDashboard) {
  50 + if (!angular.equals(newDashboard, prevDashboard)) {
  51 + if (scope.isCustomerUser()) {
  52 + if (!scope.user.additionalInfo) {
  53 + scope.user.additionalInfo = {};
  54 + }
  55 + scope.user.additionalInfo.defaultDashboardId = newDashboard ? newDashboard.id.id : null;
  56 + }
  57 + }
  58 + });
  59 +
27 $compile(element.contents())(scope); 60 $compile(element.contents())(scope);
28 } 61 }
29 return { 62 return {