From 45beee3d1b0e6da464744936cc28ea1301422b14 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Tue, 10 Oct 2017 10:15:24 +0200
Subject: [PATCH] draft of user tab in admin panel

---
 frontend-js/.gitignore                        |   1 +
 frontend-js/src/main/css/global.css           | 611 +++++++++---------
 .../src/main/js/gui/admin/EditUserDialog.js   | 313 +++++++++
 .../src/main/js/gui/admin/MapsAdminPanel.js   |  13 +-
 .../src/main/js/gui/admin/UsersAdminPanel.js  | 150 ++++-
 .../test/js/gui/admin/EditUserDialog-test.js  |  35 +
 .../test/js/gui/admin/MapsAdminPanel-test.js  |   8 +-
 .../test/js/gui/admin/UserAdminPanel-test.js  |  53 ++
 8 files changed, 863 insertions(+), 321 deletions(-)
 create mode 100644 frontend-js/src/main/js/gui/admin/EditUserDialog.js
 create mode 100644 frontend-js/src/test/js/gui/admin/EditUserDialog-test.js
 create mode 100644 frontend-js/src/test/js/gui/admin/UserAdminPanel-test.js

diff --git a/frontend-js/.gitignore b/frontend-js/.gitignore
index 2a7fe3fc8f..e6134eefdd 100644
--- a/frontend-js/.gitignore
+++ b/frontend-js/.gitignore
@@ -2,6 +2,7 @@
 .idea/jsLibraryMappings.xml
 .idea/dictionaries/
 .idea/dbnavigator.xml
+.idea/inspectionProfiles/
 
 /dist/
 /coverage/
diff --git a/frontend-js/src/main/css/global.css b/frontend-js/src/main/css/global.css
index 7ba03d1b17..9346429b9c 100644
--- a/frontend-js/src/main/css/global.css
+++ b/frontend-js/src/main/css/global.css
@@ -1,517 +1,516 @@
 .ui-widget-content a {
-	text-decoration: underline;
+    text-decoration: underline;
 }
 
 .minerva-header {
-	background-color: #333333;
-	color: #ffffff;
-	display: block;
+    background-color: #333333;
+    color: #ffffff;
+    display: block;
 }
 
 .minerva-header a:link, .minerva-header a:visited {
-	background-color: #333333;
-	color: #ffffff;
-	line-height: 36px;
-	height: 36px;
-	text-decoration: none;
-	transition: background-color 0.4s ease-in-out 0s;
-	font-size: 13px;
-	font-weight: 900;
-	padding-left: 15px;
+    background-color: #333333;
+    color: #ffffff;
+    line-height: 36px;
+    height: 36px;
+    text-decoration: none;
+    transition: background-color 0.4s ease-in-out 0s;
+    font-size: 13px;
+    font-weight: 900;
+    padding-left: 15px;
 }
 
 .minerva-header a:hover {
-	background-color: #000000;
-	transition: background-color 0.4s ease-in-out 0s;
+    background-color: #000000;
+    transition: background-color 0.4s ease-in-out 0s;
 }
 
 .minerva-checkbox-grid li {
-	display: block;
-	float: left;
-	width: 20%;
+    display: block;
+    float: left;
+    width: 20%;
 }
 
 .minerva-checkbox-grid li .checkbox {
-	margin-top: 2px;
-	margin-bottom: 2px;
+    margin-top: 2px;
+    margin-bottom: 2px;
 }
 
 .minerva-checkbox-grid:after {
-	content: '';
-	display: block;
-	clear: both;
+    content: '';
+    display: block;
+    clear: both;
 }
 
 .mapChartNameDiv {
-	width: 150px;
-	float: left;
-	display: inline-block;
+    width: 150px;
+    float: left;
+    display: inline-block;
 }
 
 .mapChartRowEvenDiv {
-	white-space: nowrap;
-	overflow: hidden;
-	margin: 1px;
-	width: 360px;
-	background-color: #D3D3D3;
+    white-space: nowrap;
+    overflow: hidden;
+    margin: 1px;
+    width: 360px;
+    background-color: #D3D3D3;
 }
 
 .mapChartRowOddDiv {
-	white-space: nowrap;
-	overflow: hidden;
-	margin: 1px;
-	width: 360px;
-	background-color: #C0C0C0;
+    white-space: nowrap;
+    overflow: hidden;
+    margin: 1px;
+    width: 360px;
+    background-color: #C0C0C0;
 }
 
 .canvasDebugClass {
-	border: 0;
-	position: absolute;
-	top: 0;
-	left: 0
+    border: 0;
+    position: absolute;
+    top: 0;
+    left: 0
 }
 
 .menuBelt {
-	width: 100%;
-	min-height: 36px;
-	height: auto;
-	float: left;
-	z-index: -1;
-	background: #13ACE0;
-	background: -moz-linear-gradient(top, #2CCAFE, #13ACE0);
-	background: -webkit-gradient(linear, left top, left bottom, from(#2CCAFE),
-		to(#13ACE0));
+    width: 100%;
+    min-height: 36px;
+    height: auto;
+    float: left;
+    z-index: -1;
+    background: #13ACE0;
+    background: -moz-linear-gradient(top, #2CCAFE, #13ACE0);
+    background: -webkit-gradient(linear, left top, left bottom, from(#2CCAFE),
+    to(#13ACE0));
 }
 
 .headerBelt {
-	width: 100%;
-	min-height: 36px;
-	height: auto;
-	position: absolute;
-	top: 0;
-	left: 0;
-	z-index: -1;
-	background: #13ACE0;
-	background: -moz-linear-gradient(top, #2CCAFE, #13ACE0);
-	background: -webkit-gradient(linear, left top, left bottom, from(#2CCAFE),
-		to(#13ACE0));
+    width: 100%;
+    min-height: 36px;
+    height: auto;
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: -1;
+    background: #13ACE0;
+    background: -moz-linear-gradient(top, #2CCAFE, #13ACE0);
+    background: -webkit-gradient(linear, left top, left bottom, from(#2CCAFE),
+    to(#13ACE0));
 }
 
 .headerHideDivButton {
-	display: inline;
-	width: auto;
-	height: 36px;
-	float: left
+    display: inline;
+    width: auto;
+    height: 36px;
+    float: left
 }
 
 .headerHideButton {
-	color: #FFFFFF;
-	height: 36px;
-	line-height: 36px;
-	padding: 0 16px;
-	margin: 0;
-	border: none;
-	background-color: transparent;
-	font-size: 20px;
-	border-right: 1px solid #9DE1F8;
-	cursor: pointer;
-	transition: background-color 0.4s ease-in-out 0s;
+    color: #FFFFFF;
+    height: 36px;
+    line-height: 36px;
+    padding: 0 16px;
+    margin: 0;
+    border: none;
+    background-color: transparent;
+    font-size: 20px;
+    border-right: 1px solid #9DE1F8;
+    cursor: pointer;
+    transition: background-color 0.4s ease-in-out 0s;
 }
 
 .headerHideButton:hover {
-	background-color: #01536D;
-	transition: background-color 0.4s ease-in-out 0s;
+    background-color: #01536D;
+    transition: background-color 0.4s ease-in-out 0s;
 }
 
 .headerTextBold {
-	display: inline;
-	width: auto;
-	height: 36px;
-	float: left;
-	line-height: 36px;
-	padding: 0 17px;
-	color: #FFFFFF;
-	font-weight: 900;
-	font-size: 13px;
-	text-align: center;
-	border-right: 1px solid #9DE1F8;
+    display: inline;
+    width: auto;
+    height: 36px;
+    float: left;
+    line-height: 36px;
+    padding: 0 17px;
+    color: #FFFFFF;
+    font-weight: 900;
+    font-size: 13px;
+    text-align: center;
+    border-right: 1px solid #9DE1F8;
 }
 
 .minerva-legend {
-	position: absolute;
-	bottom: 10px;
-	right: 10px;
-	box-shadow: 0 3px 20px #999999;
-	border-top: 6px solid #017DA7;
+    position: absolute;
+    bottom: 10px;
+    right: 10px;
+    box-shadow: 0 3px 20px #999999;
+    border-top: 6px solid #017DA7;
 }
 
 .searchPanel {
-	background-color: #FFFFFF;
-	box-shadow: 0 3px 30px #B9B9B9;
-	padding: 15px;
-	margin: 30px;
-	color: #666666;
-	font-size: 13px;
-	font-weight: 900;
+    background-color: #FFFFFF;
+    box-shadow: 0 3px 30px #B9B9B9;
+    padding: 15px;
+    margin: 30px;
+    color: #666666;
+    font-size: 13px;
+    font-weight: 900;
 }
 
 .input-field {
-	background-color: #21BDF1;
-	color: #ffffff;
-	width: 210px;
-	box-shadow: none;
-	-moz-box-shadow: none;
-	-webkit-box-shadow: none;
-	font-size: 14px;
-	font-weight: 900;
-	padding: 8px;
-	font-family: Lato;
-	margin: 0;
-	border: none
+    background-color: #21BDF1;
+    color: #ffffff;
+    width: 210px;
+    box-shadow: none;
+    -moz-box-shadow: none;
+    -webkit-box-shadow: none;
+    font-size: 14px;
+    font-weight: 900;
+    padding: 8px;
+    font-family: Lato;
+    margin: 0;
+    border: none
 }
 
 .chkbox {
-	width: 16px;
-	height: 16px;
-	display: inline-block;
-	-moz-border-radius: 0px;
-	-webkit-border-radius: 0px;
-	border-radius: 0px;
+    width: 16px;
+    height: 16px;
+    display: inline-block;
+    -moz-border-radius: 0px;
+    -webkit-border-radius: 0px;
+    border-radius: 0px;
 }
 
 .result-table {
-	width: 100%;
-	border-spacing: 2px;
-	-webkit-border-horizontal-spacing: 2px;
-	-webkit-border-vertical-spacing: 2px;
+    width: 100%;
+    border-spacing: 2px;
+    -webkit-border-horizontal-spacing: 2px;
+    -webkit-border-vertical-spacing: 2px;
 }
 
 .mapClass {
-	position: absolute;
-	top: 36px;
-	bottom: 0;
-	right: 0;
-	left: 0;
-	border: none;
-	border-left: 1px solid #e1e1e1;
+    position: absolute;
+    top: 36px;
+    bottom: 0;
+    right: 0;
+    left: 0;
+    border: none;
+    border-left: 1px solid #e1e1e1;
 }
 
 .footerLinks {
-	position: absolute;
-	left: 0;
-	right: 0;
-	bottom: 0;
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
 }
 
 .footerLinks {
-	background-color: #333333;
-	color: #ffffff;
-	display: block;
-	float: left;
-	line-height: 35px;
-	height: 35px;
-	z-index: 10000
+    background-color: #333333;
+    color: #ffffff;
+    display: block;
+    float: left;
+    line-height: 35px;
+    height: 35px;
+    z-index: 10000
 }
 
 .footerLinks a:link, .footerLinks a:visited {
-	text-decoration: none;
-	font-size: 13px;
-	font-weight: 900;
-	color: #ffffff;
-	transition: all 0.4s ease-in-out 0s;
+    text-decoration: none;
+    font-size: 13px;
+    font-weight: 900;
+    color: #ffffff;
+    transition: all 0.4s ease-in-out 0s;
 }
 
 .footerLinks a:hover {
-	color: #999999;
-	transition: all 0.4s ease-in-out 0s;
+    color: #999999;
+    transition: all 0.4s ease-in-out 0s;
 }
 
 /* twitter typeahead */
 .tt-query /* UPDATE: newer versions use tt-input instead of tt-query */
-	{
-	width: 396px;
-	height: 30px;
-	padding: 8px 12px;
-	font-size: 24px;
-	line-height: 30px;
-	border: 2px solid #ccc;
-	border-radius: 8px;
-	outline: none;
+{
+    width: 396px;
+    height: 30px;
+    padding: 8px 12px;
+    font-size: 24px;
+    line-height: 30px;
+    border: 2px solid #ccc;
+    border-radius: 8px;
+    outline: none;
 }
 
 .tt-query {
-	/* UPDATE: newer versions use tt-input instead of tt-query */
-	box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+    /* UPDATE: newer versions use tt-input instead of tt-query */
+    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
 }
 
 .tt-hint {
-	color: #999;
+    color: #999;
 }
 
 .tt-menu {
-	/* UPDATE: newer versions use tt-menu instead of tt-dropdown-menu */
-	width: 222px;
-	margin-top: 12px;
-	padding: 8px 0;
-	background-color: #fff;
-	border: 1px solid #ccc;
-	border: 1px solid rgba(0, 0, 0, 0.2);
-	border-radius: 8px;
-	box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+    /* UPDATE: newer versions use tt-menu instead of tt-dropdown-menu */
+    width: 222px;
+    margin-top: 12px;
+    padding: 8px 0;
+    background-color: #fff;
+    border: 1px solid #ccc;
+    border: 1px solid rgba(0, 0, 0, 0.2);
+    border-radius: 8px;
+    box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
 }
 
 .tt-suggestion {
-	padding: 3px 20px;
-	line-height: 24px;
+    padding: 3px 20px;
+    line-height: 24px;
 }
 
 .tt-suggestion:hover {
-	cursor: pointer;
-	color: #fff;
-	background-color: #0097cf;
+    cursor: pointer;
+    color: #fff;
+    background-color: #0097cf;
 }
 
 .tt-suggestion.tt-cursor {
-	color: #fff;
-	background-color: #0097cf;
+    color: #fff;
+    background-color: #0097cf;
 }
 
 .dropdown-submenu {
-	position: relative;
+    position: relative;
 }
 
-.dropdown-submenu>.dropdown-menu {
-	top: 0;
-	left: 100%;
-	margin-top: -6px;
-	margin-left: -1px;
-	-webkit-border-radius: 0 6px 6px 6px;
-	-moz-border-radius: 0 6px 6px 6px;
-	border-radius: 0 6px 6px 6px;
+.dropdown-submenu > .dropdown-menu {
+    top: 0;
+    left: 100%;
+    margin-top: -6px;
+    margin-left: -1px;
+    -webkit-border-radius: 0 6px 6px 6px;
+    -moz-border-radius: 0 6px 6px 6px;
+    border-radius: 0 6px 6px 6px;
 }
 
-.dropdown-submenu:hover>.dropdown-menu {
-	display: block;
+.dropdown-submenu:hover > .dropdown-menu {
+    display: block;
 }
 
-.dropdown-submenu>a:after {
-	display: block;
-	content: " ";
-	float: right;
-	width: 0;
-	height: 0;
-	border-color: transparent;
-	border-style: solid;
-	border-width: 5px 0 5px 5px;
-	border-left-color: #cccccc;
-	margin-top: 5px;
-	margin-right: -10px;
+.dropdown-submenu > a:after {
+    display: block;
+    content: " ";
+    float: right;
+    width: 0;
+    height: 0;
+    border-color: transparent;
+    border-style: solid;
+    border-width: 5px 0 5px 5px;
+    border-left-color: #cccccc;
+    margin-top: 5px;
+    margin-right: -10px;
 }
 
-.dropdown-submenu:hover>a:after {
-	border-left-color: #ffffff;
+.dropdown-submenu:hover > a:after {
+    border-left-color: #ffffff;
 }
 
 .dropdown-submenu.pull-left {
-	float: none;
+    float: none;
 }
 
-.dropdown-submenu.pull-left>.dropdown-menu {
-	left: -100%;
-	margin-left: 10px;
-	-webkit-border-radius: 6px 0 6px 6px;
-	-moz-border-radius: 6px 0 6px 6px;
-	border-radius: 6px 0 6px 6px;
+.dropdown-submenu.pull-left > .dropdown-menu {
+    left: -100%;
+    margin-left: 10px;
+    -webkit-border-radius: 6px 0 6px 6px;
+    -moz-border-radius: 6px 0 6px 6px;
+    border-radius: 6px 0 6px 6px;
 }
 
 .dual-listbox .dual-listbox__search {
-	display: none;
+    display: none;
 }
 
 .dual-listbox .dual-listbox__title {
-	padding: 5px 2px;
+    padding: 5px 2px;
 }
 
 .dual-listbox .dual-listbox__item {
-	padding: 2px;
+    padding: 2px;
 }
 
 .dual-listbox .dual-listbox__button {
-	background-color: #999;
+    background-color: #999;
 }
 
 .minerva-multi-select-special {
-	background-color: #00FF00;
+    background-color: #00FF00;
 }
 
 .dual-listbox__item--selected .minerva-multi-select-special {
-	background-color: rgba(8, 157, 227, .7);
+    background-color: rgba(8, 157, 227, .7);
 }
 
-.dual-listbox .dual-listbox__available, .dual-listbox .dual-listbox__selected
-	{
-	width: 200px;
-	height: 200px;
+.dual-listbox .dual-listbox__available, .dual-listbox .dual-listbox__selected {
+    width: 200px;
+    height: 200px;
 }
 
 .minerva-export-dual-listbox-container {
-	padding: 10px;
-	float: left;
+    padding: 10px;
+    float: left;
 }
 
 table.minerva-publication-table td {
-	padding: 0px;
+    padding: 0px;
 }
 
 input.minerva-input-text, input.minerva-input-password {
-	background-color: #21BDF1;
-	color: #ffffff;
-	width: 210px;
-	box-shadow: none;
-	-moz-box-shadow: none;
-	-webkit-box-shadow: none;
-	font-size: 14px;
-	font-weight: 900;
-	padding: 8px;
-	font-family: Lato;
-	margin: 0;
-	border: none
+    background-color: #21BDF1;
+    color: #ffffff;
+    width: 210px;
+    box-shadow: none;
+    -moz-box-shadow: none;
+    -webkit-box-shadow: none;
+    font-size: 14px;
+    font-weight: 900;
+    padding: 8px;
+    font-family: Lato;
+    margin: 0;
+    border: none
 }
 
 .minerva-window-drug-table {
-	border-collapse: collapse;
+    border-collapse: collapse;
 }
 
 table.minerva-window-drug-table, table.minerva-window-drug-table th,
-	table.minerva-window-drug-table td {
-	padding: 2px;
-	border: 1px solid black;
+table.minerva-window-drug-table td {
+    padding: 2px;
+    border: 1px solid black;
 }
 
 .minerva-left-panel {
-	display: table-cell;
-	position: relative;
-	float: left;
-	border: none;
-	background-color: #ffffff;
-	width: 357px;
-	height: 100%;
+    display: table-cell;
+    position: relative;
+    float: left;
+    border: none;
+    background-color: #ffffff;
+    width: 357px;
+    height: 100%;
 }
 
 .minerva-left-panel .minerva-help-button {
-	position: absolute;
-	top: 35px;
-	right: 35px;
-	width: 18px;
+    position: absolute;
+    top: 35px;
+    right: 35px;
+    width: 18px;
 }
 
-.minerva-left-panel .minerva-label, .minerva-element-info-div .minerva-label
-	{
-	font-weight: 900;
-	line-height: 24px;
+.minerva-left-panel .minerva-label, .minerva-element-info-div .minerva-label {
+    font-weight: 900;
+    line-height: 24px;
 }
 
 .minerva-annotation-row-odd {
-	padding: 5px;
-	background-color: #EAEAEA;
+    padding: 5px;
+    background-color: #EAEAEA;
 }
 
 .minerva-annotation-row-even {
-	padding: 5px;
-	background-color: #ffffff;
+    padding: 5px;
+    background-color: #ffffff;
 }
 
 .minerva-overview-button {
-	color: #FFFFFF;
-	height: 36px;
-	line-height: 35px;
-	padding: 0 18px;
-	margin: 0;
-	border: none;
-	background-color: #017DA7;
-	font-size: 13px;
-	font-weight: 900;
-	border-right: 1px solid #9DE1F8;
-	cursor: pointer;
-	transition: background-color 0.4s ease-in-out 0s;
+    color: #FFFFFF;
+    height: 36px;
+    line-height: 35px;
+    padding: 0 18px;
+    margin: 0;
+    border: none;
+    background-color: #017DA7;
+    font-size: 13px;
+    font-weight: 900;
+    border-right: 1px solid #9DE1F8;
+    cursor: pointer;
+    transition: background-color 0.4s ease-in-out 0s;
 }
 
 .minerva-overview-button:hover {
-	background-color: #01536D;
-	transition: background-color 0.4s ease-in-out 0s;
+    background-color: #01536D;
+    transition: background-color 0.4s ease-in-out 0s;
 }
 
 .minerva-top-checkbox-div {
-	height: 36px;
-	vertical-align: top;
-	font-size: 13px;
-	font-weight: 900;
-	color: #ffffff;
-	display: inline;
-	width: auto;
-	float: left;
-	text-align: left;
-	padding: 0 0 0 15px;
-	margin: 0;
+    height: 36px;
+    vertical-align: top;
+    font-size: 13px;
+    font-weight: 900;
+    color: #ffffff;
+    display: inline;
+    width: auto;
+    float: left;
+    text-align: left;
+    padding: 0 0 0 15px;
+    margin: 0;
 }
 
 .minerva-top-checkbox-div label {
-	padding: 0 15px 0 5px;
-	font-size: 11px;
-	line-height: 35px;
-	display: inline;
-	float: left;
+    padding: 0 15px 0 5px;
+    font-size: 11px;
+    line-height: 35px;
+    display: inline;
+    float: left;
 }
 
 .minerva-top-checkbox-div input {
-	margin-top: 12px;
-	display: inline;
-	float: left;
+    margin-top: 12px;
+    display: inline;
+    float: left;
 }
 
 .minerva-overlay-dialog div[style*="display: table-cell"] {
-	padding: 2px;
-	vertical-align: top;
+    padding: 2px;
+    vertical-align: top;
 }
 
 .minerva-open-submap-button {
-	padding: 2px;
-	margin: 2px;
+    padding: 2px;
+    margin: 2px;
 }
 
 h1 {
-	font-size: 22px;
-	color: #999999;
-	text-align: center;
-	padding-bottom: 10px;
-	font-weight: 400;
+    font-size: 22px;
+    color: #999999;
+    text-align: center;
+    padding-bottom: 10px;
+    font-weight: 400;
 }
 
 .minerva-projects-tab .minerva-menu-row button,
-	.minerva-edit-project-dialog .minerva-menu-row button {
-	margin: 5px;
+.minerva-edit-project-dialog .minerva-menu-row button,
+.minerva-users-tab .minerva-menu-row button {
+    margin: 5px;
 }
 
 .minerva-no-close .ui-dialog-titlebar-close {
-	display: none
+    display: none
 }
 
 .ui-icon {
-	display: inline-block;
+    display: inline-block;
 }
 
 .ui-icon-refresh {
-	background: url(images/icons/refresh.png.xhtml) 50% 50% no-repeat;
+    background: url(images/icons/refresh.png.xhtml) 50% 50% no-repeat;
 }
 
-.nav>li>a {
-	text-decoration: none;
+.nav > li > a {
+    text-decoration: none;
 }
 
 .minerva-edit-project-dialog div[style*="display: table-cell"] {
-	padding: 2px;
+    padding: 2px;
 }
 
 .minerva-datatable-toolbar {
-	float: left;
+    float: left;
 }
\ No newline at end of file
diff --git a/frontend-js/src/main/js/gui/admin/EditUserDialog.js b/frontend-js/src/main/js/gui/admin/EditUserDialog.js
new file mode 100644
index 0000000000..f14ee87ede
--- /dev/null
+++ b/frontend-js/src/main/js/gui/admin/EditUserDialog.js
@@ -0,0 +1,313 @@
+"use strict";
+
+/* exported logger */
+var Promise = require("bluebird");
+
+var AbstractGuiElement = require('../AbstractGuiElement');
+var GuiConnector = require('../../GuiConnector');
+
+var Functions = require('../../Functions');
+var logger = require('../../logger');
+
+var guiUtils = new (require('../leftPanel/GuiUtils'))();
+
+function EditUserDialog(params) {
+  AbstractGuiElement.call(this, params);
+  var self = this;
+  self.setUser(params.user);
+
+  self.createGui();
+}
+
+EditUserDialog.prototype = Object.create(AbstractGuiElement.prototype);
+EditUserDialog.prototype.constructor = EditUserDialog;
+
+EditUserDialog.prototype.setUser = function (user) {
+  this._user = user;
+};
+EditUserDialog.prototype.getUser = function () {
+  return this._user;
+};
+
+EditUserDialog.prototype.createGui = function () {
+  var self = this;
+  var element = self.getElement();
+
+  var tabDiv = Functions.createElement({
+    type: "div",
+    name: "tabView",
+    className: "tabbable boxed parentTabs"
+  });
+  element.appendChild(tabDiv);
+
+  var tabMenuDiv = Functions.createElement({
+    type: "ul",
+    className: "nav nav-tabs"
+  });
+  tabDiv.appendChild(tabMenuDiv);
+
+  var tabContentDiv = Functions.createElement({
+    type: "div",
+    className: "tab-content"
+  });
+  tabDiv.appendChild(tabContentDiv);
+
+  self.createGeneralTab(tabMenuDiv, tabContentDiv);
+  self.createProjectsTab(tabMenuDiv, tabContentDiv);
+};
+
+EditUserDialog.prototype.createGeneralTab = function (tabMenuDiv, tabContentDiv) {
+  var self = this;
+  self.addTab({
+    tabMenuDiv: tabMenuDiv,
+    tabContentDiv: tabContentDiv,
+    name: "GENERAL",
+    id: self.getUser().getLogin() + "_general_tab",
+    content: self.createGeneralTabContent()
+  });
+
+};
+
+EditUserDialog.prototype.addTab = function (params) {
+  var navLi = guiUtils.createTabMenuObject({
+    id: params.id,
+    name: params.name,
+    navigationBar: params.tabMenuDiv
+  });
+  params.tabMenuDiv.appendChild(navLi);
+
+  var contentDiv = guiUtils.createTabContentObject({
+    id: params.id,
+    navigationObject: navLi,
+    navigationBar: params.tabMenuDiv
+  });
+
+  if (params.content !== undefined) {
+    contentDiv.appendChild(params.content);
+  }
+
+  params.tabContentDiv.appendChild(contentDiv);
+};
+
+EditUserDialog.prototype.createGeneralTabContent = function () {
+  var self = this;
+  var user = self.getUser();
+
+  var result = new Functions.createElement({
+    type: "div"
+  });
+
+  var table = new Functions.createElement({
+    type: "div",
+    style: "display:table"
+  });
+  result.appendChild(table);
+
+  var loginRow = new Functions.createElement({
+    type: "div",
+    style: "display:table-row"
+  });
+  table.appendChild(loginRow);
+  loginRow.appendChild(new Functions.createElement({
+    type: "div",
+    style: "display:table-cell",
+    content: "Login"
+  }));
+  loginRow.appendChild(new Functions.createElement({
+    type: "div",
+    style: "display:table-cell",
+    content: user.getLogin()
+  }));
+
+
+  var menuRow = Functions.createElement({
+    type: "div",
+    className: "minerva-menu-row",
+    style: "display:table-row; margin:10px"
+  });
+  result.appendChild(menuRow);
+
+  var saveUserButton = Functions.createElement({
+    type: "button",
+    name: "saveUser",
+    content: '<span class="ui-icon ui-icon-disk"></span>&nbsp;SAVE',
+    onclick: function () {
+      return self.onSaveClicked().then(function () {
+        return self.close();
+      }, GuiConnector.alert);
+    }
+  });
+  var cancelButton = Functions.createElement({
+    type: "button",
+    name: "cancelProject",
+    content: '<span class="ui-icon ui-icon-cancel"></span>&nbsp;CANCEL',
+    onclick: function () {
+      return self.close();
+    }
+  });
+  menuRow.appendChild(saveUserButton);
+  menuRow.appendChild(cancelButton);
+
+  return result;
+
+};
+
+EditUserDialog.prototype.createProjectsTab = function (tabMenuDiv, tabContentDiv) {
+  var self = this;
+  self.addTab({
+    tabMenuDiv: tabMenuDiv,
+    tabContentDiv: tabContentDiv,
+    name: "PROJECTS",
+    id: self.getProject().getProjectId() + "_projects_tab",
+    content: self.createProjectsTabContent()
+  });
+};
+
+EditUserDialog.prototype.createProjectsTabContent = function () {
+  var self = this;
+  var result = Functions.createElement({
+    type: "div"
+  });
+  result.appendChild(self._createProjectsTable());
+  return result;
+};
+
+EditUserDialog.prototype._createProjectsTable = function () {
+  var result = Functions.createElement({
+    type: "div",
+    style: "margin-top:10px;"
+  });
+
+  var projectsTable = Functions.createElement({
+    type: "table",
+    name: "projectsTable",
+    className: "display",
+    style: "width:100%"
+  });
+  result.appendChild(projectsTable);
+
+  return result;
+};
+
+EditUserDialog.prototype.init = function () {
+  var self = this;
+  return self.initProjectsTab().then(function () {
+    return self.refreshProjects();
+  }).then(function () {
+    $(window).trigger('resize');
+  });
+};
+
+EditUserDialog.prototype.initProjectsTab = function () {
+  var self = this;
+
+  var usersTable = $("[name=projectsTable]", self.getElement())[0];
+
+  return self.createUserPrivilegeColumns().then(function (columns) {
+    $(usersTable).DataTable({
+      columns: columns
+    });
+  });
+};
+
+EditUserDialog.prototype.refreshProjects = function () {
+  var self = this;
+  return ServerConnector.getProjects().then(function (projects) {
+    return self.setProjects(projects);
+  });
+};
+
+EditUserDialog.prototype.setProjects = function (projects) {
+  var self = this;
+  self._userByLogin = [];
+  return self.createUserPrivilegeColumns().then(function (columns) {
+    var dataTable = $($("[name='projectsTable']", self.getElement())[0]).DataTable();
+    var data = [];
+    for (var i = 0; i < projects.length; i++) {
+      var project = projects[i];
+      var rowData = self.projectToTableRow(project, columns);
+      data.push(rowData);
+    }
+    dataTable.clear().rows.add(data).draw();
+  });
+};
+
+EditUserDialog.prototype.projectToTableRow = function (project, columns) {
+  var row = [];
+
+  row[0] = project.getProjectId();
+
+  for (var i = 1; i < columns.length; i++) {
+    row.push("");
+  }
+
+  return row;
+};
+
+
+EditUserDialog.prototype.destroy = function () {
+  var self = this;
+  var div = self.getElement();
+  var usersTable = $("[name=projectsTable]", self.getElement())[0];
+  if ($.fn.DataTable.isDataTable(usersTable)) {
+    $(usersTable).DataTable().destroy();
+  }
+
+  if ($(div).hasClass("ui-dialog-content")) {
+    $(div).dialog("destroy");
+  }
+};
+
+EditUserDialog.prototype.open = function () {
+  var self = this;
+  var div = self.getElement();
+  if (!$(div).hasClass("ui-dialog-content")) {
+    $(div).dialog({
+      title: self.getProject().getProjectId(),
+      width: window.innerWidth / 2,
+      height: window.innerHeight / 2
+    });
+  }
+  $(div).dialog("open");
+};
+
+EditUserDialog.prototype.onSaveClicked = function () {
+  var self = this;
+  var user = self.getUser();
+
+  return ServerConnector.updateUser(user);
+};
+
+EditUserDialog.prototype.close = function () {
+  var self = this;
+  $(self.getElement()).dialog("close");
+};
+
+EditUserDialog.prototype.createUserPrivilegeColumns = function () {
+  var self = this;
+
+  if (self._userPrivilegeColumns !== undefined) {
+    return Promise.resolve(self._userPrivilegeColumns);
+  }
+
+  return ServerConnector.getConfiguration().then(function (configuration) {
+    self._userPrivilegeColumns = [{
+      title: "ProjectId"
+    }];
+    var privilegeTypes = configuration.getPrivilegeTypes();
+    for (var i = 0; i < privilegeTypes.length; i++) {
+      var type = privilegeTypes[i];
+      if (type.getObjectType() === "Project") {
+        self._userPrivilegeColumns.push({
+          "title": type.getCommonName(),
+          privilegeType: type
+        });
+      }
+    }
+    return self._userPrivilegeColumns;
+  });
+
+};
+
+
+module.exports = EditUserDialog;
diff --git a/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js b/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js
index 05cc7467e2..0508cd356e 100644
--- a/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js
+++ b/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js
@@ -6,6 +6,7 @@ var AbstractAdminPanel = require('./AbstractAdminPanel');
 var AddProjectDialog = require('./AddProjectDialog');
 var EditProjectDialog = require('./EditProjectDialog');
 
+// noinspection JSUnusedLocalSymbols
 var logger = require('../../logger');
 
 var Functions = require('../../Functions');
@@ -27,13 +28,13 @@ MapsAdminPanel.prototype.constructor = MapsAdminPanel;
 MapsAdminPanel.prototype._createGui = function () {
   var self = this;
   var projectsDiv = Functions.createElement({
-    type: "div",
+    type: "div"
   });
   self.getElement().appendChild(projectsDiv);
 
   var dataDiv = Functions.createElement({
     type: "div",
-    style: "display:table; width:100%",
+    style: "display:table; width:100%"
   });
   projectsDiv.appendChild(dataDiv);
 
@@ -48,7 +49,7 @@ MapsAdminPanel.prototype._createMenuRow = function () {
   var menuRow = Functions.createElement({
     type: "div",
     className: "minerva-menu-row",
-    style: "display:table-row; margin:10px",
+    style: "display:table-row; margin:10px"
   });
 
   var addProjectButton = Functions.createElement({
@@ -57,7 +58,7 @@ MapsAdminPanel.prototype._createMenuRow = function () {
     content: '<span class="ui-icon ui-icon-circle-plus"></span>&nbsp;ADD PROJECT',
     onclick: function () {
       return self.onAddClicked().then(null, GuiConnector.alert);
-    },
+    }
   });
   var refreshButton = Functions.createElement({
     type: "button",
@@ -210,7 +211,7 @@ MapsAdminPanel.prototype.onAddClicked = function () {
       element: Functions.createElement({
         type: "div"
       }),
-      customMap: null,
+      customMap: null
     });
     self._addDialog = dialog;
     return dialog.init().then(function () {
@@ -255,7 +256,7 @@ MapsAdminPanel.prototype.getDialog = function (project) {
         type: "div"
       }),
       project: project,
-      customMap: null,
+      customMap: null
     });
     self._dialogs[project.getProjectId()] = dialog;
     return dialog.init().then(function () {
diff --git a/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js b/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
index d716fa2cfa..c8951c68c9 100644
--- a/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
+++ b/frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
@@ -1,22 +1,160 @@
 "use strict";
 
-/* exported logger */
-/* exported Promise*/
-
 var AbstractAdminPanel = require('./AbstractAdminPanel');
 
-var Promise = require("bluebird");
+var Functions = require('../../Functions');
+var GuiConnector = require('../../GuiConnector');
 
 function UsersAdminPanel(params) {
-  AbstractAdminPanel.call(this, params);
+  var self = this;
+  AbstractAdminPanel.call(self, params);
+  self._createGui();
+  $(self.getElement()).addClass("minerva-users-tab");
 
 }
 
 UsersAdminPanel.prototype = Object.create(AbstractAdminPanel.prototype);
 UsersAdminPanel.prototype.constructor = UsersAdminPanel;
 
-UsersAdminPanel.prototype.init = function() {
+UsersAdminPanel.prototype._createGui = function () {
+  var self = this;
+  var usersDiv = Functions.createElement({
+    type: "div"
+  });
+  self.getElement().appendChild(usersDiv);
+
+  var dataDiv = Functions.createElement({
+    type: "div",
+    style: "display:table; width:100%"
+  });
+  usersDiv.appendChild(dataDiv);
+
+  dataDiv.appendChild(self._createMenuRow());
+  dataDiv.appendChild(self._createUsersTableRow());
+  dataDiv.appendChild(self._createMenuRow());
+
+};
+
+UsersAdminPanel.prototype._createMenuRow = function () {
+  var self = this;
+  var menuRow = Functions.createElement({
+    type: "div",
+    className: "minerva-menu-row",
+    style: "display:table-row; margin:10px"
+  });
+
+  var addUserButton = Functions.createElement({
+    type: "button",
+    name: "addProject",
+    content: '<span class="ui-icon ui-icon-circle-plus"></span>&nbsp;ADD USER',
+    onclick: function () {
+      return self.onAddClicked().then(null, GuiConnector.alert);
+    }
+  });
+  var refreshButton = Functions.createElement({
+    type: "button",
+    name: "refreshProject",
+    content: '<span class="ui-icon ui-icon-refresh"></span>&nbsp;REFRESH',
+    onclick: function () {
+      return self.onRefreshClicked().then(null, GuiConnector.alert);
+    }
+  });
+  menuRow.appendChild(addUserButton);
+  menuRow.appendChild(refreshButton);
+  return menuRow;
+};
+
+UsersAdminPanel.prototype._createUsersTableRow = function () {
+  var self = this;
+  var projectsRow = Functions.createElement({
+    type: "div",
+    style: "display:table-row; width:100%"
+  });
+
+  var usersTable = Functions.createElement({
+    type: "table",
+    name: "usersTable",
+    className: "display",
+    style: "width:100%"
+  });
+  projectsRow.appendChild(usersTable);
+
+  $(usersTable).DataTable({
+    columns: [{
+      title: 'Login'
+    }, {
+      title: 'Name'
+    }, {
+      title: 'Surname'
+    }, {
+      title: 'Email'
+    }, {
+      title: 'Edit'
+    }, {
+      title: 'Remove'
+    }]
+  });
+  $(usersTable).on("click", "[name='removeProject']", function () {
+    var button = this;
+    return self.removeProject($(button).attr("data")).then(null, GuiConnector.alert);
+  });
+
+  $(usersTable).on("click", "[name='showEditDialog']", function () {
+    var button = this;
+    return self.showEditDialog($(button).attr("data")).then(null, GuiConnector.alert);
+  });
+
+  return projectsRow;
+};
+
+UsersAdminPanel.prototype.init = function () {
+  var self = this;
+  return ServerConnector.getUsers().then(function (users) {
+    return self.setUsers(users);
+  });
+};
+
+UsersAdminPanel.prototype.setUsers = function (users) {
+  var self = this;
+  var dataTable = $($("[name='usersTable']", self.getElement())[0]).DataTable();
+  var data = [];
+  for (var i = 0; i < users.length; i++) {
+    var user = users[i];
+    var rowData = self.userToTableRow(user);
+    data.push(rowData);
+  }
+  dataTable.clear().rows.add(data).draw();
+};
+
+UsersAdminPanel.prototype.userToTableRow = function (user, row) {
+  if (row === undefined) {
+    row = [];
+  }
+
+  row[0] = user.getLogin();
+  row[1] = user.getName();
+  row[2] = user.getSurname();
+  row[3] = user.getEmail();
+  row[4] = "<button name='showEditDialog' data='" + user.getLogin() + "'>EDIT</button>";
+  row[5] = "<button name='removeUser' data='" + user.getLogin() + "'>REMOVE</button>";
+
+  return row;
+};
+
+UsersAdminPanel.prototype.onRefreshClicked = function () {
+  var self = this;
+  return ServerConnector.getUsers(true).then(function (users) {
+    return self.setUsers(users);
+  });
+};
+
 
+UsersAdminPanel.prototype.destroy = function () {
+  var self = this;
+  var table = $("[name='usersTable']", self.getElement())[0];
+  if ($.fn.DataTable.isDataTable(table)) {
+    $(table).DataTable().destroy();
+  }
 };
 
 module.exports = UsersAdminPanel;
diff --git a/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js b/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js
new file mode 100644
index 0000000000..f249501a53
--- /dev/null
+++ b/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js
@@ -0,0 +1,35 @@
+"use strict";
+
+/* exported assert */
+/* exported logger */
+require("../../mocha-config");
+
+var EditUserDialog = require('../../../../main/js/gui/admin/EditUserDialog');
+var User = require('../../../../main/js/map/data/User');
+var logger = require('../../logger');
+
+var assert = require('assert');
+
+describe('EditUserDialog', function () {
+
+  describe('init', function () {
+    it('empty user', function () {
+      var dialog;
+      var project;
+      var user = new User({});
+      return ServerConnector.getProject().then(function (result) {
+        project = result;
+        dialog = new EditUserDialog({
+          element: testDiv,
+          project: project,
+          user: user,
+          customMap: null
+        });
+        return dialog.init();
+      }).then(function () {
+        dialog.destroy();
+      });
+    });
+  });
+
+});
diff --git a/frontend-js/src/test/js/gui/admin/MapsAdminPanel-test.js b/frontend-js/src/test/js/gui/admin/MapsAdminPanel-test.js
index 0517e10f52..36ba521fc3 100644
--- a/frontend-js/src/test/js/gui/admin/MapsAdminPanel-test.js
+++ b/frontend-js/src/test/js/gui/admin/MapsAdminPanel-test.js
@@ -6,8 +6,10 @@
 require("../../mocha-config");
 
 var MapsAdminPanel = require('../../../../main/js/gui/admin/MapsAdminPanel');
+// noinspection JSUnusedLocalSymbols
 var logger = require('../../logger');
 
+// noinspection JSUnusedLocalSymbols
 var assert = require('assert');
 
 describe('MapsAdminPanel', function () {
@@ -22,7 +24,7 @@ describe('MapsAdminPanel', function () {
       mapTab = new MapsAdminPanel({
         element: testDiv,
         project: project,
-        configuration: configuration,
+        configuration: configuration
       });
       return mapTab.init();
     }).then(function () {
@@ -43,7 +45,7 @@ describe('MapsAdminPanel', function () {
         mapTab = new MapsAdminPanel({
           element: testDiv,
           project: project,
-          configuration: configuration,
+          configuration: configuration
         });
         return mapTab.init();
       }).then(function () {
@@ -62,7 +64,7 @@ describe('MapsAdminPanel', function () {
         mapTab = new MapsAdminPanel({
           element: testDiv,
           project: project,
-          configuration: configuration,
+          configuration: configuration
         });
         return mapTab.init();
       }).then(function () {
diff --git a/frontend-js/src/test/js/gui/admin/UserAdminPanel-test.js b/frontend-js/src/test/js/gui/admin/UserAdminPanel-test.js
new file mode 100644
index 0000000000..6180140bef
--- /dev/null
+++ b/frontend-js/src/test/js/gui/admin/UserAdminPanel-test.js
@@ -0,0 +1,53 @@
+"use strict";
+
+/* exported logger */
+/* exported assert */
+
+require("../../mocha-config");
+
+var UsersAdminPanel = require('../../../../main/js/gui/admin/UsersAdminPanel');
+var logger = require('../../logger');
+
+var assert = require('assert');
+
+describe('UsersAdminPanel', function () {
+
+  it('init', function () {
+    var usersTab;
+    var project;
+    return ServerConnector.getProject().then(function (result) {
+      project = result;
+      return ServerConnector.getConfiguration();
+    }).then(function (configuration) {
+      usersTab = new UsersAdminPanel({
+        element: testDiv,
+        project: project,
+        configuration: configuration
+      });
+      return usersTab.init();
+    }).then(function () {
+      return usersTab.destroy();
+    });
+  });
+
+  it('refresh', function () {
+    var mapTab;
+    var project;
+    return ServerConnector.getProject().then(function (result) {
+      project = result;
+      return ServerConnector.getConfiguration();
+    }).then(function (configuration) {
+      mapTab = new UsersAdminPanel({
+        element: testDiv,
+        project: project,
+        configuration: configuration
+      });
+      return mapTab.init();
+    }).then(function () {
+      return mapTab.onRefreshClicked();
+    }).then(function () {
+      return mapTab.destroy();
+    });
+  });
+
+});
-- 
GitLab