mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-24 07:37:55 +01:00
Format CSS and TSX files.
This commit is contained in:
parent
0f4a8ee087
commit
ae02780a1a
@ -13,7 +13,7 @@
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
cursor: crosshair;
|
||||
background-image: url("../img/bootstrap-colorpicker/saturation.png");
|
||||
background-image: url('../img/bootstrap-colorpicker/saturation.png');
|
||||
}
|
||||
|
||||
.colorpicker-saturation i {
|
||||
@ -26,8 +26,8 @@
|
||||
margin: -4px 0 0 -4px;
|
||||
border: 1px solid #000;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.colorpicker-saturation i b {
|
||||
@ -36,8 +36,8 @@
|
||||
height: 5px;
|
||||
border: 1px solid #fff;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.colorpicker-hue,
|
||||
@ -64,12 +64,12 @@
|
||||
}
|
||||
|
||||
.colorpicker-hue {
|
||||
background-image: url("../img/bootstrap-colorpicker/hue.png");
|
||||
background-image: url('../img/bootstrap-colorpicker/hue.png');
|
||||
}
|
||||
|
||||
.colorpicker-alpha {
|
||||
display: none;
|
||||
background-image: url("../img/bootstrap-colorpicker/alpha.png");
|
||||
background-image: url('../img/bootstrap-colorpicker/alpha.png');
|
||||
}
|
||||
|
||||
.colorpicker {
|
||||
@ -80,8 +80,8 @@
|
||||
padding: 4px;
|
||||
margin-top: 1px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
.colorpicker:after {
|
||||
display: table;
|
||||
line-height: 0;
|
||||
content: "";
|
||||
content: '';
|
||||
}
|
||||
|
||||
.colorpicker:after {
|
||||
@ -135,7 +135,7 @@
|
||||
height: 10px;
|
||||
margin-top: 5px;
|
||||
clear: both;
|
||||
background-image: url("../img/bootstrap-colorpicker/alpha.png");
|
||||
background-image: url('../img/bootstrap-colorpicker/alpha.png');
|
||||
background-position: 0 100%;
|
||||
}
|
||||
|
||||
@ -194,11 +194,11 @@
|
||||
}
|
||||
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-hue {
|
||||
background-image: url("../img/bootstrap-colorpicker/hue-horizontal.png");
|
||||
background-image: url('../img/bootstrap-colorpicker/hue-horizontal.png');
|
||||
}
|
||||
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-alpha {
|
||||
background-image: url("../img/bootstrap-colorpicker/alpha-horizontal.png");
|
||||
background-image: url('../img/bootstrap-colorpicker/alpha-horizontal.png');
|
||||
}
|
||||
|
||||
.colorpicker.colorpicker-hidden {
|
||||
@ -211,4 +211,4 @@
|
||||
|
||||
.colorpicker-inline.colorpicker-visible {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,180 @@
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
*
|
||||
*/.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("../img/bootstrap-colorpicker/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("../img/bootstrap-colorpicker/hue.png")}.colorpicker-alpha{display:none;background-image:url("../img/bootstrap-colorpicker/alpha.png")}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("../img/bootstrap-colorpicker/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-element .input-group-addon i{display:block;width:16px;height:16px;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("../img/bootstrap-colorpicker/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("../img/bootstrap-colorpicker/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}
|
||||
*/
|
||||
.colorpicker-saturation {
|
||||
float: left;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
cursor: crosshair;
|
||||
background-image: url('../img/bootstrap-colorpicker/saturation.png');
|
||||
}
|
||||
.colorpicker-saturation i {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
margin: -4px 0 0 -4px;
|
||||
border: 1px solid #000;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.colorpicker-saturation i b {
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border: 1px solid #fff;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.colorpicker-hue,
|
||||
.colorpicker-alpha {
|
||||
float: left;
|
||||
width: 15px;
|
||||
height: 100px;
|
||||
margin-bottom: 4px;
|
||||
margin-left: 4px;
|
||||
cursor: row-resize;
|
||||
}
|
||||
.colorpicker-hue i,
|
||||
.colorpicker-alpha i {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
margin-top: -1px;
|
||||
background: #000;
|
||||
border-top: 1px solid #fff;
|
||||
}
|
||||
.colorpicker-hue {
|
||||
background-image: url('../img/bootstrap-colorpicker/hue.png');
|
||||
}
|
||||
.colorpicker-alpha {
|
||||
display: none;
|
||||
background-image: url('../img/bootstrap-colorpicker/alpha.png');
|
||||
}
|
||||
.colorpicker {
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2500;
|
||||
min-width: 130px;
|
||||
padding: 4px;
|
||||
margin-top: 1px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
*zoom: 1;
|
||||
}
|
||||
.colorpicker:before,
|
||||
.colorpicker:after {
|
||||
display: table;
|
||||
line-height: 0;
|
||||
content: '';
|
||||
}
|
||||
.colorpicker:after {
|
||||
clear: both;
|
||||
}
|
||||
.colorpicker:before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 6px;
|
||||
display: inline-block;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-left: 7px solid transparent;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: '';
|
||||
}
|
||||
.colorpicker:after {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 7px;
|
||||
display: inline-block;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-left: 6px solid transparent;
|
||||
content: '';
|
||||
}
|
||||
.colorpicker div {
|
||||
position: relative;
|
||||
}
|
||||
.colorpicker.colorpicker-with-alpha {
|
||||
min-width: 140px;
|
||||
}
|
||||
.colorpicker.colorpicker-with-alpha .colorpicker-alpha {
|
||||
display: block;
|
||||
}
|
||||
.colorpicker-color {
|
||||
height: 10px;
|
||||
margin-top: 5px;
|
||||
clear: both;
|
||||
background-image: url('../img/bootstrap-colorpicker/alpha.png');
|
||||
background-position: 0 100%;
|
||||
}
|
||||
.colorpicker-color div {
|
||||
height: 10px;
|
||||
}
|
||||
.colorpicker-element .input-group-addon i {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.colorpicker.colorpicker-inline {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
float: none;
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal {
|
||||
width: 110px;
|
||||
height: auto;
|
||||
min-width: 110px;
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-saturation {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-color {
|
||||
width: 100px;
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-hue,
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-alpha {
|
||||
float: left;
|
||||
width: 100px;
|
||||
height: 15px;
|
||||
margin-bottom: 4px;
|
||||
margin-left: 0;
|
||||
cursor: col-resize;
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-hue i,
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-alpha i {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 15px;
|
||||
margin-top: 0;
|
||||
background: #fff;
|
||||
border: 0;
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-hue {
|
||||
background-image: url('../img/bootstrap-colorpicker/hue-horizontal.png');
|
||||
}
|
||||
.colorpicker.colorpicker-horizontal .colorpicker-alpha {
|
||||
background-image: url('../img/bootstrap-colorpicker/alpha-horizontal.png');
|
||||
}
|
||||
.colorpicker.colorpicker-hidden {
|
||||
display: none;
|
||||
}
|
||||
.colorpicker.colorpicker-visible {
|
||||
display: block;
|
||||
}
|
||||
.colorpicker-inline.colorpicker-visible {
|
||||
display: inline-block;
|
||||
}
|
||||
|
108
libraries/bootstrap/css/bootstrap-theme.css
vendored
108
libraries/bootstrap/css/bootstrap-theme.css
vendored
@ -10,9 +10,9 @@
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
@ -26,8 +26,8 @@
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
@ -36,7 +36,7 @@
|
||||
.btn-default {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
@ -55,7 +55,7 @@
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
@ -73,7 +73,7 @@
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
@ -91,7 +91,7 @@
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
@ -109,7 +109,7 @@
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
@ -127,7 +127,7 @@
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
@ -145,14 +145,14 @@
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e8e8e8;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
@ -161,50 +161,50 @@
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #357ebd;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
|
||||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
|
||||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
@ -212,136 +212,136 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #3071a9;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3278b3;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-theme.css.map */
|
||||
|
341
libraries/bootstrap/css/bootstrap-theme.min.css
vendored
341
libraries/bootstrap/css/bootstrap-theme.min.css
vendored
File diff suppressed because one or more lines are too long
1068
libraries/bootstrap/css/bootstrap.css
vendored
1068
libraries/bootstrap/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
6046
libraries/bootstrap/css/bootstrap.min.css
vendored
6046
libraries/bootstrap/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
20
packages/editor/src/bootstrap-fixes.css
vendored
20
packages/editor/src/bootstrap-fixes.css
vendored
@ -1,20 +1,20 @@
|
||||
/*
|
||||
These are patches or hacks to avoid boostrap interfering with Mui styles
|
||||
This file is meant to be removed when removing bootstrap
|
||||
*/
|
||||
*/
|
||||
/*
|
||||
/* bootstrap modal */
|
||||
|
||||
.wise-editor .modal {
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 1000;
|
||||
}
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
6053
packages/editor/src/bootstrap-prefix.min.css
vendored
6053
packages/editor/src/bootstrap-prefix.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,103 +1,104 @@
|
||||
div#header {
|
||||
width: 100%;
|
||||
height:50px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index:1000;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
div#headerNotifier {
|
||||
border: 1px solid rgb(241, 163, 39);
|
||||
background-color: rgb(252, 235, 192);
|
||||
border-radius: 3px;
|
||||
position: fixed;
|
||||
padding: 5px 9px;
|
||||
color: back;
|
||||
white-space: nowrap;
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
bottom: 10px;
|
||||
border: 1px solid rgb(241, 163, 39);
|
||||
background-color: rgb(252, 235, 192);
|
||||
border-radius: 3px;
|
||||
position: fixed;
|
||||
padding: 5px 9px;
|
||||
color: back;
|
||||
white-space: nowrap;
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
div#toolbarRight {
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
margin: 6px 10px;
|
||||
height: 100%;
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
margin: 6px 10px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#account {
|
||||
float: right;
|
||||
display: inline;
|
||||
float: right;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#account >img {
|
||||
width: 36x;
|
||||
height: 36px;
|
||||
#account > img {
|
||||
width: 36x;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
#accountSettingsPanel{
|
||||
padding:10px 10px;
|
||||
#accountSettingsPanel {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
#share {
|
||||
margin: 0 30px;
|
||||
float: right;
|
||||
margin: 0 30px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.actionButton {
|
||||
cursor: pointer;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
padding: 10px 25px;
|
||||
font-size: 15px;
|
||||
min-width: 64px;
|
||||
box-sizing: border-box;
|
||||
font-weight: 600;
|
||||
border-radius: 9px;
|
||||
color: white;
|
||||
background-color: #ffa800;
|
||||
cursor: pointer;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
justify-content: center;
|
||||
padding: 10px 25px;
|
||||
font-size: 15px;
|
||||
min-width: 64px;
|
||||
box-sizing: border-box;
|
||||
font-weight: 600;
|
||||
border-radius: 9px;
|
||||
color: white;
|
||||
background-color: #ffa800;
|
||||
}
|
||||
|
||||
.actionButton:hover {
|
||||
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
||||
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
|
||||
box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
||||
}
|
||||
|
||||
div#toolbar {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: #fff;
|
||||
min-width: 900px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: #fff;
|
||||
min-width: 900px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div#toolbar .buttonContainer {
|
||||
height: 50px;
|
||||
padding-top: 8px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
float: left;
|
||||
border-left: 1px solid lightgray;
|
||||
height: 50px;
|
||||
padding-top: 8px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
float: left;
|
||||
border-left: 1px solid lightgray;
|
||||
}
|
||||
|
||||
div#mapName >span {
|
||||
border-radius: 4px;
|
||||
float: left;
|
||||
padding: 8px;
|
||||
min-width: 30px;
|
||||
font-weight: bold;
|
||||
div#mapName > span {
|
||||
border-radius: 4px;
|
||||
float: left;
|
||||
padding: 8px;
|
||||
min-width: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div#backToList {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
float: left;
|
||||
margin: 13px 20px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
float: left;
|
||||
margin: 13px 20px;
|
||||
}
|
||||
|
||||
/******************************************************************************************/
|
||||
@ -108,120 +109,120 @@ div#toolbar .buttonOn,
|
||||
div#toolbar .buttonOff,
|
||||
div#toolbar .buttonActive,
|
||||
div#toolbar .buttonOn:hover {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
float: left;
|
||||
text-align: center;
|
||||
z-index: 4;
|
||||
margin-top: 3px;
|
||||
padding-top: 2px;
|
||||
padding-left: 2px;
|
||||
margin-left: 3px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
float: left;
|
||||
text-align: center;
|
||||
z-index: 4;
|
||||
margin-top: 3px;
|
||||
padding-top: 2px;
|
||||
padding-left: 2px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
div#toolbar .buttonOn:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div#toolbar .buttonOn {
|
||||
opacity: 0.8;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
div#toolbar .buttonOff {
|
||||
opacity: 0.4;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
div#exportAnchor {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
div#toolbar .buttonExtOn,
|
||||
div#toolbar .buttonExtOff,
|
||||
div#toolbar .buttonExtActive,
|
||||
div#toolbar .buttonExtOn:hover {
|
||||
width: 40px;
|
||||
height: 28px;
|
||||
float: left;
|
||||
text-align: left;
|
||||
z-index: 4;
|
||||
margin-top: 3px;
|
||||
padding-top: 2px;
|
||||
padding-left: 5px;
|
||||
margin-left: 3px;
|
||||
width: 40px;
|
||||
height: 28px;
|
||||
float: left;
|
||||
text-align: left;
|
||||
z-index: 4;
|
||||
margin-top: 3px;
|
||||
padding-top: 2px;
|
||||
padding-left: 5px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
div#toolbar .buttonExtOn:hover {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div#toolbar .buttonExtActive {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div#toolbar .buttonExtOn {
|
||||
opacity: 0.8;
|
||||
cursor: pointer
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div#toolbar .buttonExtOff {
|
||||
opacity: 0.4;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
div#exportAnchor {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/***************************************************************************************************/
|
||||
/* Other toolbar styles */
|
||||
/***************************************************************************************************/
|
||||
.toolbarTip {
|
||||
background-color: #000000;
|
||||
padding: 5px 5px;
|
||||
color: #f5f5f5;
|
||||
font-size: 11px;
|
||||
background-color: #000000;
|
||||
padding: 5px 5px;
|
||||
color: #f5f5f5;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
div#colorPalette {
|
||||
border: 1px solid #bbb4d6;
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
width: 160px;
|
||||
top: 89px;
|
||||
border: 1px solid #bbb4d6;
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
width: 160px;
|
||||
top: 89px;
|
||||
}
|
||||
|
||||
div.toolbarPanelLink,
|
||||
div.toolbarPanelLinkSelectedLink {
|
||||
cursor: pointer;
|
||||
color: black;
|
||||
margin: 1px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
color: black;
|
||||
margin: 1px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.toolbarPanelLink:hover,
|
||||
div.toolbarPanelLinkSelectedLink {
|
||||
cursor: pointer;
|
||||
background-color: #efefef;
|
||||
cursor: pointer;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.toolbarPaneTip {
|
||||
background-color: rgb(228, 226, 210);
|
||||
padding: 5px 5px;
|
||||
color: #f5f5f5;
|
||||
font-size: 11px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
||||
border: 3px double rgb(190, 190, 190);
|
||||
}
|
||||
background-color: rgb(228, 226, 210);
|
||||
padding: 5px 5px;
|
||||
color: #f5f5f5;
|
||||
font-size: 11px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
||||
border: 3px double rgb(190, 190, 190);
|
||||
}
|
||||
|
@ -34,144 +34,131 @@ import { EditorRenderMode } from '@wisemapping/mindplot';
|
||||
export type ToolbarActionType = 'export' | 'publish' | 'history' | 'print' | 'share' | 'info';
|
||||
|
||||
export type ToolbarPropsType = {
|
||||
editorMode: EditorRenderMode;
|
||||
onAction: (action: ToolbarActionType) => void;
|
||||
editorMode: EditorRenderMode;
|
||||
onAction: (action: ToolbarActionType) => void;
|
||||
};
|
||||
|
||||
export default function Toolbar({
|
||||
editorMode: editorMode,
|
||||
onAction,
|
||||
editorMode: editorMode,
|
||||
onAction,
|
||||
}: ToolbarPropsType): React.ReactElement {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<HeaderContainer className="wise-editor">
|
||||
<div id="toolbar">
|
||||
<div id="backToList">
|
||||
<img src={BackIconSvg} />
|
||||
</div>
|
||||
{(editorMode === 'edition-editor' || editorMode === 'edition-owner') && (
|
||||
<div id="persist" className="buttonContainer">
|
||||
<ToolbarButton id="save" className="buttonOn">
|
||||
<img src={SaveSvg} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
)}
|
||||
{(editorMode === 'edition-editor' || editorMode === 'edition-owner' || editorMode === 'showcase') && (
|
||||
<>
|
||||
<div id="edit" className="buttonContainer">
|
||||
<ToolbarButton id="undoEdition" className="buttonOn">
|
||||
<img src={UndoSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="redoEdition" className="buttonOn">
|
||||
<img src={RedoSvg} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<div id="nodeStyle" className="buttonContainer">
|
||||
<ToolbarButton id="addTopic" className="buttonOn">
|
||||
<img src={TopicAddSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="deleteTopic" className="buttonOn">
|
||||
<img src={TopicDeleteSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="topicBorder" className="buttonExtOn">
|
||||
<img src={TopicBorderSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButtonExt id="topicColor" className="buttonExtOn">
|
||||
<img src={TopicColorSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButtonExt id="topicShape" className="buttonExtOn">
|
||||
<img src={TopicShapeSvg} />
|
||||
</ToolbarButtonExt>
|
||||
</div>
|
||||
<div id="font" className="buttonContainer">
|
||||
<ToolbarButton id="fontFamily" className="buttonOn">
|
||||
<img src={FontTypeSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="fontSize" className="buttonExtOn">
|
||||
<img src={FontSizeSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButton id="fontBold" className="buttonOn">
|
||||
<img src={FontBoldSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="fontItalic" className="buttonOn">
|
||||
<img src={FontItalicSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="fontColor" className="buttonExtOn">
|
||||
<img src={FontColorSvg} />
|
||||
</ToolbarButtonExt>
|
||||
</div>
|
||||
<div id="nodeContent" className="buttonContainer">
|
||||
<ToolbarButtonExt id="topicIcon" className="buttonExtOn">
|
||||
<img src={TopicIconSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButton id="topicNote" className="buttonOn">
|
||||
<img src={TopicNoteSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="topicLink" className="buttonOn">
|
||||
<img src={TopicLinkSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="topicRelation" className="buttonOn">
|
||||
<img src={TopicRelationSvg} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<div id="separator" className="buttonContainer"></div>
|
||||
</>
|
||||
)}
|
||||
<ToolbarRightContainer>
|
||||
<ToolbarButton
|
||||
id="export"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('export')}
|
||||
>
|
||||
<img src={ExportSvg} />
|
||||
</ToolbarButton>
|
||||
{(editorMode === 'edition-owner' || editorMode === 'edition-editor' || editorMode === 'edition-viewer') && (
|
||||
<ToolbarButton
|
||||
id="print"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('print')}
|
||||
>
|
||||
<img src={PrintSvg} />
|
||||
</ToolbarButton>
|
||||
)}
|
||||
<ToolbarButton
|
||||
id="info"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('info')}
|
||||
>
|
||||
<img src={InfoSvg} />
|
||||
</ToolbarButton>
|
||||
{editorMode === 'edition-owner' && (
|
||||
<>
|
||||
<ToolbarButton
|
||||
id="history"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('history')}
|
||||
>
|
||||
<img src={HistorySvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="publishIt"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('publish')}
|
||||
>
|
||||
<img src={PublicSvg} />
|
||||
</ToolbarButton>
|
||||
</>
|
||||
)}
|
||||
{(editorMode === 'edition-owner' || editorMode === 'edition-editor') && (
|
||||
<ToolbarButton id="account">
|
||||
<img src={AccountSvg} />
|
||||
</ToolbarButton>
|
||||
)}
|
||||
{editorMode === 'edition-owner' && (
|
||||
<ActionButton onClick={() => onAction('share')}>
|
||||
{intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' })}
|
||||
</ActionButton>
|
||||
|
||||
)}
|
||||
</ToolbarRightContainer>
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<HeaderContainer className="wise-editor">
|
||||
<div id="toolbar">
|
||||
<div id="backToList">
|
||||
<img src={BackIconSvg} />
|
||||
</div>
|
||||
{(editorMode === 'edition-editor' || editorMode === 'edition-owner') && (
|
||||
<div id="persist" className="buttonContainer">
|
||||
<ToolbarButton id="save" className="buttonOn">
|
||||
<img src={SaveSvg} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
)}
|
||||
{(editorMode === 'edition-editor' ||
|
||||
editorMode === 'edition-owner' ||
|
||||
editorMode === 'showcase') && (
|
||||
<>
|
||||
<div id="edit" className="buttonContainer">
|
||||
<ToolbarButton id="undoEdition" className="buttonOn">
|
||||
<img src={UndoSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="redoEdition" className="buttonOn">
|
||||
<img src={RedoSvg} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
</HeaderContainer>
|
||||
);
|
||||
<div id="nodeStyle" className="buttonContainer">
|
||||
<ToolbarButton id="addTopic" className="buttonOn">
|
||||
<img src={TopicAddSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="deleteTopic" className="buttonOn">
|
||||
<img src={TopicDeleteSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="topicBorder" className="buttonExtOn">
|
||||
<img src={TopicBorderSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButtonExt id="topicColor" className="buttonExtOn">
|
||||
<img src={TopicColorSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButtonExt id="topicShape" className="buttonExtOn">
|
||||
<img src={TopicShapeSvg} />
|
||||
</ToolbarButtonExt>
|
||||
</div>
|
||||
<div id="font" className="buttonContainer">
|
||||
<ToolbarButton id="fontFamily" className="buttonOn">
|
||||
<img src={FontTypeSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="fontSize" className="buttonExtOn">
|
||||
<img src={FontSizeSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButton id="fontBold" className="buttonOn">
|
||||
<img src={FontBoldSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="fontItalic" className="buttonOn">
|
||||
<img src={FontItalicSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButtonExt id="fontColor" className="buttonExtOn">
|
||||
<img src={FontColorSvg} />
|
||||
</ToolbarButtonExt>
|
||||
</div>
|
||||
<div id="nodeContent" className="buttonContainer">
|
||||
<ToolbarButtonExt id="topicIcon" className="buttonExtOn">
|
||||
<img src={TopicIconSvg} />
|
||||
</ToolbarButtonExt>
|
||||
<ToolbarButton id="topicNote" className="buttonOn">
|
||||
<img src={TopicNoteSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="topicLink" className="buttonOn">
|
||||
<img src={TopicLinkSvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton id="topicRelation" className="buttonOn">
|
||||
<img src={TopicRelationSvg} />
|
||||
</ToolbarButton>
|
||||
</div>
|
||||
<div id="separator" className="buttonContainer"></div>
|
||||
</>
|
||||
)}
|
||||
<ToolbarRightContainer>
|
||||
<ToolbarButton id="export" className="buttonOn" onClick={() => onAction('export')}>
|
||||
<img src={ExportSvg} />
|
||||
</ToolbarButton>
|
||||
{(editorMode === 'edition-owner' ||
|
||||
editorMode === 'edition-editor' ||
|
||||
editorMode === 'edition-viewer') && (
|
||||
<ToolbarButton id="print" className="buttonOn" onClick={() => onAction('print')}>
|
||||
<img src={PrintSvg} />
|
||||
</ToolbarButton>
|
||||
)}
|
||||
<ToolbarButton id="info" className="buttonOn" onClick={() => onAction('info')}>
|
||||
<img src={InfoSvg} />
|
||||
</ToolbarButton>
|
||||
{editorMode === 'edition-owner' && (
|
||||
<>
|
||||
<ToolbarButton id="history" className="buttonOn" onClick={() => onAction('history')}>
|
||||
<img src={HistorySvg} />
|
||||
</ToolbarButton>
|
||||
<ToolbarButton
|
||||
id="publishIt"
|
||||
className="buttonOn"
|
||||
onClick={() => onAction('publish')}
|
||||
>
|
||||
<img src={PublicSvg} />
|
||||
</ToolbarButton>
|
||||
</>
|
||||
)}
|
||||
{(editorMode === 'edition-owner' || editorMode === 'edition-editor') && (
|
||||
<ToolbarButton id="account">
|
||||
<img src={AccountSvg} />
|
||||
</ToolbarButton>
|
||||
)}
|
||||
{editorMode === 'edition-owner' && (
|
||||
<ActionButton onClick={() => onAction('share')}>
|
||||
{intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' })}
|
||||
</ActionButton>
|
||||
)}
|
||||
</ToolbarRightContainer>
|
||||
</div>
|
||||
</HeaderContainer>
|
||||
);
|
||||
}
|
||||
|
@ -1,282 +1,283 @@
|
||||
/********************************************************************************/
|
||||
/* Header & Toolbar Styles */
|
||||
/********************************************************************************/
|
||||
@import "bootstrap-prefix.min.css";
|
||||
@import "bootstrap-fixes.css";
|
||||
@import 'bootstrap-prefix.min.css';
|
||||
@import 'bootstrap-fixes.css';
|
||||
|
||||
html {
|
||||
/* avoid bootstrap overriding font-size and breaking Mui */
|
||||
font-size: initial;
|
||||
/* avoid bootstrap overriding font-size and breaking Mui */
|
||||
font-size: initial;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
min-width: 100vw;
|
||||
min-height: 100vh;
|
||||
margin: 0px;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
min-width: 100vw;
|
||||
min-height: 100vh;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.mindplot-root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div#mindplot {
|
||||
position: relative;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
background-color: #f2f2f2;
|
||||
background-image: linear-gradient(#ebe9e7 1px, transparent 1px), linear-gradient(to right, #ebe9e7 1px, #f2f2f2 1px);
|
||||
background-size: 50px 50px;
|
||||
position: relative;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
background-color: #f2f2f2;
|
||||
background-image: linear-gradient(#ebe9e7 1px, transparent 1px),
|
||||
linear-gradient(to right, #ebe9e7 1px, #f2f2f2 1px);
|
||||
background-size: 50px 50px;
|
||||
}
|
||||
|
||||
.notesTip {
|
||||
background-color: #dfcf3c;
|
||||
padding: 5px 15px;
|
||||
color: #666666;
|
||||
/*font-weight: bold;*/
|
||||
/*width: 100px;*/
|
||||
font-size: 13px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
background-color: #dfcf3c;
|
||||
padding: 5px 15px;
|
||||
color: #666666;
|
||||
/*font-weight: bold;*/
|
||||
/*width: 100px;*/
|
||||
font-size: 13px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.linkTip {
|
||||
background-color: #dfcf3c;
|
||||
padding: 5px 15px;
|
||||
color: #666666;
|
||||
/*font-weight: bold;*/
|
||||
/*width: 100px;*/
|
||||
font-size: 13px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
background-color: #dfcf3c;
|
||||
padding: 5px 15px;
|
||||
color: #666666;
|
||||
/*font-weight: bold;*/
|
||||
/*width: 100px;*/
|
||||
font-size: 13px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.keyboardShortcutTip {
|
||||
background-color: black;
|
||||
padding: 5px 15px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
background-color: black;
|
||||
padding: 5px 15px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/** */
|
||||
/* Modal dialogs definitions */
|
||||
|
||||
div.modalDialog {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 11000;
|
||||
width: 500px;
|
||||
margin: -250px 0 0 -250px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #999;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 11000;
|
||||
width: 500px;
|
||||
margin: -250px 0 0 -250px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #999;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
div.modalDialog .content {
|
||||
padding: 5px 5px;
|
||||
padding: 5px 5px;
|
||||
}
|
||||
|
||||
div.modalDialog .title {
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 0 #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 5px 15px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 0 #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 5px 15px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/*--- End Modal Dialog Form ---*/
|
||||
|
||||
.publishModalDialog .content {
|
||||
height: 420px;
|
||||
height: 420px;
|
||||
}
|
||||
|
||||
.exportModalDialog .content {
|
||||
height: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.shareModalDialog .content {
|
||||
height: 440px;
|
||||
height: 440px;
|
||||
}
|
||||
|
||||
div.shareModalDialog {
|
||||
width: 550px;
|
||||
width: 550px;
|
||||
}
|
||||
|
||||
.panelIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 4px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 4px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.panelIcon:hover {
|
||||
background-color: #efefef;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.wise-editor .popover {
|
||||
font-size: 13px;
|
||||
max-width: none;
|
||||
font-size: 13px;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
#floating-panel {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
align-items: stretch;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
div#position {
|
||||
margin-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#position-button {
|
||||
cursor: pointer;
|
||||
border: solid black 1px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 40px 40px;
|
||||
background-color: #FFF;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
border: solid black 1px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 40px 40px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#zoom-button {
|
||||
width: 40px;
|
||||
border: 0;
|
||||
width: 40px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#zoom-plus,
|
||||
#zoom-minus {
|
||||
border: solid black 1px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 40px 40px;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
background-color: #FFF;
|
||||
border: solid black 1px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 40px 40px;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#zoom-plus {
|
||||
border-radius: 8px 8px 0 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
#zoom-minus {
|
||||
border-radius: 0 0 8px 8px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
|
||||
div#shotcuts > img{
|
||||
margin: 20px 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
div#shotcuts > img {
|
||||
margin: 20px 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#keyboardTable {
|
||||
font-family: Arial, verdana, serif;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
font-family: Arial, verdana, serif;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#keyboardTable td {
|
||||
padding: 3px;
|
||||
white-space: nowrap;
|
||||
padding: 3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#keyboardTable th {
|
||||
padding: 5px;
|
||||
white-space: nowrap;
|
||||
padding: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#keyboardTable th {
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tryInfoPanel {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
border: solid 2px #ffa800;
|
||||
margin: auto;
|
||||
width: 99%;
|
||||
border-radius: 9px;
|
||||
width: 96%;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
border: solid 2px #ffa800;
|
||||
margin: auto;
|
||||
width: 99%;
|
||||
border-radius: 9px;
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.tryInfoPanel {
|
||||
font-size: 15px;
|
||||
}
|
||||
.tryInfoPanel {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tryInfoPanel {
|
||||
font-size: 13px;
|
||||
}
|
||||
.tryInfoPanel {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.tryInfoPanel .tryInfoPanelInner {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.tryInfoPanel .tryInfoPanelInner .closeButton {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
.tryInfoPanel .tryInfoPanelInner .closeButton {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.tryInfoPanel .tryInfoPanelInner .closeButton button {
|
||||
cursor: pointer;
|
||||
border-style: hidden;
|
||||
background-color: transparent;
|
||||
padding: 0px;
|
||||
cursor: pointer;
|
||||
border-style: hidden;
|
||||
background-color: transparent;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.tryInfoPanel .tryInfoPanelInner .closeButton button img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
filter: invert(73%) sepia(21%) saturate(4699%) hue-rotate(357deg) brightness(98%) contrast(108%);
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
filter: invert(73%) sepia(21%) saturate(4699%) hue-rotate(357deg) brightness(98%) contrast(108%);
|
||||
}
|
||||
|
||||
.tryInfoPanelWithToolbar {
|
||||
top: 55px;
|
||||
top: 55px;
|
||||
}
|
||||
|
||||
.tryInfoPanelWithoutToolbar {
|
||||
top: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.tryInfoPanelClosed {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tryInfoPanel > p {
|
||||
justify-content: center;
|
||||
}
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
/* Overwrite some styles */
|
||||
div#footer {
|
||||
width: 100%;
|
||||
padding: 20px 30px;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background-color: #ffa800;
|
||||
width: 100%;
|
||||
padding: 20px 30px;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background-color: #ffa800;
|
||||
}
|
||||
|
||||
div#footer-desc {
|
||||
float: left;
|
||||
height: 100px;
|
||||
padding: 0px 10px;
|
||||
float: left;
|
||||
height: 100px;
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
div#footer-logo {
|
||||
float: left;
|
||||
height: 100px;
|
||||
float: left;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#floating-panel {
|
||||
bottom: 20px;
|
||||
align-items: stretch;
|
||||
bottom: 20px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
div#mindplot {
|
||||
top:0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#toolbar {
|
||||
display: none;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import Editor, { EditorOptions } from '../../../../src/index';
|
||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||
|
||||
const initialization = (designer: Designer) => {
|
||||
|
||||
designer.addEvent('loadSuccess', () => {
|
||||
const elem = document.getElementById('mindplot');
|
||||
if (elem) {
|
||||
@ -35,10 +34,10 @@ const mapId = 'welcome';
|
||||
const options: EditorOptions = {
|
||||
zoom: 0.8,
|
||||
locked: false,
|
||||
mapTitle: "Develop WiseMapping",
|
||||
mapTitle: 'Develop WiseMapping',
|
||||
mode: 'edition-owner',
|
||||
locale: 'en',
|
||||
enableKeyboardEvents: true
|
||||
enableKeyboardEvents: true,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
|
@ -5,7 +5,6 @@ import Editor, { EditorOptions } from '../../../../src/index';
|
||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||
|
||||
const initialization = (designer: Designer) => {
|
||||
|
||||
designer.addEvent('loadSuccess', () => {
|
||||
const elem = document.getElementById('mindplot');
|
||||
if (elem) {
|
||||
@ -25,7 +24,6 @@ const initialization = (designer: Designer) => {
|
||||
option.selected = option.value === mapId;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
@ -36,10 +34,10 @@ const persistence = new LocalStorageManager('samples/{id}.wxml', false);
|
||||
const options: EditorOptions = {
|
||||
zoom: 0.8,
|
||||
locked: false,
|
||||
mapTitle: "Develop WiseMapping",
|
||||
mapTitle: 'Develop WiseMapping',
|
||||
mode: 'viewonly',
|
||||
locale: 'en',
|
||||
enableKeyboardEvents: true
|
||||
enableKeyboardEvents: true,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
|
@ -1,25 +1,24 @@
|
||||
body
|
||||
{
|
||||
font-size: 1em !important;
|
||||
color: #000 !important;
|
||||
font-family: Arial !important;
|
||||
body {
|
||||
font-size: 1em !important;
|
||||
color: #000 !important;
|
||||
font-family: Arial !important;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid darkgray;
|
||||
border-spacing: 0px;
|
||||
border: 1px solid darkgray;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
td {
|
||||
border: 1px solid darkgray;
|
||||
padding: 10px;
|
||||
border: 1px solid darkgray;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
tbody tr td:first-child {
|
||||
width: 20%;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.eventForm {
|
||||
float: left;
|
||||
margin: 10px;
|
||||
}
|
||||
float: left;
|
||||
margin: 10px;
|
||||
}
|
||||
|
@ -11,45 +11,42 @@ import Button from '@mui/material/Button';
|
||||
import AlertTitle from '@mui/material/AlertTitle';
|
||||
|
||||
const ClientHealthSentinel = (): React.ReactElement => {
|
||||
const status: ClientStatus = useSelector(activeInstanceStatus);
|
||||
const status: ClientStatus = useSelector(activeInstanceStatus);
|
||||
|
||||
const handleOnClose = () => {
|
||||
window.location.href = '/c/login';
|
||||
};
|
||||
const handleOnClose = () => {
|
||||
window.location.href = '/c/login';
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
open={status.state != 'healthy'}
|
||||
onClose={handleOnClose}
|
||||
maxWidth="sm"
|
||||
fullWidth={true}
|
||||
>
|
||||
<DialogTitle>
|
||||
<FormattedMessage
|
||||
id="expired.title"
|
||||
defaultMessage="Your session has expired"
|
||||
/>
|
||||
</DialogTitle>
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
open={status.state != 'healthy'}
|
||||
onClose={handleOnClose}
|
||||
maxWidth="sm"
|
||||
fullWidth={true}
|
||||
>
|
||||
<DialogTitle>
|
||||
<FormattedMessage id="expired.title" defaultMessage="Your session has expired" />
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<Alert severity="error">
|
||||
<AlertTitle>
|
||||
<FormattedMessage
|
||||
id="expired.description"
|
||||
defaultMessage="Your current session has expired. Please, sign in and try again."
|
||||
/>
|
||||
</AlertTitle>
|
||||
</Alert>
|
||||
</DialogContent>
|
||||
<DialogContent>
|
||||
<Alert severity="error">
|
||||
<AlertTitle>
|
||||
<FormattedMessage
|
||||
id="expired.description"
|
||||
defaultMessage="Your current session has expired. Please, sign in and try again."
|
||||
/>
|
||||
</AlertTitle>
|
||||
</Alert>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button type="button" color="primary" size="medium" onClick={handleOnClose}>
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
<DialogActions>
|
||||
<Button type="button" color="primary" size="medium" onClick={handleOnClose}>
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default ClientHealthSentinel;
|
||||
|
@ -13,78 +13,82 @@ import { activeInstance, fetchAccount, fetchMapById } from '../../redux/clientSl
|
||||
import EditorOptionsBuilder from './EditorOptionsBuilder';
|
||||
|
||||
export type EditorPropsType = {
|
||||
isTryMode: boolean;
|
||||
isTryMode: boolean;
|
||||
};
|
||||
|
||||
const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
||||
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
||||
const hotkey = useSelector(hotkeysEnabled);
|
||||
const userLocale = AppI18n.getUserLocale();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
||||
const hotkey = useSelector(hotkeysEnabled);
|
||||
const userLocale = AppI18n.getUserLocale();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
||||
}, []);
|
||||
|
||||
const findEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
||||
let result: EditorRenderMode = null;
|
||||
if (isTryMode) {
|
||||
result = 'showcase';
|
||||
} else if (global.mindmapLocked) {
|
||||
result = 'viewonly';
|
||||
} else {
|
||||
const fetchResult = fetchMapById(mapId);
|
||||
if (!fetchResult.isLoading) {
|
||||
if (fetchResult.error) {
|
||||
throw new Error(`Map info could not be loaded: ${JSON.stringify(fetchResult.error)}`);
|
||||
}
|
||||
|
||||
if (!fetchResult.map) {
|
||||
throw new Error(`Map info could not be loaded. Info not present: ${JSON.stringify(fetchResult)}`);
|
||||
}
|
||||
result = `edition-${fetchResult.map.role}`;
|
||||
}
|
||||
const findEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
||||
let result: EditorRenderMode = null;
|
||||
if (isTryMode) {
|
||||
result = 'showcase';
|
||||
} else if (global.mindmapLocked) {
|
||||
result = 'viewonly';
|
||||
} else {
|
||||
const fetchResult = fetchMapById(mapId);
|
||||
if (!fetchResult.isLoading) {
|
||||
if (fetchResult.error) {
|
||||
throw new Error(`Map info could not be loaded: ${JSON.stringify(fetchResult.error)}`);
|
||||
}
|
||||
return result;
|
||||
|
||||
if (!fetchResult.map) {
|
||||
throw new Error(
|
||||
`Map info could not be loaded. Info not present: ${JSON.stringify(fetchResult)}`,
|
||||
);
|
||||
}
|
||||
result = `edition-${fetchResult.map.role}`;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// What is the role ?
|
||||
const mapId = EditorOptionsBuilder.loadMapId();
|
||||
const mode = findEditorMode(isTryMode, mapId);
|
||||
// What is the role ?
|
||||
const mapId = EditorOptionsBuilder.loadMapId();
|
||||
const mode = findEditorMode(isTryMode, mapId);
|
||||
|
||||
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
||||
// at the i18n resource loading.
|
||||
const isAccountLoaded = mode === 'showcase' || fetchAccount;
|
||||
const loadCompleted = mode && isAccountLoaded;
|
||||
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
||||
// at the i18n resource loading.
|
||||
const isAccountLoaded = mode === 'showcase' || fetchAccount;
|
||||
const loadCompleted = mode && isAccountLoaded;
|
||||
|
||||
let options, persistence: PersistenceManager;
|
||||
if (loadCompleted) {
|
||||
options = EditorOptionsBuilder.build(userLocale.code, mode, hotkey);
|
||||
persistence = client.buildPersistenceManager(mode);
|
||||
}
|
||||
|
||||
return loadCompleted ? (
|
||||
<IntlProvider
|
||||
locale={userLocale.code}
|
||||
defaultLocale={Locales.EN.code}
|
||||
messages={userLocale.message as Record<string, string>}
|
||||
>
|
||||
<Editor onAction={setActiveDialog}
|
||||
options={options}
|
||||
persistenceManager={persistence}
|
||||
mapId={mapId} />
|
||||
{
|
||||
activeDialog &&
|
||||
<ActionDispatcher
|
||||
action={activeDialog}
|
||||
onClose={() => setActiveDialog(null)}
|
||||
mapsId={[mapId]}
|
||||
fromEditor
|
||||
/>
|
||||
}
|
||||
</IntlProvider>) : <></>
|
||||
}
|
||||
let options, persistence: PersistenceManager;
|
||||
if (loadCompleted) {
|
||||
options = EditorOptionsBuilder.build(userLocale.code, mode, hotkey);
|
||||
persistence = client.buildPersistenceManager(mode);
|
||||
}
|
||||
|
||||
return loadCompleted ? (
|
||||
<IntlProvider
|
||||
locale={userLocale.code}
|
||||
defaultLocale={Locales.EN.code}
|
||||
messages={userLocale.message as Record<string, string>}
|
||||
>
|
||||
<Editor
|
||||
onAction={setActiveDialog}
|
||||
options={options}
|
||||
persistenceManager={persistence}
|
||||
mapId={mapId}
|
||||
/>
|
||||
{activeDialog && (
|
||||
<ActionDispatcher
|
||||
action={activeDialog}
|
||||
onClose={() => setActiveDialog(null)}
|
||||
mapsId={[mapId]}
|
||||
fromEditor
|
||||
/>
|
||||
)}
|
||||
</IntlProvider>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditorPage;
|
||||
|
||||
|
@ -18,78 +18,85 @@ import Typography from '@mui/material/Typography';
|
||||
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
||||
|
||||
const ForgotPassword = () => {
|
||||
const [email, setEmail] = useState<string>('');
|
||||
const [error, setError] = useState<ErrorInfo>();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
const [email, setEmail] = useState<string>('');
|
||||
const [error, setError] = useState<ErrorInfo>();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
|
||||
const service: Client = useSelector(activeInstance);
|
||||
const mutation = useMutation<void, ErrorInfo, string>(
|
||||
(email: string) => service.resetPassword(email),
|
||||
{
|
||||
onSuccess: () => history.push('/c/forgot-password-success'),
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
const service: Client = useSelector(activeInstance);
|
||||
const mutation = useMutation<void, ErrorInfo, string>(
|
||||
(email: string) => service.resetPassword(email),
|
||||
{
|
||||
onSuccess: () => history.push('/c/forgot-password-success'),
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(email);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(email);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage id="forgot.title" defaultMessage="Reset your password" />
|
||||
</Typography>
|
||||
return (
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage id="forgot.title" defaultMessage="Reset your password" />
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
id="forgot.desc"
|
||||
defaultMessage="We will send you an email to reset your password."
|
||||
/>
|
||||
</Typography>
|
||||
<Typography>
|
||||
<FormattedMessage
|
||||
id="forgot.desc"
|
||||
defaultMessage="We will send you an email to reset your password."
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<GlobalError error={error} />
|
||||
<GlobalError error={error} />
|
||||
|
||||
<form onSubmit={handleOnSubmit}>
|
||||
<input type='hidden' value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
label={intl.formatMessage({ id: 'forgot.email', defaultMessage: 'Email' })}
|
||||
autoComplete="email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
error={error}
|
||||
/>
|
||||
<form onSubmit={handleOnSubmit}>
|
||||
<input type="hidden" value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
label={intl.formatMessage({ id: 'forgot.email', defaultMessage: 'Email' })}
|
||||
autoComplete="email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
error={error}
|
||||
/>
|
||||
|
||||
<SubmitButton
|
||||
value={intl.formatMessage({
|
||||
id: 'forgot.register',
|
||||
defaultMessage: 'Send recovery link',
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</FormContainer>
|
||||
);
|
||||
<SubmitButton
|
||||
value={intl.formatMessage({
|
||||
id: 'forgot.register',
|
||||
defaultMessage: 'Send recovery link',
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</FormContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const ForgotPasswordPage = (): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({ id: 'forgot.page-title', defaultMessage: 'Forgot Password | WiseMapping' });
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'ForgotPassword:Init' });
|
||||
}, []);
|
||||
const intl = useIntl();
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({
|
||||
id: 'forgot.page-title',
|
||||
defaultMessage: 'Forgot Password | WiseMapping',
|
||||
});
|
||||
ReactGA.send({
|
||||
hitType: 'pageview',
|
||||
page: window.location.pathname,
|
||||
title: 'ForgotPassword:Init',
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header type="only-signin" />
|
||||
<ForgotPassword />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Header type="only-signin" />
|
||||
<ForgotPassword />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ForgotPasswordPage };
|
||||
|
@ -9,45 +9,52 @@ import Button from '@mui/material/Button';
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
const ForgotPasswordSuccessPage = (): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({ id: 'forgotsuccess.page-title', defaultMessage: 'Password Recovered | WiseMapping' });
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'ForgotPassword:Success' });
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({
|
||||
id: 'forgotsuccess.page-title',
|
||||
defaultMessage: 'Password Recovered | WiseMapping',
|
||||
});
|
||||
ReactGA.send({
|
||||
hitType: 'pageview',
|
||||
page: window.location.pathname,
|
||||
title: 'ForgotPassword:Success',
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header type="none" />
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage
|
||||
id="forgot.success.title"
|
||||
defaultMessage="Your temporal password has been sent."
|
||||
/>
|
||||
</Typography>
|
||||
return (
|
||||
<div>
|
||||
<Header type="none" />
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage
|
||||
id="forgot.success.title"
|
||||
defaultMessage="Your temporal password has been sent."
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Typography paragraph>
|
||||
<FormattedMessage
|
||||
id="forgot.success.desc"
|
||||
defaultMessage="We've sent you an email that will allow you to reset your password. You should receive it in the next minutes."
|
||||
/>
|
||||
</Typography>
|
||||
<Typography paragraph>
|
||||
<FormattedMessage
|
||||
id="forgot.success.desc"
|
||||
defaultMessage="We've sent you an email that will allow you to reset your password. You should receive it in the next minutes."
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to="/c/login"
|
||||
disableElevation={true}
|
||||
>
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</FormContainer>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to="/c/login"
|
||||
disableElevation={true}
|
||||
>
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</FormContainer>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPasswordSuccessPage;
|
||||
|
@ -3,19 +3,19 @@ import { ErrorInfo } from '../../../classes/client';
|
||||
import StyledAlert from './styled';
|
||||
|
||||
type GlobalErrorProps = {
|
||||
error?: ErrorInfo;
|
||||
error?: ErrorInfo;
|
||||
};
|
||||
|
||||
const GlobalError = (props: GlobalErrorProps): React.ReactElement | null => {
|
||||
const error = props.error;
|
||||
const hasError = Boolean(error?.msg);
|
||||
const errorMsg = error?.msg;
|
||||
const error = props.error;
|
||||
const hasError = Boolean(error?.msg);
|
||||
const errorMsg = error?.msg;
|
||||
|
||||
return hasError ? (
|
||||
<StyledAlert severity="error" variant="filled" hidden={!hasError}>
|
||||
{errorMsg}
|
||||
</StyledAlert>
|
||||
) : null;
|
||||
return hasError ? (
|
||||
<StyledAlert severity="error" variant="filled" hidden={!hasError}>
|
||||
{errorMsg}
|
||||
</StyledAlert>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default GlobalError;
|
||||
|
@ -3,51 +3,51 @@ import React, { ChangeEvent } from 'react';
|
||||
import { ErrorInfo } from '../../../classes/client';
|
||||
|
||||
type InputProps = {
|
||||
name: string;
|
||||
error?: ErrorInfo;
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
type: string;
|
||||
value?: string;
|
||||
autoComplete?: string;
|
||||
fullWidth?: boolean;
|
||||
disabled?: boolean;
|
||||
maxLength?: number,
|
||||
rows?: number
|
||||
name: string;
|
||||
error?: ErrorInfo;
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
type: string;
|
||||
value?: string;
|
||||
autoComplete?: string;
|
||||
fullWidth?: boolean;
|
||||
disabled?: boolean;
|
||||
maxLength?: number;
|
||||
rows?: number;
|
||||
};
|
||||
|
||||
const Input = ({
|
||||
name,
|
||||
error,
|
||||
onChange,
|
||||
required = true,
|
||||
type,
|
||||
value,
|
||||
label,
|
||||
autoComplete,
|
||||
fullWidth = true,
|
||||
disabled = false,
|
||||
maxLength = 254,
|
||||
name,
|
||||
error,
|
||||
onChange,
|
||||
required = true,
|
||||
type,
|
||||
value,
|
||||
label,
|
||||
autoComplete,
|
||||
fullWidth = true,
|
||||
disabled = false,
|
||||
maxLength = 254,
|
||||
}: InputProps): React.ReactElement => {
|
||||
const fieldError = error?.fields?.[name];
|
||||
return (
|
||||
<TextField
|
||||
name={name}
|
||||
type={type}
|
||||
label={label}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={Boolean(fieldError)}
|
||||
helperText={fieldError}
|
||||
variant="outlined"
|
||||
required={required}
|
||||
fullWidth={fullWidth}
|
||||
margin="dense"
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
inputProps={{ maxLength: maxLength }}
|
||||
/>
|
||||
);
|
||||
const fieldError = error?.fields?.[name];
|
||||
return (
|
||||
<TextField
|
||||
name={name}
|
||||
type={type}
|
||||
label={label}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={Boolean(fieldError)}
|
||||
helperText={fieldError}
|
||||
variant="outlined"
|
||||
required={required}
|
||||
fullWidth={fullWidth}
|
||||
margin="dense"
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
inputProps={{ maxLength: maxLength }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default Input;
|
||||
|
@ -3,37 +3,37 @@ import React, { useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
type SubmitButton = {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
const SubmitButton = (props: SubmitButton): React.ReactElement => {
|
||||
const [disabled] = useState(props.disabled ? true : false);
|
||||
const intl = useIntl();
|
||||
const [disabled] = useState(props.disabled ? true : false);
|
||||
const intl = useIntl();
|
||||
|
||||
let valueTxt = props.value;
|
||||
if (disabled) {
|
||||
valueTxt = intl.formatMessage({ id: 'common.wait', defaultMessage: 'Please wait ...' });
|
||||
}
|
||||
const [value] = useState(valueTxt);
|
||||
return (
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
disableElevation={true}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
width: '350px',
|
||||
height: '53px',
|
||||
padding: '0px 20px',
|
||||
margin: '7px 0px',
|
||||
fontSize: '18px',
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</Button>
|
||||
);
|
||||
let valueTxt = props.value;
|
||||
if (disabled) {
|
||||
valueTxt = intl.formatMessage({ id: 'common.wait', defaultMessage: 'Please wait ...' });
|
||||
}
|
||||
const [value] = useState(valueTxt);
|
||||
return (
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
disableElevation={true}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
width: '350px',
|
||||
height: '53px',
|
||||
padding: '0px 20px',
|
||||
margin: '7px 0px',
|
||||
fontSize: '18px',
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubmitButton;
|
||||
|
@ -5,79 +5,70 @@ import poweredByIcon from './pwrdby-white.svg';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const Footer = (): React.ReactElement => {
|
||||
return (
|
||||
<StyledFooter>
|
||||
<div style={{ padding: 0, margin: 0 }}>
|
||||
<a href="http://www.wisemapping.org/">
|
||||
<img src={poweredByIcon} alt="Powered By WiseMapping" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h4>
|
||||
<FormattedMessage id="footer.faqandhelp" defaultMessage="Help & FAQ" />
|
||||
</h4>
|
||||
<div>
|
||||
<a href="https://www.wisemapping.com/aboutus.html">
|
||||
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.wisemapping.com/faq.html">
|
||||
<FormattedMessage id="footer.faq" defaultMessage="F.A.Q." />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.wisemapping.com/termsofuse.html">
|
||||
<FormattedMessage
|
||||
id="footer.termsandconditions"
|
||||
defaultMessage="Term And Conditions"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>
|
||||
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
||||
</h4>
|
||||
<div>
|
||||
<a href="mailto:support@wisemapping.com">
|
||||
<FormattedMessage id="footer.support" defaultMessage="Support" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="mailto:feedback@wisemapping.com">
|
||||
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="mailto:team@wisemapping.com">
|
||||
<FormattedMessage id="footer.team" defaultMessage="Our Team" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>
|
||||
<FormattedMessage id="footer.others" defaultMessage="Others" />
|
||||
</h4>
|
||||
<div>
|
||||
<a href="https://www.paypal.com/donate/?hosted_button_id=CF7GJ7T6E4RS4">
|
||||
<FormattedMessage
|
||||
id="footer.donations"
|
||||
defaultMessage="Donations"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="http://www.wisemapping.org/">
|
||||
<FormattedMessage
|
||||
id="footer.opensource"
|
||||
defaultMessage="Open Source"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</StyledFooter>
|
||||
);
|
||||
return (
|
||||
<StyledFooter>
|
||||
<div style={{ padding: 0, margin: 0 }}>
|
||||
<a href="http://www.wisemapping.org/">
|
||||
<img src={poweredByIcon} alt="Powered By WiseMapping" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h4>
|
||||
<FormattedMessage id="footer.faqandhelp" defaultMessage="Help & FAQ" />
|
||||
</h4>
|
||||
<div>
|
||||
<a href="https://www.wisemapping.com/aboutus.html">
|
||||
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.wisemapping.com/faq.html">
|
||||
<FormattedMessage id="footer.faq" defaultMessage="F.A.Q." />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.wisemapping.com/termsofuse.html">
|
||||
<FormattedMessage id="footer.termsandconditions" defaultMessage="Term And Conditions" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>
|
||||
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
||||
</h4>
|
||||
<div>
|
||||
<a href="mailto:support@wisemapping.com">
|
||||
<FormattedMessage id="footer.support" defaultMessage="Support" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="mailto:feedback@wisemapping.com">
|
||||
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="mailto:team@wisemapping.com">
|
||||
<FormattedMessage id="footer.team" defaultMessage="Our Team" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>
|
||||
<FormattedMessage id="footer.others" defaultMessage="Others" />
|
||||
</h4>
|
||||
<div>
|
||||
<a href="https://www.paypal.com/donate/?hosted_button_id=CF7GJ7T6E4RS4">
|
||||
<FormattedMessage id="footer.donations" defaultMessage="Donations" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="http://www.wisemapping.org/">
|
||||
<FormattedMessage id="footer.opensource" defaultMessage="Open Source" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</StyledFooter>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
@ -2,11 +2,11 @@ import Container from '@mui/material/Container';
|
||||
import withStyles from '@mui/styles/withStyles';
|
||||
|
||||
const FormContainer = withStyles({
|
||||
root: {
|
||||
padding: '20px 10px 0px 20px',
|
||||
maxWidth: '380px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
root: {
|
||||
padding: '20px 10px 0px 20px',
|
||||
maxWidth: '380px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
})(Container);
|
||||
|
||||
export default FormContainer;
|
||||
|
@ -8,89 +8,83 @@ import Button from '@mui/material/Button';
|
||||
import logo from './logo-small.svg';
|
||||
|
||||
interface HeaderProps {
|
||||
type: 'only-signup' | 'only-signin' | 'none';
|
||||
type: 'only-signup' | 'only-signin' | 'none';
|
||||
}
|
||||
|
||||
export const Header = ({ type }: HeaderProps): React.ReactElement => {
|
||||
let signUpButton;
|
||||
let text;
|
||||
let signInButton;
|
||||
if (type === 'only-signup') {
|
||||
text = (
|
||||
<span className="header-area-content-span">
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="header.donthaveaccount"
|
||||
defaultMessage="Don't have an account ?"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
signUpButton = <SignUpButton className="header-area-right2" />;
|
||||
} else if (type === 'only-signin') {
|
||||
text = (
|
||||
<span className="header-area-content-span">
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="header.haveaccount"
|
||||
defaultMessage="Already have an account?"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
signUpButton = <SignInButton className="header-area-right2" />;
|
||||
} else if (type === 'none') {
|
||||
text = '';
|
||||
signUpButton = '';
|
||||
} else {
|
||||
signUpButton = <SignUpButton className="header-area-right2" />;
|
||||
signInButton = <SignInButton className="header-area-right2" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledNav>
|
||||
<StyledDiv>
|
||||
<Logo>
|
||||
<Link to="/c/login" className="header-logo">
|
||||
<img src={String(logo)} alt="logo" />
|
||||
</Link>
|
||||
</Logo>
|
||||
{text}
|
||||
{signUpButton}
|
||||
{signInButton}
|
||||
</StyledDiv>
|
||||
</StyledNav>
|
||||
let signUpButton;
|
||||
let text;
|
||||
let signInButton;
|
||||
if (type === 'only-signup') {
|
||||
text = (
|
||||
<span className="header-area-content-span">
|
||||
<span>
|
||||
<FormattedMessage id="header.donthaveaccount" defaultMessage="Don't have an account ?" />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
signUpButton = <SignUpButton className="header-area-right2" />;
|
||||
} else if (type === 'only-signin') {
|
||||
text = (
|
||||
<span className="header-area-content-span">
|
||||
<span>
|
||||
<FormattedMessage id="header.haveaccount" defaultMessage="Already have an account?" />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
signUpButton = <SignInButton className="header-area-right2" />;
|
||||
} else if (type === 'none') {
|
||||
text = '';
|
||||
signUpButton = '';
|
||||
} else {
|
||||
signUpButton = <SignUpButton className="header-area-right2" />;
|
||||
signInButton = <SignInButton className="header-area-right2" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledNav>
|
||||
<StyledDiv>
|
||||
<Logo>
|
||||
<Link to="/c/login" className="header-logo">
|
||||
<img src={String(logo)} alt="logo" />
|
||||
</Link>
|
||||
</Logo>
|
||||
{text}
|
||||
{signUpButton}
|
||||
{signInButton}
|
||||
</StyledDiv>
|
||||
</StyledNav>
|
||||
);
|
||||
};
|
||||
|
||||
interface ButtonProps {
|
||||
className?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const SignInButton = (props: ButtonProps): React.ReactElement => {
|
||||
return (
|
||||
<span className={`${props.className}`}>
|
||||
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/login">
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span className={`${props.className}`}>
|
||||
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/login">
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const SignUpButton = (props: ButtonProps): React.ReactElement => {
|
||||
return (
|
||||
<span className={`${props.className}`}>
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="outlined"
|
||||
component={Link}
|
||||
to="/c/registration"
|
||||
>
|
||||
<FormattedMessage id="login.signup" defaultMessage="Sign Up" />
|
||||
</Button>
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span className={`${props.className}`}>
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="outlined"
|
||||
component={Link}
|
||||
to="/c/registration"
|
||||
>
|
||||
<FormattedMessage id="login.signup" defaultMessage="Sign Up" />
|
||||
</Button>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
@ -14,130 +14,130 @@ import ReactGA from 'react-ga4';
|
||||
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
||||
|
||||
type ConfigStatusProps = {
|
||||
enabled?: boolean;
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.ReactElement => {
|
||||
let result;
|
||||
if (enabled === true) {
|
||||
result = (
|
||||
<div className="db-warn-msg">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="login.hsqldbcofig"
|
||||
defaultMessage="Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL"
|
||||
description="Missing production database configured"
|
||||
/>
|
||||
<a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration">
|
||||
{' '}
|
||||
here
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return result || null;
|
||||
let result;
|
||||
if (enabled === true) {
|
||||
result = (
|
||||
<div className="db-warn-msg">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="login.hsqldbcofig"
|
||||
defaultMessage="Although HSQLDB is bundled with WiseMapping by default during the installation, we do not recommend this database for production use. Please consider using MySQL 5.7 instead. You can find more information how to configure MySQL"
|
||||
description="Missing production database configured"
|
||||
/>
|
||||
<a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration">
|
||||
{' '}
|
||||
here
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return result || null;
|
||||
};
|
||||
|
||||
const LoginError = () => {
|
||||
// @Todo: This must be reviewed to be based on navigation state.
|
||||
// Login error example: http://localhost:8080/c/login?login.error=2
|
||||
const errorCode = new URLSearchParams(window.location.search).get('login_error');
|
||||
const intl = useIntl();
|
||||
// @Todo: This must be reviewed to be based on navigation state.
|
||||
// Login error example: http://localhost:8080/c/login?login.error=2
|
||||
const errorCode = new URLSearchParams(window.location.search).get('login_error');
|
||||
const intl = useIntl();
|
||||
|
||||
let msg: null | string = null;
|
||||
if (errorCode) {
|
||||
switch (errorCode) {
|
||||
case '3':
|
||||
msg = intl.formatMessage({
|
||||
id: 'login.userinactive',
|
||||
defaultMessage:
|
||||
"Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!.",
|
||||
});
|
||||
break;
|
||||
default:
|
||||
msg = intl.formatMessage({
|
||||
id: 'login.error',
|
||||
defaultMessage: 'The email address or password you entered is not valid.',
|
||||
});
|
||||
}
|
||||
let msg: null | string = null;
|
||||
if (errorCode) {
|
||||
switch (errorCode) {
|
||||
case '3':
|
||||
msg = intl.formatMessage({
|
||||
id: 'login.userinactive',
|
||||
defaultMessage:
|
||||
"Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!.",
|
||||
});
|
||||
break;
|
||||
default:
|
||||
msg = intl.formatMessage({
|
||||
id: 'login.error',
|
||||
defaultMessage: 'The email address or password you entered is not valid.',
|
||||
});
|
||||
}
|
||||
return msg ? <GlobalError error={{ msg: msg }} /> : null;
|
||||
}
|
||||
return msg ? <GlobalError error={{ msg: msg }} /> : null;
|
||||
};
|
||||
|
||||
const LoginPage = (): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({ id: 'login.page-title', defaultMessage: 'Login | WiseMapping' });
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Login' });
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({
|
||||
id: 'login.page-title',
|
||||
defaultMessage: 'Login | WiseMapping',
|
||||
});
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Login' });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header type="only-signup" />
|
||||
return (
|
||||
<div>
|
||||
<Header type="only-signup" />
|
||||
|
||||
<FormContainer maxWidth="xs">
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage id="login.title" defaultMessage="Welcome" />
|
||||
</Typography>
|
||||
<FormContainer maxWidth="xs">
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage id="login.title" defaultMessage="Welcome" />
|
||||
</Typography>
|
||||
|
||||
<Typography paragraph>
|
||||
<FormattedMessage id="login.desc" defaultMessage="Log into your account" />
|
||||
</Typography>
|
||||
<Typography paragraph>
|
||||
<FormattedMessage id="login.desc" defaultMessage="Log into your account" />
|
||||
</Typography>
|
||||
|
||||
<LoginError />
|
||||
<LoginError />
|
||||
|
||||
<FormControl>
|
||||
<form action="/c/perform-login" method="POST">
|
||||
<input type='hidden' value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
||||
<Input
|
||||
name="username"
|
||||
type="email"
|
||||
label={intl.formatMessage({
|
||||
id: 'login.email',
|
||||
defaultMessage: 'Email',
|
||||
})}
|
||||
required
|
||||
autoComplete="email"
|
||||
/>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
label={intl.formatMessage({
|
||||
id: 'login.password',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
required
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<div>
|
||||
<input name="remember-me" id="remember-me" type="checkbox" />
|
||||
<label htmlFor="remember-me">
|
||||
<FormattedMessage
|
||||
id="login.remberme"
|
||||
defaultMessage="Remember me"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<SubmitButton
|
||||
value={intl.formatMessage({
|
||||
id: 'login.signin',
|
||||
defaultMessage: 'Sign In',
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<form action="/c/perform-login" method="POST">
|
||||
<input type="hidden" value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
||||
<Input
|
||||
name="username"
|
||||
type="email"
|
||||
label={intl.formatMessage({
|
||||
id: 'login.email',
|
||||
defaultMessage: 'Email',
|
||||
})}
|
||||
required
|
||||
autoComplete="email"
|
||||
/>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
label={intl.formatMessage({
|
||||
id: 'login.password',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
required
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<div>
|
||||
<input name="remember-me" id="remember-me" type="checkbox" />
|
||||
<label htmlFor="remember-me">
|
||||
<FormattedMessage id="login.remberme" defaultMessage="Remember me" />
|
||||
</label>
|
||||
</div>
|
||||
<SubmitButton
|
||||
value={intl.formatMessage({
|
||||
id: 'login.signin',
|
||||
defaultMessage: 'Sign In',
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</FormControl>
|
||||
|
||||
<Link component={RouterLink} to="/c/forgot-password">
|
||||
<FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" />
|
||||
</Link>
|
||||
<ConfigStatusMessage />
|
||||
</FormContainer>
|
||||
<Link component={RouterLink} to="/c/forgot-password">
|
||||
<FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" />
|
||||
</Link>
|
||||
<ConfigStatusMessage />
|
||||
</FormContainer>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
|
@ -14,165 +14,168 @@ import FormGroup from '@mui/material/FormGroup';
|
||||
import Switch from '@mui/material/Switch';
|
||||
|
||||
type AccountInfoDialogProps = {
|
||||
onClose: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type AccountInfoModel = {
|
||||
email: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
email: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
};
|
||||
|
||||
const defaultModel: AccountInfoModel = { firstname: '', lastname: '', email: '' };
|
||||
const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps): React.ReactElement => {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const [remove, setRemove] = React.useState<boolean>(false);
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const [remove, setRemove] = React.useState<boolean>(false);
|
||||
|
||||
const [model, setModel] = React.useState<AccountInfoModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
const [model, setModel] = React.useState<AccountInfoModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
|
||||
const mutationChangeName = useMutation<void, ErrorInfo, AccountInfoModel>(
|
||||
(model: AccountInfoModel) => {
|
||||
return client.updateAccountInfo(model.firstname, model.lastname);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('account');
|
||||
onClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const mutationRemove = useMutation<void, ErrorInfo, void>(
|
||||
() => {
|
||||
return client.deleteAccount();
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
window.location.href = '/c/login';
|
||||
onClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const account = fetchAccount();
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
setModel({
|
||||
email: account?.email,
|
||||
lastname: account?.lastname,
|
||||
firstname: account?.firstname,
|
||||
});
|
||||
}
|
||||
}, [account?.email]);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
const mutationChangeName = useMutation<void, ErrorInfo, AccountInfoModel>(
|
||||
(model: AccountInfoModel) => {
|
||||
return client.updateAccountInfo(model.firstname, model.lastname);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('account');
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
if (remove) {
|
||||
mutationRemove.mutate();
|
||||
} else {
|
||||
mutationChangeName.mutate(model);
|
||||
}
|
||||
};
|
||||
const mutationRemove = useMutation<void, ErrorInfo, void>(
|
||||
() => {
|
||||
return client.deleteAccount();
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
window.location.href = '/c/login';
|
||||
onClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
const account = fetchAccount();
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
setModel({
|
||||
email: account?.email,
|
||||
lastname: account?.lastname,
|
||||
firstname: account?.firstname,
|
||||
});
|
||||
}
|
||||
}, [account?.email]);
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof AccountInfoModel]: value });
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const handleOnRemoveChange = (event) => {
|
||||
setRemove(event.target.checked);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
if (remove) {
|
||||
mutationRemove.mutate();
|
||||
} else {
|
||||
mutationChangeName.mutate(model);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'accountinfo.title', defaultMessage: 'Account info' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'accountinfo.button',
|
||||
defaultMessage: 'Accept',
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof AccountInfoModel]: value });
|
||||
};
|
||||
|
||||
const handleOnRemoveChange = (event) => {
|
||||
setRemove(event.target.checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'accountinfo.title', defaultMessage: 'Account info' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'accountinfo.button',
|
||||
defaultMessage: 'Accept',
|
||||
})}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="email"
|
||||
type="text"
|
||||
disabled={true}
|
||||
label={intl.formatMessage({ id: 'accountinfo.email', defaultMessage: 'Email' })}
|
||||
value={model.email}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="firstname"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'accountinfo.firstname',
|
||||
defaultMessage: 'First Name',
|
||||
})}
|
||||
value={model.firstname}
|
||||
onChange={handleOnChange}
|
||||
required={true}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="lastname"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'accountinfo.lastname',
|
||||
defaultMessage: 'Last Name',
|
||||
})}
|
||||
value={model.lastname}
|
||||
onChange={handleOnChange}
|
||||
required={true}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<FormGroup>
|
||||
{remove && (
|
||||
<Alert severity="error">
|
||||
<FormattedMessage
|
||||
id="account.delete-warning"
|
||||
defaultMessage="Keep in mind that you will not be able retrieve any mindmap you have added. All your information will be deleted and it can not be restored."
|
||||
/>
|
||||
</Alert>
|
||||
)}
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={remove}
|
||||
onChange={handleOnRemoveChange}
|
||||
name="remove"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={intl.formatMessage({
|
||||
id: 'accountinfo.deleteaccount',
|
||||
defaultMessage: 'Delete Account',
|
||||
})}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="email"
|
||||
type="text"
|
||||
disabled={true}
|
||||
label={intl.formatMessage({ id: 'accountinfo.email', defaultMessage: 'Email' })}
|
||||
value={model.email}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="firstname"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'accountinfo.firstname',
|
||||
defaultMessage: 'First Name',
|
||||
})}
|
||||
value={model.firstname}
|
||||
onChange={handleOnChange}
|
||||
required={true}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="lastname"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'accountinfo.lastname',
|
||||
defaultMessage: 'Last Name',
|
||||
})}
|
||||
value={model.lastname}
|
||||
onChange={handleOnChange}
|
||||
required={true}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<FormGroup>
|
||||
{remove && (
|
||||
<Alert severity="error">
|
||||
<FormattedMessage
|
||||
id="account.delete-warning"
|
||||
defaultMessage="Keep in mind that you will not be able retrieve any mindmap you have added. All your information will be deleted and it can not be restored."
|
||||
/>
|
||||
</Alert>
|
||||
)}
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={remove}
|
||||
onChange={handleOnRemoveChange}
|
||||
name="remove"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={intl.formatMessage({ id: 'accountinfo.deleteaccount', defaultMessage: 'Delete Account' })}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
);
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
||||
export default AccountInfoDialog;
|
||||
|
@ -9,108 +9,108 @@ import { useSelector } from 'react-redux';
|
||||
import { activeInstance } from '../../../../redux/clientSlice';
|
||||
|
||||
type ChangePasswordDialogProps = {
|
||||
onClose: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type ChangePasswordModel = {
|
||||
password: string;
|
||||
retryPassword: string;
|
||||
password: string;
|
||||
retryPassword: string;
|
||||
};
|
||||
|
||||
const defaultModel: ChangePasswordModel = { password: '', retryPassword: '' };
|
||||
const ChangePasswordDialog = ({ onClose }: ChangePasswordDialogProps): React.ReactElement => {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<ChangePasswordModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<ChangePasswordModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
|
||||
const mutation = useMutation<void, ErrorInfo, ChangePasswordModel>(
|
||||
(model: ChangePasswordModel) => {
|
||||
return client.updateAccountPassword(model.password);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
const mutation = useMutation<void, ErrorInfo, ChangePasswordModel>(
|
||||
(model: ChangePasswordModel) => {
|
||||
return client.updateAccountPassword(model.password);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
// Check password are equal ...
|
||||
if (model.password != model.retryPassword) {
|
||||
setError({
|
||||
msg: intl.formatMessage({
|
||||
id: 'changepwd.password-match',
|
||||
defaultMessage: 'Password do not match. Please, try again.',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
mutation.mutate(model);
|
||||
};
|
||||
// Check password are equal ...
|
||||
if (model.password != model.retryPassword) {
|
||||
setError({
|
||||
msg: intl.formatMessage({
|
||||
id: 'changepwd.password-match',
|
||||
defaultMessage: 'Password do not match. Please, try again.',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof ChangePasswordModel]: value });
|
||||
};
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'changepwd.title', defaultMessage: 'Change Password' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'changepwd.description',
|
||||
defaultMessage: 'Please, provide the new password for your account.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'changepwd.button', defaultMessage: 'Change' })}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
label={intl.formatMessage({
|
||||
id: 'changepwd.password',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
value={model.password}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof ChangePasswordModel]: value });
|
||||
};
|
||||
|
||||
<Input
|
||||
name="retryPassword"
|
||||
type="password"
|
||||
label={intl.formatMessage({
|
||||
id: 'changepwd.confirm-password',
|
||||
defaultMessage: 'Confirm Password',
|
||||
})}
|
||||
value={model.retryPassword}
|
||||
onChange={handleOnChange}
|
||||
required={true}
|
||||
fullWidth={true}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
);
|
||||
return (
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'changepwd.title', defaultMessage: 'Change Password' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'changepwd.description',
|
||||
defaultMessage: 'Please, provide the new password for your account.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'changepwd.button', defaultMessage: 'Change' })}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
label={intl.formatMessage({
|
||||
id: 'changepwd.password',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
value={model.password}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="retryPassword"
|
||||
type="password"
|
||||
label={intl.formatMessage({
|
||||
id: 'changepwd.confirm-password',
|
||||
defaultMessage: 'Confirm Password',
|
||||
})}
|
||||
value={model.retryPassword}
|
||||
onChange={handleOnChange}
|
||||
required={true}
|
||||
fullWidth={true}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
||||
export default ChangePasswordDialog;
|
||||
|
@ -16,90 +16,86 @@ import ExitToAppOutlined from '@mui/icons-material/ExitToAppOutlined';
|
||||
|
||||
type ActionType = 'change-password' | 'account-info' | undefined;
|
||||
const AccountMenu = (): React.ReactElement => {
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const [action, setAction] = React.useState<ActionType>(undefined);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const [action, setAction] = React.useState<ActionType>(undefined);
|
||||
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleLogout = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
const elem = document.getElementById('logoutFrom') as HTMLFormElement;
|
||||
elem.submit();
|
||||
};
|
||||
const handleLogout = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
const elem = document.getElementById('logoutFrom') as HTMLFormElement;
|
||||
elem.submit();
|
||||
};
|
||||
|
||||
const account = fetchAccount();
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}
|
||||
>
|
||||
<IconButton onClick={handleMenu} size="large">
|
||||
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="appbar-profile"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose(), setAction('account-info');
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<SettingsApplicationsOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="menu.account" defaultMessage="Account" />
|
||||
</MenuItem>
|
||||
const account = fetchAccount();
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}
|
||||
>
|
||||
<IconButton onClick={handleMenu} size="large">
|
||||
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="appbar-profile"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose(), setAction('account-info');
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<SettingsApplicationsOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="menu.account" defaultMessage="Account" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose(), setAction('change-password');
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<LockOpenOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="menu.change-password" defaultMessage="Change password" />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose(), setAction('change-password');
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<LockOpenOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="menu.change-password" defaultMessage="Change password" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleClose}>
|
||||
<form action="/c/logout" method='POST' id="logoutFrom"></form>
|
||||
<Link color="textSecondary" href="/c/logout" onClick={(e) => handleLogout(e)}>
|
||||
<ListItemIcon>
|
||||
<ExitToAppOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="menu.signout" defaultMessage="Sign Out" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
{
|
||||
action == 'change-password' && (
|
||||
<ChangePasswordDialog onClose={() => setAction(undefined)} />
|
||||
)
|
||||
}
|
||||
{action == 'account-info' && <AccountInfoDialog onClose={() => setAction(undefined)} />}
|
||||
</span >
|
||||
);
|
||||
<MenuItem onClick={handleClose}>
|
||||
<form action="/c/logout" method="POST" id="logoutFrom"></form>
|
||||
<Link color="textSecondary" href="/c/logout" onClick={(e) => handleLogout(e)}>
|
||||
<ListItemIcon>
|
||||
<ExitToAppOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="menu.signout" defaultMessage="Sign Out" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
{action == 'change-password' && <ChangePasswordDialog onClose={() => setAction(undefined)} />}
|
||||
{action == 'account-info' && <AccountInfoDialog onClose={() => setAction(undefined)} />}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountMenu;
|
||||
|
@ -18,139 +18,139 @@ import MenuItem from '@mui/material/MenuItem';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import Divider from '@mui/material/Divider';
|
||||
export type ActionType =
|
||||
| 'open'
|
||||
| 'share'
|
||||
| 'import'
|
||||
| 'delete'
|
||||
| 'info'
|
||||
| 'create'
|
||||
| 'duplicate'
|
||||
| 'export'
|
||||
| 'label'
|
||||
| 'rename'
|
||||
| 'print'
|
||||
| 'info'
|
||||
| 'publish'
|
||||
| 'history'
|
||||
| undefined;
|
||||
| 'open'
|
||||
| 'share'
|
||||
| 'import'
|
||||
| 'delete'
|
||||
| 'info'
|
||||
| 'create'
|
||||
| 'duplicate'
|
||||
| 'export'
|
||||
| 'label'
|
||||
| 'rename'
|
||||
| 'print'
|
||||
| 'info'
|
||||
| 'publish'
|
||||
| 'history'
|
||||
| undefined;
|
||||
|
||||
interface ActionProps {
|
||||
onClose: (action: ActionType) => void;
|
||||
anchor?: HTMLElement;
|
||||
mapId?: number;
|
||||
onClose: (action: ActionType) => void;
|
||||
anchor?: HTMLElement;
|
||||
mapId?: number;
|
||||
}
|
||||
|
||||
const ActionChooser = (props: ActionProps): React.ReactElement => {
|
||||
const { anchor, onClose, mapId } = props;
|
||||
const { anchor, onClose, mapId } = props;
|
||||
|
||||
const handleOnClose = (
|
||||
action: ActionType
|
||||
): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
||||
return (event): void => {
|
||||
event.stopPropagation();
|
||||
onClose(action);
|
||||
};
|
||||
const handleOnClose = (
|
||||
action: ActionType,
|
||||
): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
||||
return (event): void => {
|
||||
event.stopPropagation();
|
||||
onClose(action);
|
||||
};
|
||||
};
|
||||
|
||||
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined;
|
||||
return (
|
||||
<Menu
|
||||
anchorEl={anchor}
|
||||
keepMounted
|
||||
open={Boolean(anchor)}
|
||||
onClose={handleOnClose(undefined)}
|
||||
elevation={1}
|
||||
>
|
||||
<MenuItem onClick={handleOnClose('open')} style={{ width: '220px' }}>
|
||||
<ListItemIcon>
|
||||
<DescriptionOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.open" defaultMessage="Open" />
|
||||
</MenuItem>
|
||||
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined;
|
||||
return (
|
||||
<Menu
|
||||
anchorEl={anchor}
|
||||
keepMounted
|
||||
open={Boolean(anchor)}
|
||||
onClose={handleOnClose(undefined)}
|
||||
elevation={1}
|
||||
>
|
||||
<MenuItem onClick={handleOnClose('open')} style={{ width: '220px' }}>
|
||||
<ListItemIcon>
|
||||
<DescriptionOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.open" defaultMessage="Open" />
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
<Divider />
|
||||
|
||||
<MenuItem onClick={handleOnClose('duplicate')}>
|
||||
<ListItemIcon>
|
||||
<FileCopyOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleOnClose('duplicate')}>
|
||||
<ListItemIcon>
|
||||
<FileCopyOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
||||
</MenuItem>
|
||||
|
||||
{role == 'owner' && (
|
||||
<MenuItem onClick={handleOnClose('rename')}>
|
||||
<ListItemIcon>
|
||||
<EditOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.rename" defaultMessage="Rename" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{role == 'owner' && (
|
||||
<MenuItem onClick={handleOnClose('rename')}>
|
||||
<ListItemIcon>
|
||||
<EditOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.rename" defaultMessage="Rename" />
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<MenuItem onClick={handleOnClose('label')}>
|
||||
<ListItemIcon>
|
||||
<LabelOutlined />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.label" defaultMessage="Add Label" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleOnClose('label')}>
|
||||
<ListItemIcon>
|
||||
<LabelOutlined />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.label" defaultMessage="Add Label" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClose('delete')}>
|
||||
<ListItemIcon>
|
||||
<DeleteOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.delete" defaultMessage="Delete" />
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleOnClose('delete')}>
|
||||
<ListItemIcon>
|
||||
<DeleteOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.delete" defaultMessage="Delete" />
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
|
||||
<MenuItem onClick={handleOnClose('export')}>
|
||||
<ListItemIcon>
|
||||
<CloudDownloadOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.export" defaultMessage="Export" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleOnClose('export')}>
|
||||
<ListItemIcon>
|
||||
<CloudDownloadOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.export" defaultMessage="Export" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClose('print')}>
|
||||
<ListItemIcon>
|
||||
<PrintOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.print" defaultMessage="Print" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleOnClose('print')}>
|
||||
<ListItemIcon>
|
||||
<PrintOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.print" defaultMessage="Print" />
|
||||
</MenuItem>
|
||||
|
||||
{role == 'owner' && (
|
||||
<MenuItem onClick={handleOnClose('publish')}>
|
||||
<ListItemIcon>
|
||||
<PublicOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.publish" defaultMessage="Publish" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{role == 'owner' && (
|
||||
<MenuItem onClick={handleOnClose('publish')}>
|
||||
<ListItemIcon>
|
||||
<PublicOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.publish" defaultMessage="Publish" />
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{role == 'owner' && (
|
||||
<MenuItem onClick={handleOnClose('share')}>
|
||||
<ListItemIcon>
|
||||
<ShareOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.share" defaultMessage="Share" />
|
||||
</MenuItem>
|
||||
)}
|
||||
<Divider />
|
||||
{role == 'owner' && (
|
||||
<MenuItem onClick={handleOnClose('share')}>
|
||||
<ListItemIcon>
|
||||
<ShareOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.share" defaultMessage="Share" />
|
||||
</MenuItem>
|
||||
)}
|
||||
<Divider />
|
||||
|
||||
<MenuItem onClick={handleOnClose('info')}>
|
||||
<ListItemIcon>
|
||||
<InfoOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.info" defaultMessage="Info" />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleOnClose('info')}>
|
||||
<ListItemIcon>
|
||||
<InfoOutlinedIcon />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.info" defaultMessage="Info" />
|
||||
</MenuItem>
|
||||
|
||||
{role != 'viewer' && (
|
||||
<MenuItem onClick={handleOnClose('history')}>
|
||||
<ListItemIcon>
|
||||
<HistoryOutlined/>
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.history" defaultMessage="History" />
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
{role != 'viewer' && (
|
||||
<MenuItem onClick={handleOnClose('history')}>
|
||||
<ListItemIcon>
|
||||
<HistoryOutlined />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="action.history" defaultMessage="History" />
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionChooser;
|
||||
|
@ -8,89 +8,89 @@ import { StyledButton, NewLabelContainer, NewLabelColor, CreateLabel } from './s
|
||||
import { Tooltip } from '@mui/material';
|
||||
|
||||
const labelColors = [
|
||||
'#00b327',
|
||||
'#0565ff',
|
||||
'#2d2dd6',
|
||||
'#6a00ba',
|
||||
'#ad1599',
|
||||
'#ff1e35',
|
||||
'#ff6600',
|
||||
'#ffff47',
|
||||
'#00b327',
|
||||
'#0565ff',
|
||||
'#2d2dd6',
|
||||
'#6a00ba',
|
||||
'#ad1599',
|
||||
'#ff1e35',
|
||||
'#ff6600',
|
||||
'#ffff47',
|
||||
];
|
||||
|
||||
type AddLabelFormProps = {
|
||||
onAdd: (newLabel: Label) => void;
|
||||
onAdd: (newLabel: Label) => void;
|
||||
};
|
||||
|
||||
const AddLabelDialog = ({ onAdd }: AddLabelFormProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const [createLabelColorIndex, setCreateLabelColorIndex] = React.useState(
|
||||
Math.floor(Math.random() * labelColors.length)
|
||||
);
|
||||
const [newLabelTitle, setNewLabelTitle] = React.useState('');
|
||||
const intl = useIntl();
|
||||
const [createLabelColorIndex, setCreateLabelColorIndex] = React.useState(
|
||||
Math.floor(Math.random() * labelColors.length),
|
||||
);
|
||||
const [newLabelTitle, setNewLabelTitle] = React.useState('');
|
||||
|
||||
const newLabelColor = labelColors[createLabelColorIndex];
|
||||
const newLabelColor = labelColors[createLabelColorIndex];
|
||||
|
||||
const setNextLabelColorIndex = () => {
|
||||
const nextIndex = labelColors[createLabelColorIndex + 1] ? createLabelColorIndex + 1 : 0;
|
||||
setCreateLabelColorIndex(nextIndex);
|
||||
};
|
||||
const setNextLabelColorIndex = () => {
|
||||
const nextIndex = labelColors[createLabelColorIndex + 1] ? createLabelColorIndex + 1 : 0;
|
||||
setCreateLabelColorIndex(nextIndex);
|
||||
};
|
||||
|
||||
const handleSubmitNew = () => {
|
||||
onAdd({
|
||||
title: newLabelTitle,
|
||||
color: newLabelColor,
|
||||
id: 0,
|
||||
});
|
||||
setNewLabelTitle('');
|
||||
setNextLabelColorIndex();
|
||||
};
|
||||
const handleSubmitNew = () => {
|
||||
onAdd({
|
||||
title: newLabelTitle,
|
||||
color: newLabelColor,
|
||||
id: 0,
|
||||
});
|
||||
setNewLabelTitle('');
|
||||
setNextLabelColorIndex();
|
||||
};
|
||||
|
||||
return (
|
||||
<CreateLabel>
|
||||
<NewLabelContainer>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={intl.formatMessage({
|
||||
id: 'label.change-color',
|
||||
defaultMessage: 'Change label color',
|
||||
})}
|
||||
>
|
||||
<NewLabelColor
|
||||
htmlColor={newLabelColor}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setNextLabelColorIndex();
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<TextField
|
||||
variant="standard"
|
||||
label={intl.formatMessage({
|
||||
id: 'label.add-placeholder',
|
||||
defaultMessage: 'Label title',
|
||||
})}
|
||||
onChange={(e) => setNewLabelTitle(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSubmitNew();
|
||||
}
|
||||
}}
|
||||
value={newLabelTitle}
|
||||
/>
|
||||
<StyledButton
|
||||
onClick={() => handleSubmitNew()}
|
||||
disabled={!newLabelTitle.length}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'label.add-button',
|
||||
defaultMessage: 'Add label',
|
||||
})}
|
||||
>
|
||||
<AddIcon />
|
||||
</StyledButton>
|
||||
</NewLabelContainer>
|
||||
</CreateLabel>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<CreateLabel>
|
||||
<NewLabelContainer>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={intl.formatMessage({
|
||||
id: 'label.change-color',
|
||||
defaultMessage: 'Change label color',
|
||||
})}
|
||||
>
|
||||
<NewLabelColor
|
||||
htmlColor={newLabelColor}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setNextLabelColorIndex();
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<TextField
|
||||
variant="standard"
|
||||
label={intl.formatMessage({
|
||||
id: 'label.add-placeholder',
|
||||
defaultMessage: 'Label title',
|
||||
})}
|
||||
onChange={(e) => setNewLabelTitle(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSubmitNew();
|
||||
}
|
||||
}}
|
||||
value={newLabelTitle}
|
||||
/>
|
||||
<StyledButton
|
||||
onClick={() => handleSubmitNew()}
|
||||
disabled={!newLabelTitle.length}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'label.add-button',
|
||||
defaultMessage: 'Add label',
|
||||
})}
|
||||
>
|
||||
<AddIcon />
|
||||
</StyledButton>
|
||||
</NewLabelContainer>
|
||||
</CreateLabel>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddLabelDialog;
|
||||
|
@ -10,85 +10,82 @@ import { useDispatch } from 'react-redux';
|
||||
import { disableHotkeys, enableHotkeys } from '../../../../redux/editorSlice';
|
||||
|
||||
export type DialogProps = {
|
||||
onClose: () => void;
|
||||
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
|
||||
children: unknown;
|
||||
error?: ErrorInfo;
|
||||
onClose: () => void;
|
||||
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
|
||||
children: unknown;
|
||||
error?: ErrorInfo;
|
||||
|
||||
title: string;
|
||||
description?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
submitButton?: string;
|
||||
actionUrl?: string;
|
||||
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
||||
PaperProps?: Partial<PaperProps>;
|
||||
submitButton?: string;
|
||||
actionUrl?: string;
|
||||
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
||||
PaperProps?: Partial<PaperProps>;
|
||||
};
|
||||
|
||||
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(disableHotkeys());
|
||||
return () => {
|
||||
dispatch(enableHotkeys())
|
||||
};
|
||||
}, []);
|
||||
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props;
|
||||
|
||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (onSubmit) {
|
||||
onSubmit(e);
|
||||
}
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(disableHotkeys());
|
||||
return () => {
|
||||
dispatch(enableHotkeys());
|
||||
};
|
||||
}, []);
|
||||
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props;
|
||||
|
||||
const description = props.description ? (
|
||||
<DialogContentText>{props.description}</DialogContentText>
|
||||
) : null;
|
||||
return (
|
||||
<div>
|
||||
<StyledDialog
|
||||
open={true}
|
||||
onClose={onClose}
|
||||
maxWidth={maxWidth}
|
||||
PaperProps={PaperProps}
|
||||
fullWidth={true}
|
||||
>
|
||||
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
||||
<StyledDialogTitle>{props.title}</StyledDialogTitle>
|
||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (onSubmit) {
|
||||
onSubmit(e);
|
||||
}
|
||||
};
|
||||
|
||||
<StyledDialogContent>
|
||||
{description}
|
||||
<GlobalError error={props.error} />
|
||||
{props.children}
|
||||
</StyledDialogContent>
|
||||
const description = props.description ? (
|
||||
<DialogContentText>{props.description}</DialogContentText>
|
||||
) : null;
|
||||
return (
|
||||
<div>
|
||||
<StyledDialog
|
||||
open={true}
|
||||
onClose={onClose}
|
||||
maxWidth={maxWidth}
|
||||
PaperProps={PaperProps}
|
||||
fullWidth={true}
|
||||
>
|
||||
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
||||
<StyledDialogTitle>{props.title}</StyledDialogTitle>
|
||||
|
||||
<StyledDialogActions>
|
||||
<Button type="button" color="primary" size="medium" onClick={onClose}>
|
||||
{onSubmit ? (
|
||||
<FormattedMessage
|
||||
id="action.cancel-button"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
||||
)}
|
||||
</Button>
|
||||
{onSubmit && (
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
disableElevation={true}
|
||||
>
|
||||
{props.submitButton}
|
||||
</Button>
|
||||
)}
|
||||
</StyledDialogActions>
|
||||
</form>
|
||||
</StyledDialog>
|
||||
</div>
|
||||
);
|
||||
<StyledDialogContent>
|
||||
{description}
|
||||
<GlobalError error={props.error} />
|
||||
{props.children}
|
||||
</StyledDialogContent>
|
||||
|
||||
<StyledDialogActions>
|
||||
<Button type="button" color="primary" size="medium" onClick={onClose}>
|
||||
{onSubmit ? (
|
||||
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
||||
) : (
|
||||
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
||||
)}
|
||||
</Button>
|
||||
{onSubmit && (
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
disableElevation={true}
|
||||
>
|
||||
{props.submitButton}
|
||||
</Button>
|
||||
)}
|
||||
</StyledDialogActions>
|
||||
</form>
|
||||
</StyledDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseDialog;
|
||||
|
@ -10,102 +10,102 @@ import Input from '../../../form/input';
|
||||
import BaseDialog from '../base-dialog';
|
||||
|
||||
export type CreateModel = {
|
||||
title: string;
|
||||
description?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type CreateProps = {
|
||||
onClose: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const defaultModel: CreateModel = { title: '', description: '' };
|
||||
const CreateDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<CreateModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<CreateModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
|
||||
const mutation = useMutation<number, ErrorInfo, CreateModel>(
|
||||
(model: CreateModel) => {
|
||||
return client.createMap(model);
|
||||
},
|
||||
{
|
||||
onSuccess: (mapId: number) => {
|
||||
window.location.href = `/c/maps/${mapId}/edit`;
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
const mutation = useMutation<number, ErrorInfo, CreateModel>(
|
||||
(model: CreateModel) => {
|
||||
return client.createMap(model);
|
||||
},
|
||||
{
|
||||
onSuccess: (mapId: number) => {
|
||||
window.location.href = `/c/maps/${mapId}/edit`;
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({
|
||||
id: 'create.title',
|
||||
defaultMessage: 'Create a new mindmap',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'create.description',
|
||||
defaultMessage: 'Please, fill the new map name and description.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
maxLength={60}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({
|
||||
id: 'create.title',
|
||||
defaultMessage: 'Create a new mindmap',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'create.description',
|
||||
defaultMessage: 'Please, fill the new map name and description.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
maxLength={60}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
rows={3}
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
rows={3}
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateDialog;
|
||||
|
@ -10,50 +10,53 @@ import Alert from '@mui/material/Alert';
|
||||
import AlertTitle from '@mui/material/AlertTitle';
|
||||
|
||||
const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const mutation = useMutation((id: number) => client.deleteMap(id), {
|
||||
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
||||
onError: (error: ErrorInfo) => {
|
||||
setError(error);
|
||||
},
|
||||
});
|
||||
const mutation = useMutation((id: number) => client.deleteMap(id), {
|
||||
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
||||
onError: (error: ErrorInfo) => {
|
||||
setError(error);
|
||||
},
|
||||
});
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnSubmit = (): void => {
|
||||
mutation.mutate(mapId);
|
||||
};
|
||||
const handleOnSubmit = (): void => {
|
||||
mutation.mutate(mapId);
|
||||
};
|
||||
|
||||
const { map } = fetchMapById(mapId)
|
||||
const alertTitle = `${intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })} ${map?.title}`;
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
error={error}
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>{alertTitle}</AlertTitle>
|
||||
<FormattedMessage
|
||||
id="action.delete-description"
|
||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||
/>
|
||||
</Alert>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
const { map } = fetchMapById(mapId);
|
||||
const alertTitle = `${intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})} ${map?.title}`;
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
error={error}
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>{alertTitle}</AlertTitle>
|
||||
<FormattedMessage
|
||||
id="action.delete-description"
|
||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||
/>
|
||||
</Alert>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteDialog;
|
||||
|
@ -9,55 +9,52 @@ import BaseDialog from '../base-dialog';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import AlertTitle from '@mui/material/AlertTitle';
|
||||
|
||||
const DeleteMultiselectDialog = ({
|
||||
onClose,
|
||||
mapsId,
|
||||
}: MultiDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const DeleteMultiselectDialog = ({ onClose, mapsId }: MultiDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids), {
|
||||
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
||||
onError: (error) => {
|
||||
console.error(`Unexpected error ${error}`);
|
||||
},
|
||||
});
|
||||
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids), {
|
||||
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
||||
onError: (error) => {
|
||||
console.error(`Unexpected error ${error}`);
|
||||
},
|
||||
});
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnSubmit = (): void => {
|
||||
mutation.mutate(mapsId);
|
||||
};
|
||||
const handleOnSubmit = (): void => {
|
||||
mutation.mutate(mapsId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>
|
||||
<FormattedMessage
|
||||
id="deletem.title"
|
||||
defaultMessage="All selected maps will be deleted"
|
||||
/>
|
||||
</AlertTitle>
|
||||
<FormattedMessage
|
||||
id="action.delete-description"
|
||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||
/>
|
||||
</Alert>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>
|
||||
<FormattedMessage
|
||||
id="deletem.title"
|
||||
defaultMessage="All selected maps will be deleted"
|
||||
/>
|
||||
</AlertTitle>
|
||||
<FormattedMessage
|
||||
id="action.delete-description"
|
||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||
/>
|
||||
</Alert>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteMultiselectDialog;
|
||||
|
@ -11,104 +11,104 @@ import { SimpleDialogProps } from '..';
|
||||
import BaseDialog from '../base-dialog';
|
||||
|
||||
export type DuplicateModel = {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
const defaultModel: DuplicateModel = { title: '', description: '', id: -1 };
|
||||
const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const service: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<DuplicateModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const service: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<DuplicateModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const intl = useIntl();
|
||||
const intl = useIntl();
|
||||
|
||||
const mutation = useMutation<number, ErrorInfo, DuplicateModel>(
|
||||
(model: DuplicateModel) => {
|
||||
const { id, ...rest } = model;
|
||||
return service.duplicateMap(id, rest);
|
||||
},
|
||||
{
|
||||
onSuccess: (mapId) => {
|
||||
window.location.href = `/c/maps/${mapId}/edit`;
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
const mutation = useMutation<number, ErrorInfo, DuplicateModel>(
|
||||
(model: DuplicateModel) => {
|
||||
const { id, ...rest } = model;
|
||||
return service.duplicateMap(id, rest);
|
||||
},
|
||||
{
|
||||
onSuccess: (mapId) => {
|
||||
window.location.href = `/c/maps/${mapId}/edit`;
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
|
||||
const { map } = fetchMapById(mapId);
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
setModel(map);
|
||||
}
|
||||
}, [mapId]);
|
||||
const { map } = fetchMapById(mapId);
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
setModel(map);
|
||||
}
|
||||
}, [mapId]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'rename.description',
|
||||
defaultMessage: 'Please, fill the new map name and description.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'duplicate.title',
|
||||
defaultMessage: 'Duplicate',
|
||||
})}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'rename.description',
|
||||
defaultMessage: 'Please, fill the new map name and description.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'duplicate.title',
|
||||
defaultMessage: 'Duplicate',
|
||||
})}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DuplicateDialog;
|
||||
|
@ -10,7 +10,13 @@ import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Radio from '@mui/material/Radio';
|
||||
import Select from '@mui/material/Select';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import { Designer, TextExporterFactory, ImageExporterFactory, Exporter, Mindmap } from '@wisemapping/mindplot';
|
||||
import {
|
||||
Designer,
|
||||
TextExporterFactory,
|
||||
ImageExporterFactory,
|
||||
Exporter,
|
||||
Mindmap,
|
||||
} from '@wisemapping/mindplot';
|
||||
import Client from '../../../../classes/client';
|
||||
import { activeInstance } from '../../../../redux/clientSlice';
|
||||
|
||||
@ -22,263 +28,273 @@ type ExportFormat = 'svg' | 'jpg' | 'png' | 'txt' | 'mm' | 'wxml' | 'xls' | 'md'
|
||||
type ExportGroup = 'image' | 'document' | 'mindmap-tool';
|
||||
|
||||
type ExportDialogProps = {
|
||||
mapId: number;
|
||||
enableImgExport: boolean;
|
||||
svgXml?: string;
|
||||
onClose: () => void;
|
||||
mapId: number;
|
||||
enableImgExport: boolean;
|
||||
svgXml?: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const ExportDialog = ({
|
||||
mapId,
|
||||
onClose,
|
||||
enableImgExport
|
||||
mapId,
|
||||
onClose,
|
||||
enableImgExport,
|
||||
}: ExportDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
||||
const { map } = fetchMapById(mapId);
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const intl = useIntl();
|
||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
||||
const { map } = fetchMapById(mapId);
|
||||
const client: Client = useSelector(activeInstance);
|
||||
|
||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||
enableImgExport ? 'image' : 'document'
|
||||
);
|
||||
const [exportFormat, setExportFormat] = React.useState<ExportFormat>(
|
||||
enableImgExport ? 'svg' : 'txt'
|
||||
);
|
||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||
enableImgExport ? 'image' : 'document',
|
||||
);
|
||||
const [exportFormat, setExportFormat] = React.useState<ExportFormat>(
|
||||
enableImgExport ? 'svg' : 'txt',
|
||||
);
|
||||
|
||||
const [zoomToFit, setZoomToFit] = React.useState<boolean>(true)
|
||||
const [zoomToFit, setZoomToFit] = React.useState<boolean>(true);
|
||||
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
|
||||
const handleOnExportFormatChange = (event) => {
|
||||
setExportFormat(event.target.value);
|
||||
};
|
||||
const handleOnExportFormatChange = (event) => {
|
||||
setExportFormat(event.target.value);
|
||||
};
|
||||
|
||||
const handleOnGroupChange = (event) => {
|
||||
const value: ExportGroup = event.target.value;
|
||||
setExportGroup(value);
|
||||
const handleOnGroupChange = (event) => {
|
||||
const value: ExportGroup = event.target.value;
|
||||
setExportGroup(value);
|
||||
|
||||
let defaultFormat: ExportFormat;
|
||||
switch (value) {
|
||||
case 'document':
|
||||
defaultFormat = 'txt';
|
||||
break;
|
||||
case 'image':
|
||||
defaultFormat = 'svg';
|
||||
break;
|
||||
case 'mindmap-tool':
|
||||
defaultFormat = 'wxml';
|
||||
break;
|
||||
}
|
||||
setExportFormat(defaultFormat);
|
||||
};
|
||||
let defaultFormat: ExportFormat;
|
||||
switch (value) {
|
||||
case 'document':
|
||||
defaultFormat = 'txt';
|
||||
break;
|
||||
case 'image':
|
||||
defaultFormat = 'svg';
|
||||
break;
|
||||
case 'mindmap-tool':
|
||||
defaultFormat = 'wxml';
|
||||
break;
|
||||
}
|
||||
setExportFormat(defaultFormat);
|
||||
};
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnSubmit = (): void => {
|
||||
setSubmit(true);
|
||||
};
|
||||
const handleOnSubmit = (): void => {
|
||||
setSubmit(true);
|
||||
};
|
||||
|
||||
const handleOnZoomToFit = (): void => {
|
||||
setZoomToFit(!zoomToFit);
|
||||
};
|
||||
const handleOnZoomToFit = (): void => {
|
||||
setZoomToFit(!zoomToFit);
|
||||
};
|
||||
|
||||
const exporter = (formatType: ExportFormat): Promise<string> => {
|
||||
let svgElement: Element | null = null;
|
||||
let size: SizeType;
|
||||
let mindmap: Mindmap;
|
||||
const exporter = (formatType: ExportFormat): Promise<string> => {
|
||||
let svgElement: Element | null = null;
|
||||
let size: SizeType;
|
||||
let mindmap: Mindmap;
|
||||
|
||||
const designer: Designer = global.designer;
|
||||
if (designer != null) {
|
||||
// Depending on the type of export. It will require differt POST.
|
||||
const workspace = designer.getWorkSpace();
|
||||
svgElement = workspace.getSVGElement();
|
||||
size = { width: window.innerWidth, height: window.innerHeight };
|
||||
mindmap = designer.getMindmap();
|
||||
} else {
|
||||
mindmap = client.fetchMindmap(mapId);
|
||||
}
|
||||
const designer: Designer = global.designer;
|
||||
if (designer != null) {
|
||||
// Depending on the type of export. It will require differt POST.
|
||||
const workspace = designer.getWorkSpace();
|
||||
svgElement = workspace.getSVGElement();
|
||||
size = { width: window.innerWidth, height: window.innerHeight };
|
||||
mindmap = designer.getMindmap();
|
||||
} else {
|
||||
mindmap = client.fetchMindmap(mapId);
|
||||
}
|
||||
|
||||
let exporter: Exporter;
|
||||
switch (formatType) {
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
case 'svg': {
|
||||
exporter = ImageExporterFactory.create(formatType, svgElement, size.width, size.height, zoomToFit);
|
||||
break;
|
||||
}
|
||||
case 'wxml':
|
||||
case 'mm':
|
||||
case 'md':
|
||||
case 'txt': {
|
||||
exporter = TextExporterFactory.create(formatType, mindmap);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported encoding');
|
||||
}
|
||||
let exporter: Exporter;
|
||||
switch (formatType) {
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
case 'svg': {
|
||||
exporter = ImageExporterFactory.create(
|
||||
formatType,
|
||||
svgElement,
|
||||
size.width,
|
||||
size.height,
|
||||
zoomToFit,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'wxml':
|
||||
case 'mm':
|
||||
case 'md':
|
||||
case 'txt': {
|
||||
exporter = TextExporterFactory.create(formatType, mindmap);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported encoding');
|
||||
}
|
||||
|
||||
return exporter.exportAndEncode();
|
||||
};
|
||||
return exporter.exportAndEncode();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (submit) {
|
||||
exporter(exportFormat)
|
||||
.then((url: string) => {
|
||||
// Create hidden anchor to force download ...
|
||||
const anchor: HTMLAnchorElement = document.createElement('a');
|
||||
anchor.style.display = 'display: none';
|
||||
anchor.download = `${map?.title}.${exportFormat}`;
|
||||
anchor.href = url;
|
||||
document.body.appendChild(anchor);
|
||||
useEffect(() => {
|
||||
if (submit) {
|
||||
exporter(exportFormat)
|
||||
.then((url: string) => {
|
||||
// Create hidden anchor to force download ...
|
||||
const anchor: HTMLAnchorElement = document.createElement('a');
|
||||
anchor.style.display = 'display: none';
|
||||
anchor.download = `${map?.title}.${exportFormat}`;
|
||||
anchor.href = url;
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
// Trigger click ...
|
||||
anchor.click();
|
||||
// Trigger click ...
|
||||
anchor.click();
|
||||
|
||||
// Clean up ...
|
||||
URL.revokeObjectURL(url);
|
||||
document.body.removeChild(anchor);
|
||||
}).catch((fail) => {
|
||||
console.error("Unexpected error during export:" + fail);
|
||||
});
|
||||
// Clean up ...
|
||||
URL.revokeObjectURL(url);
|
||||
document.body.removeChild(anchor);
|
||||
})
|
||||
.catch((fail) => {
|
||||
console.error('Unexpected error during export:' + fail);
|
||||
});
|
||||
|
||||
onClose();
|
||||
}
|
||||
}, [submit]);
|
||||
onClose();
|
||||
}
|
||||
}, [submit]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
title={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||
description={intl.formatMessage({ id: 'export.desc', defaultMessage: 'Export this map in the format that you want and start using it in your presentations or sharing by email' })}
|
||||
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||
>
|
||||
{
|
||||
!enableImgExport &&
|
||||
<Alert severity="info">
|
||||
<FormattedMessage
|
||||
id="export.warning"
|
||||
defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar."
|
||||
/>
|
||||
</Alert>
|
||||
}
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
className={classes.label}
|
||||
value="image"
|
||||
disabled={!enableImgExport}
|
||||
control={<Radio color="primary" />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.image',
|
||||
defaultMessage:
|
||||
'Image: Get a graphic representation of your map including all colors and shapes.',
|
||||
})}
|
||||
color="secondary"
|
||||
style={{ fontSize: '9px' }}
|
||||
/>
|
||||
{exportGroup == 'image' && (
|
||||
<>
|
||||
<Select
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
value={exportFormat}
|
||||
className={classes.select}
|
||||
>
|
||||
<MenuItem value="svg" className={classes.menu}>
|
||||
Scalable Vector Graphics (SVG)
|
||||
</MenuItem>
|
||||
<MenuItem value="png" className={classes.menu}>
|
||||
Portable Network Graphics (PNG)
|
||||
</MenuItem>
|
||||
<MenuItem value="jpg" className={classes.menu}>
|
||||
JPEG Image (JPEG)
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormControlLabel
|
||||
className={classes.select}
|
||||
control={<Checkbox checked={zoomToFit} onChange={handleOnZoomToFit} />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.img-center',
|
||||
defaultMessage:
|
||||
'Center and zoom to fit',
|
||||
})} />
|
||||
</>
|
||||
)}
|
||||
</FormControl>
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
title={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'export.desc',
|
||||
defaultMessage:
|
||||
'Export this map in the format that you want and start using it in your presentations or sharing by email',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||
>
|
||||
{!enableImgExport && (
|
||||
<Alert severity="info">
|
||||
<FormattedMessage
|
||||
id="export.warning"
|
||||
defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar."
|
||||
/>
|
||||
</Alert>
|
||||
)}
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
className={classes.label}
|
||||
value="image"
|
||||
disabled={!enableImgExport}
|
||||
control={<Radio color="primary" />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.image',
|
||||
defaultMessage:
|
||||
'Image: Get a graphic representation of your map including all colors and shapes.',
|
||||
})}
|
||||
color="secondary"
|
||||
style={{ fontSize: '9px' }}
|
||||
/>
|
||||
{exportGroup == 'image' && (
|
||||
<>
|
||||
<Select
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
value={exportFormat}
|
||||
className={classes.select}
|
||||
>
|
||||
<MenuItem value="svg" className={classes.menu}>
|
||||
Scalable Vector Graphics (SVG)
|
||||
</MenuItem>
|
||||
<MenuItem value="png" className={classes.menu}>
|
||||
Portable Network Graphics (PNG)
|
||||
</MenuItem>
|
||||
<MenuItem value="jpg" className={classes.menu}>
|
||||
JPEG Image (JPEG)
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<FormControlLabel
|
||||
className={classes.select}
|
||||
control={<Checkbox checked={zoomToFit} onChange={handleOnZoomToFit} />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.img-center',
|
||||
defaultMessage: 'Center and zoom to fit',
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
className={classes.label}
|
||||
value="document"
|
||||
control={<Radio color="primary" />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.document-label',
|
||||
defaultMessage:
|
||||
'Document: Export your mindmap in a self-contained document ready to share',
|
||||
})}
|
||||
color="secondary"
|
||||
/>
|
||||
{exportGroup == 'document' && (
|
||||
<Select
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
value={exportFormat}
|
||||
className={classes.select}
|
||||
>
|
||||
<MenuItem className={classes.select} value="txt">
|
||||
Plain Text File (TXT)
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.select} value="md">
|
||||
Markdown (MD)
|
||||
</MenuItem>
|
||||
{/* <MenuItem className={classes.select} value="xls">
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
className={classes.label}
|
||||
value="document"
|
||||
control={<Radio color="primary" />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.document-label',
|
||||
defaultMessage:
|
||||
'Document: Export your mindmap in a self-contained document ready to share',
|
||||
})}
|
||||
color="secondary"
|
||||
/>
|
||||
{exportGroup == 'document' && (
|
||||
<Select
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
value={exportFormat}
|
||||
className={classes.select}
|
||||
>
|
||||
<MenuItem className={classes.select} value="txt">
|
||||
Plain Text File (TXT)
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.select} value="md">
|
||||
Markdown (MD)
|
||||
</MenuItem>
|
||||
{/* <MenuItem className={classes.select} value="xls">
|
||||
Microsoft Excel (XLS)
|
||||
</MenuItem> */}
|
||||
</Select>
|
||||
)}
|
||||
</FormControl>
|
||||
</Select>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
className={classes.label}
|
||||
value="mindmap-tool"
|
||||
control={<Radio color="primary" />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.document',
|
||||
defaultMessage:
|
||||
'Mindmap Tools: Export your mindmap in thirdparty mindmap tool formats',
|
||||
})}
|
||||
color="secondary"
|
||||
/>
|
||||
{exportGroup == 'mindmap-tool' && (
|
||||
<Select
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
className={classes.select}
|
||||
value={exportFormat}
|
||||
>
|
||||
<MenuItem className={classes.select} value="wxml">
|
||||
WiseMapping (WXML)
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.select} value="mm">
|
||||
Freemind 1.0.1 (MM)
|
||||
</MenuItem>
|
||||
{/* <MenuItem className={classes.select} value="mmap">
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
className={classes.label}
|
||||
value="mindmap-tool"
|
||||
control={<Radio color="primary" />}
|
||||
label={intl.formatMessage({
|
||||
id: 'export.document',
|
||||
defaultMessage:
|
||||
'Mindmap Tools: Export your mindmap in thirdparty mindmap tool formats',
|
||||
})}
|
||||
color="secondary"
|
||||
/>
|
||||
{exportGroup == 'mindmap-tool' && (
|
||||
<Select
|
||||
onChange={handleOnExportFormatChange}
|
||||
variant="outlined"
|
||||
className={classes.select}
|
||||
value={exportFormat}
|
||||
>
|
||||
<MenuItem className={classes.select} value="wxml">
|
||||
WiseMapping (WXML)
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.select} value="mm">
|
||||
Freemind 1.0.1 (MM)
|
||||
</MenuItem>
|
||||
{/* <MenuItem className={classes.select} value="mmap">
|
||||
MindManager (MMAP)
|
||||
</MenuItem> */}
|
||||
</Select>
|
||||
)}
|
||||
</FormControl>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
</Select>
|
||||
)}
|
||||
</FormControl>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExportDialog;
|
||||
|
@ -19,115 +19,94 @@ import Link from '@mui/material/Link';
|
||||
import Paper from '@mui/material/Paper';
|
||||
|
||||
const HistoryDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const queryClient = useQueryClient();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const { data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>(`history-${mapId}`, () => {
|
||||
return client.fetchHistory(mapId);
|
||||
const intl = useIntl();
|
||||
const queryClient = useQueryClient();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const { data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>(`history-${mapId}`, () => {
|
||||
return client.fetchHistory(mapId);
|
||||
});
|
||||
const changeHistory: ChangeHistory[] = data ? data : [];
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
queryClient.invalidateQueries(`history-${mapId}`);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnClick = (event, vid: number): void => {
|
||||
event.preventDefault();
|
||||
client.revertHistory(mapId, vid).then(() => {
|
||||
handleOnClose();
|
||||
});
|
||||
const changeHistory: ChangeHistory[] = data ? data : [];
|
||||
};
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
queryClient.invalidateQueries(`history-${mapId}`);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnClick = (event, vid: number): void => {
|
||||
event.preventDefault();
|
||||
client.revertHistory(mapId, vid).then(() => {
|
||||
handleOnClose();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
title={intl.formatMessage({
|
||||
id: 'action.history-title',
|
||||
defaultMessage: 'Version history',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'action.history-description',
|
||||
defaultMessage: 'List of changes introduced in the last 90 days.',
|
||||
})}
|
||||
>
|
||||
<TableContainer component={Paper} style={{ maxHeight: '200px' }} variant="outlined">
|
||||
<Table size="small" stickyHeader>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="left">
|
||||
<FormattedMessage
|
||||
id="maps.modified-by"
|
||||
defaultMessage="Modified By"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<FormattedMessage
|
||||
id="maps.modified"
|
||||
defaultMessage="Modified"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="left"></TableCell>
|
||||
<TableCell align="left"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{changeHistory.length == 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>
|
||||
<FormattedMessage
|
||||
id="history.no-changes"
|
||||
defaultMessage="There is no changes available"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
changeHistory.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell align="left">{row.lastModificationBy}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Tooltip
|
||||
title={dayjs(row.lastModificationTime).format(
|
||||
'lll'
|
||||
)}
|
||||
placement="bottom-start"
|
||||
>
|
||||
<span>
|
||||
{dayjs(row.lastModificationTime).fromNow()}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Link
|
||||
href={`/c/maps/${mapId}/${row.id}/view`}
|
||||
target="history"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="maps.view"
|
||||
defaultMessage="View"
|
||||
/>
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Link
|
||||
href="#"
|
||||
onClick={(e) => handleOnClick(e, row.id)}>
|
||||
<FormattedMessage
|
||||
id="maps.revert"
|
||||
defaultMessage="Revert"
|
||||
/>
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
title={intl.formatMessage({
|
||||
id: 'action.history-title',
|
||||
defaultMessage: 'Version history',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'action.history-description',
|
||||
defaultMessage: 'List of changes introduced in the last 90 days.',
|
||||
})}
|
||||
>
|
||||
<TableContainer component={Paper} style={{ maxHeight: '200px' }} variant="outlined">
|
||||
<Table size="small" stickyHeader>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="left">
|
||||
<FormattedMessage id="maps.modified-by" defaultMessage="Modified By" />
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<FormattedMessage id="maps.modified" defaultMessage="Modified" />
|
||||
</TableCell>
|
||||
<TableCell align="left"></TableCell>
|
||||
<TableCell align="left"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{changeHistory.length == 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>
|
||||
<FormattedMessage
|
||||
id="history.no-changes"
|
||||
defaultMessage="There is no changes available"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
changeHistory.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell align="left">{row.lastModificationBy}</TableCell>
|
||||
<TableCell align="left">
|
||||
<Tooltip
|
||||
title={dayjs(row.lastModificationTime).format('lll')}
|
||||
placement="bottom-start"
|
||||
>
|
||||
<span>{dayjs(row.lastModificationTime).fromNow()}</span>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Link href={`/c/maps/${mapId}/${row.id}/view`} target="history">
|
||||
<FormattedMessage id="maps.view" defaultMessage="View" />
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Link href="#" onClick={(e) => handleOnClick(e, row.id)}>
|
||||
<FormattedMessage id="maps.revert" defaultMessage="Revert" />
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistoryDialog;
|
||||
|
@ -13,204 +13,206 @@ import Input from '../../../form/input';
|
||||
import BaseDialog from '../base-dialog';
|
||||
|
||||
export type ImportModel = {
|
||||
title: string;
|
||||
description?: string;
|
||||
contentType?: string;
|
||||
content?: null | string;
|
||||
title: string;
|
||||
description?: string;
|
||||
contentType?: string;
|
||||
content?: null | string;
|
||||
};
|
||||
|
||||
export type CreateProps = {
|
||||
onClose: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type ErrorFile = {
|
||||
error: boolean;
|
||||
message: string;
|
||||
}
|
||||
error: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
const defaultModel: ImportModel = { title: '' };
|
||||
const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<ImportModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const [errorFile, setErrorFile] = React.useState<ErrorFile>({error: false, message: ''});
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<ImportModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const [errorFile, setErrorFile] = React.useState<ErrorFile>({ error: false, message: '' });
|
||||
const intl = useIntl();
|
||||
|
||||
const mutation = useMutation<number, ErrorInfo, ImportModel>(
|
||||
(model: ImportModel) => {
|
||||
return client.importMap(model);
|
||||
},
|
||||
{
|
||||
onSuccess: (mapId: number) => {
|
||||
window.location.href = `/c/maps/${mapId}/edit`;
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
const mutation = useMutation<number, ErrorInfo, ImportModel>(
|
||||
(model: ImportModel) => {
|
||||
return client.importMap(model);
|
||||
},
|
||||
{
|
||||
onSuccess: (mapId: number) => {
|
||||
window.location.href = `/c/maps/${mapId}/edit`;
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof ImportModel]: value });
|
||||
};
|
||||
|
||||
const handleOnFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = event?.target?.files;
|
||||
const reader = new FileReader();
|
||||
|
||||
if (files) {
|
||||
const file = files[0];
|
||||
// Closure to capture the file information.
|
||||
reader.onload = (event) => {
|
||||
// Suggest file name ...
|
||||
const fileName = file.name;
|
||||
if (fileName) {
|
||||
const title = fileName.split('.')[0];
|
||||
if (!model.title || 0 === model.title.length) {
|
||||
model.title = title;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
const extensionFile = file.name.split('.').pop();
|
||||
const extensionAccept = ['wxml', 'mm'];
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof ImportModel]: value });
|
||||
};
|
||||
|
||||
const handleOnFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = event?.target?.files;
|
||||
const reader = new FileReader();
|
||||
|
||||
if (files) {
|
||||
const file = files[0];
|
||||
// Closure to capture the file information.
|
||||
reader.onload = (event) => {
|
||||
// Suggest file name ...
|
||||
const fileName = file.name;
|
||||
if (fileName) {
|
||||
const title = fileName.split('.')[0];
|
||||
if (!model.title || 0 === model.title.length) {
|
||||
model.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
const extensionFile = file.name.split('.').pop();
|
||||
const extensionAccept = ['wxml', 'mm'];
|
||||
|
||||
if (!extensionAccept.includes(extensionFile)) {
|
||||
setErrorFile({
|
||||
error: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'import.error-file',
|
||||
defaultMessage: 'Import error {error}',
|
||||
},
|
||||
{
|
||||
error: 'You can import WiseMapping and Freemind maps to your list of maps. Select the file you want to import.'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
model.contentType = 'application/xml'
|
||||
|
||||
const fileContent = event?.target?.result;
|
||||
const mapConent: string = typeof fileContent === 'string' ? fileContent : fileContent.toString();
|
||||
|
||||
try {
|
||||
const importer: Importer = TextImporterFactory.create(extensionFile, mapConent)
|
||||
|
||||
importer.import(model.title, model.description)
|
||||
.then(res => {
|
||||
model.content = res;
|
||||
setModel({ ...model });
|
||||
})
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
setErrorFile({
|
||||
error: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'import.error-file',
|
||||
defaultMessage: 'Import error {error}',
|
||||
},
|
||||
{
|
||||
error: e.message
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Read in the image file as a data URL.
|
||||
reader.readAsText(file);
|
||||
if (!extensionAccept.includes(extensionFile)) {
|
||||
setErrorFile({
|
||||
error: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'import.error-file',
|
||||
defaultMessage: 'Import error {error}',
|
||||
},
|
||||
{
|
||||
error:
|
||||
'You can import WiseMapping and Freemind maps to your list of maps. Select the file you want to import.',
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({
|
||||
id: 'import.title',
|
||||
defaultMessage: 'Import existing mindmap',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'import.description',
|
||||
defaultMessage:
|
||||
'You can import WiseMapping and Freemind maps to your list of maps. Select the file you want to import.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'import.button', defaultMessage: 'Create' })}
|
||||
model.contentType = 'application/xml';
|
||||
|
||||
const fileContent = event?.target?.result;
|
||||
const mapConent: string =
|
||||
typeof fileContent === 'string' ? fileContent : fileContent.toString();
|
||||
|
||||
try {
|
||||
const importer: Importer = TextImporterFactory.create(extensionFile, mapConent);
|
||||
|
||||
importer.import(model.title, model.description).then((res) => {
|
||||
model.content = res;
|
||||
setModel({ ...model });
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
setErrorFile({
|
||||
error: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'import.error-file',
|
||||
defaultMessage: 'Import error {error}',
|
||||
},
|
||||
{
|
||||
error: e.message,
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Read in the image file as a data URL.
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({
|
||||
id: 'import.title',
|
||||
defaultMessage: 'Import existing mindmap',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'import.description',
|
||||
defaultMessage:
|
||||
'You can import WiseMapping and Freemind maps to your list of maps. Select the file you want to import.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'import.button', defaultMessage: 'Create' })}
|
||||
>
|
||||
{errorFile.error && (
|
||||
<Alert severity="error">
|
||||
<p>{errorFile.message}</p>
|
||||
</Alert>
|
||||
)}
|
||||
<FormControl fullWidth={true}>
|
||||
<input
|
||||
accept=".wxml,.mm"
|
||||
id="contained-button-file"
|
||||
type="file"
|
||||
required={true}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleOnFileChange}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<label htmlFor="contained-button-file">
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
component="span"
|
||||
style={{ margin: '10px 5px', width: '100%' }}
|
||||
>
|
||||
{errorFile.error &&
|
||||
<Alert severity='error'>
|
||||
<p>{errorFile.message}</p>
|
||||
</Alert>
|
||||
}
|
||||
<FormControl fullWidth={true}>
|
||||
<input
|
||||
accept=".wxml,.mm"
|
||||
id="contained-button-file"
|
||||
type="file"
|
||||
required={true}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleOnFileChange}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<label htmlFor="contained-button-file">
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
component="span"
|
||||
style={{ margin: '10px 5px', width: '100%' }}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="maps.choose-file"
|
||||
defaultMessage="Choose a file"
|
||||
/>
|
||||
</Button>
|
||||
</label>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
<FormattedMessage id="maps.choose-file" defaultMessage="Choose a file" />
|
||||
</Button>
|
||||
</label>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImportDialog;
|
||||
|
@ -15,87 +15,87 @@ import ShareDialog from './share-dialog';
|
||||
import LabelDialog from './label-dialog';
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
|
||||
export type BasicMapInfo = {
|
||||
name: string;
|
||||
description: string | undefined;
|
||||
name: string;
|
||||
description: string | undefined;
|
||||
};
|
||||
|
||||
type ActionDialogProps = {
|
||||
action?: ActionType;
|
||||
mapsId: number[];
|
||||
onClose: (success?: boolean) => void;
|
||||
fromEditor: boolean;
|
||||
action?: ActionType;
|
||||
mapsId: number[];
|
||||
onClose: (success?: boolean) => void;
|
||||
fromEditor: boolean;
|
||||
};
|
||||
|
||||
const ActionDispatcher = ({ mapsId, action, onClose, fromEditor }: ActionDialogProps): React.ReactElement => {
|
||||
const ActionDispatcher = ({
|
||||
mapsId,
|
||||
action,
|
||||
onClose,
|
||||
fromEditor,
|
||||
}: ActionDialogProps): React.ReactElement => {
|
||||
useEffect(() => {
|
||||
ReactGA.event({
|
||||
category: 'map metadata',
|
||||
action: action,
|
||||
nonInteraction: true,
|
||||
});
|
||||
}, [action]);
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.event({
|
||||
category: 'map metadata',
|
||||
action: action,
|
||||
nonInteraction: true
|
||||
const handleOnClose = (success?: boolean): void => {
|
||||
onClose(success);
|
||||
};
|
||||
|
||||
});
|
||||
}, [action]);
|
||||
switch (action) {
|
||||
case 'open':
|
||||
window.location.href = `/c/maps/${mapsId}/edit`;
|
||||
break;
|
||||
case 'print':
|
||||
window.open(`/c/maps/${mapsId}/print`, 'print');
|
||||
break;
|
||||
}
|
||||
|
||||
const handleOnClose = (success?: boolean): void => {
|
||||
onClose(success);
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
case 'open':
|
||||
window.location.href = `/c/maps/${mapsId}/edit`;
|
||||
break;
|
||||
case 'print':
|
||||
window.open(`/c/maps/${mapsId}/print`, 'print');
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||
{action === 'delete' && mapsId.length == 1 && (
|
||||
<DeleteDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
||||
)}
|
||||
{action === 'delete' && mapsId.length > 1 && (
|
||||
<DeleteMultiselectDialog onClose={handleOnClose} mapsId={mapsId} />
|
||||
)}
|
||||
{action === 'rename' && <RenameDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'duplicate' && (
|
||||
<DuplicateDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
||||
)}
|
||||
{action === 'history' && <HistoryDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'import' && <ImportDialog onClose={handleOnClose} />}
|
||||
{action === 'publish' && <PublishDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'info' && <InfoDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||
{action === 'export' && (
|
||||
<ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={fromEditor} />
|
||||
)}
|
||||
{action === 'share' && <ShareDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'label' && <LabelDialog onClose={handleOnClose} mapsId={mapsId} />}
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span>
|
||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||
{action === 'delete' && mapsId.length == 1 && (
|
||||
<DeleteDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
||||
)}
|
||||
{action === 'delete' && mapsId.length > 1 && (
|
||||
<DeleteMultiselectDialog onClose={handleOnClose} mapsId={mapsId} />
|
||||
)}
|
||||
{action === 'rename' && <RenameDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'duplicate' && <DuplicateDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'history' && <HistoryDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'import' && <ImportDialog onClose={handleOnClose} />}
|
||||
{action === 'publish' && <PublishDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'info' && <InfoDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||
{action === 'export' && (
|
||||
<ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={fromEditor} />
|
||||
)}
|
||||
{action === 'share' && <ShareDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||
{action === 'label' && <LabelDialog onClose={handleOnClose} mapsId={mapsId} />}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
ActionDispatcher.defaultProps = {
|
||||
fromEditor: false,
|
||||
fromEditor: false,
|
||||
};
|
||||
|
||||
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
||||
queryClient.invalidateQueries('maps');
|
||||
onClose();
|
||||
queryClient.invalidateQueries('maps');
|
||||
onClose();
|
||||
};
|
||||
|
||||
export type SimpleDialogProps = {
|
||||
mapId: number;
|
||||
onClose: (success?: boolean) => void;
|
||||
mapId: number;
|
||||
onClose: (success?: boolean) => void;
|
||||
};
|
||||
|
||||
export type MultiDialogProps = {
|
||||
mapsId: number[];
|
||||
onClose: (success?: boolean) => void;
|
||||
mapsId: number[];
|
||||
onClose: (success?: boolean) => void;
|
||||
};
|
||||
|
||||
export default ActionDispatcher;
|
||||
|
@ -14,174 +14,112 @@ import Typography from '@mui/material/Typography';
|
||||
import List from '@mui/material/List';
|
||||
import LocalizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
|
||||
|
||||
// Load fromNow pluggin
|
||||
dayjs.extend(LocalizedFormat)
|
||||
|
||||
dayjs.extend(LocalizedFormat);
|
||||
|
||||
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const { map } = fetchMapById(mapId);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const { map } = fetchMapById(mapId);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setError(undefined);
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'info.title', defaultMessage: 'Info' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'info.description-msg',
|
||||
defaultMessage:
|
||||
'By publishing the map you make it visible to everyone on the Internet.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'info.button', defaultMessage: 'Accept' })}
|
||||
>
|
||||
<Paper style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined" elevation={0}>
|
||||
<Card variant="outlined">
|
||||
<List dense={true}>
|
||||
<ListItem>
|
||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||
<FormattedMessage
|
||||
id="info.basic-info"
|
||||
defaultMessage="Basic Info"
|
||||
/>
|
||||
</Typography>
|
||||
</ListItem>
|
||||
return (
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'info.title', defaultMessage: 'Info' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'info.description-msg',
|
||||
defaultMessage: 'By publishing the map you make it visible to everyone on the Internet.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'info.button', defaultMessage: 'Accept' })}
|
||||
>
|
||||
<Paper style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined" elevation={0}>
|
||||
<Card variant="outlined">
|
||||
<List dense={true}>
|
||||
<ListItem>
|
||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||
<FormattedMessage id="info.basic-info" defaultMessage="Basic Info" />
|
||||
</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage id="info.name" defaultMessage="Name" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.title}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.name" defaultMessage="Name" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.title}</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="info.description"
|
||||
defaultMessage="Description"
|
||||
/>
|
||||
:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.description}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.description" defaultMessage="Description" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.description}</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage id="info.creator" defaultMessage="Creator" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.createdBy}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.creator" defaultMessage="Creator" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.createdBy}</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="info.creation-time"
|
||||
defaultMessage="Creation Date"
|
||||
/>
|
||||
:
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{dayjs(map?.creationTime).format('LLL')}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.creation-time" defaultMessage="Creation Date" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{dayjs(map?.creationTime).format('LLL')}</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="info.modified-tny"
|
||||
defaultMessage="Last Modified By"
|
||||
/>
|
||||
:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.lastModificationBy}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.modified-tny" defaultMessage="Last Modified By" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{map?.lastModificationBy}</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="info.modified-time"
|
||||
defaultMessage="Last Modified Date"
|
||||
/>
|
||||
:
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{dayjs(map?.lastModificationTime).format('LLL')}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.modified-time" defaultMessage="Last Modified Date" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{dayjs(map?.lastModificationTime).format('LLL')}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage id="info.starred" defaultMessage="Starred" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{Boolean(map?.starred).toString()}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Card>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.starred" defaultMessage="Starred" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{Boolean(map?.starred).toString()}</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Card>
|
||||
|
||||
<Card variant="outlined" style={{ marginTop: '10px' }}>
|
||||
<List dense={true}>
|
||||
<ListItem>
|
||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||
<FormattedMessage id="info.sharing" defaultMessage="Sharing" />
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
<ListItem>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textPrimary"
|
||||
className={classes.textDesc}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="info.public-visibility"
|
||||
defaultMessage="Publicly Visible"
|
||||
/>
|
||||
:
|
||||
</Typography>
|
||||
<Typography variant="body2">{Boolean(map?.isPublic).toString()}</Typography>
|
||||
</ListItem>
|
||||
</Card>
|
||||
</Paper>
|
||||
</BaseDialog>
|
||||
);
|
||||
<Card variant="outlined" style={{ marginTop: '10px' }}>
|
||||
<List dense={true}>
|
||||
<ListItem>
|
||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||
<FormattedMessage id="info.sharing" defaultMessage="Sharing" />
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
<ListItem>
|
||||
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||
<FormattedMessage id="info.public-visibility" defaultMessage="Publicly Visible" />:
|
||||
</Typography>
|
||||
<Typography variant="body2">{Boolean(map?.isPublic).toString()}</Typography>
|
||||
</ListItem>
|
||||
</Card>
|
||||
</Paper>
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoDialog;
|
||||
|
@ -12,75 +12,77 @@ import { LabelSelector } from '../../maps-list/label-selector';
|
||||
import { activeInstance } from '../../../../redux/clientSlice';
|
||||
import { ChangeLabelMutationFunctionParam, getChangeLabelMutationFunction } from '../../maps-list';
|
||||
|
||||
|
||||
const LabelDialog = ({ mapsId, onClose }: MultiDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// TODO: pass down map data instead of using query?
|
||||
const { data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||
return client.fetchAllMaps();
|
||||
// TODO: pass down map data instead of using query?
|
||||
const { data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||
return client.fetchAllMaps();
|
||||
});
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const maps = data.filter((m) => mapsId.includes(m.id));
|
||||
|
||||
const changeLabelMutation = useMutation<
|
||||
void,
|
||||
ErrorInfo,
|
||||
ChangeLabelMutationFunctionParam,
|
||||
number
|
||||
>(getChangeLabelMutationFunction(client), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('maps');
|
||||
queryClient.invalidateQueries('labels');
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
});
|
||||
|
||||
const handleChangesInLabels = (label: Label, checked: boolean) => {
|
||||
setError(undefined);
|
||||
changeLabelMutation.mutate({
|
||||
maps,
|
||||
label,
|
||||
checked,
|
||||
});
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
};
|
||||
|
||||
const maps = data.filter(m => mapsId.includes(m.id));
|
||||
|
||||
const changeLabelMutation = useMutation<void, ErrorInfo, ChangeLabelMutationFunctionParam, number>(
|
||||
getChangeLabelMutationFunction(client),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('maps');
|
||||
queryClient.invalidateQueries('labels');
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleChangesInLabels = (label: Label, checked: boolean) => {
|
||||
setError(undefined);
|
||||
changeLabelMutation.mutate({
|
||||
maps,
|
||||
label,
|
||||
checked
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={onClose}
|
||||
title={intl.formatMessage({
|
||||
id: 'label.title',
|
||||
defaultMessage: 'Add a label',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'label.description',
|
||||
defaultMessage:
|
||||
'Use labels to organize your maps.',
|
||||
})}
|
||||
PaperProps={{ classes: { root: classes.paper } }}
|
||||
error={error}
|
||||
>
|
||||
<>
|
||||
<Typography variant="body2" marginTop="10px">
|
||||
<FormattedMessage id="label.add-for" defaultMessage="Editing labels for " />
|
||||
{
|
||||
maps.length > 1 ?
|
||||
<FormattedMessage id="label.maps-count"
|
||||
defaultMessage="{count} maps"
|
||||
values={{ count: maps.length }}
|
||||
/> :
|
||||
maps.map(m => m.title).join(', ')
|
||||
}
|
||||
</Typography>
|
||||
<LabelSelector onChange={handleChangesInLabels} maps={maps} />
|
||||
</>
|
||||
</BaseDialog>
|
||||
</div>);
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={onClose}
|
||||
title={intl.formatMessage({
|
||||
id: 'label.title',
|
||||
defaultMessage: 'Add a label',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'label.description',
|
||||
defaultMessage: 'Use labels to organize your maps.',
|
||||
})}
|
||||
PaperProps={{ classes: { root: classes.paper } }}
|
||||
error={error}
|
||||
>
|
||||
<>
|
||||
<Typography variant="body2" marginTop="10px">
|
||||
<FormattedMessage id="label.add-for" defaultMessage="Editing labels for " />
|
||||
{maps.length > 1 ? (
|
||||
<FormattedMessage
|
||||
id="label.maps-count"
|
||||
defaultMessage="{count} maps"
|
||||
values={{ count: maps.length }}
|
||||
/>
|
||||
) : (
|
||||
maps.map((m) => m.title).join(', ')
|
||||
)}
|
||||
</Typography>
|
||||
<LabelSelector onChange={handleChangesInLabels} maps={maps} />
|
||||
</>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LabelDialog;
|
||||
|
@ -21,139 +21,133 @@ import Box from '@mui/system/Box';
|
||||
import AppConfig from '../../../../classes/app-config';
|
||||
|
||||
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const { map } = fetchMapById(mapId);
|
||||
const { map } = fetchMapById(mapId);
|
||||
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const [activeTab, setActiveTab] = React.useState('1');
|
||||
const queryClient = useQueryClient();
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const mutation = useMutation<void, ErrorInfo, boolean>(
|
||||
(model: boolean) => {
|
||||
return client.updateMapToPublic(mapId, model);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
setModel(model);
|
||||
handleOnMutationSuccess(onClose, queryClient);
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const [activeTab, setActiveTab] = React.useState('1');
|
||||
const queryClient = useQueryClient();
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
const mutation = useMutation<void, ErrorInfo, boolean>(
|
||||
(model: boolean) => {
|
||||
return client.updateMapToPublic(mapId, model);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
setModel(model);
|
||||
handleOnMutationSuccess(onClose, queryClient);
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setError(undefined);
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean): void => {
|
||||
event.preventDefault();
|
||||
setModel(checked);
|
||||
};
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean): void => {
|
||||
event.preventDefault();
|
||||
setModel(checked);
|
||||
};
|
||||
|
||||
const handleTabChange = (event: React.ChangeEvent<HTMLInputElement>, newValue: string) => {
|
||||
setActiveTab(newValue);
|
||||
};
|
||||
const handleTabChange = (event: React.ChangeEvent<HTMLInputElement>, newValue: string) => {
|
||||
setActiveTab(newValue);
|
||||
};
|
||||
|
||||
const baseUrl = AppConfig.getBaseUrl();
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'publish.title', defaultMessage: 'Publish' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'publish.description',
|
||||
defaultMessage:
|
||||
'By publishing the map you make it visible to everyone on the Internet.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'publish.button',
|
||||
defaultMessage: 'Accept',
|
||||
})}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={model}
|
||||
onChange={handleOnChange}
|
||||
name="public"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={intl.formatMessage({
|
||||
id: 'publish.checkbox',
|
||||
defaultMessage: 'Enable public sharing',
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
const baseUrl = AppConfig.getBaseUrl();
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'publish.title', defaultMessage: 'Publish' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'publish.description',
|
||||
defaultMessage: 'By publishing the map you make it visible to everyone on the Internet.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'publish.button',
|
||||
defaultMessage: 'Accept',
|
||||
})}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox checked={model} onChange={handleOnChange} name="public" color="primary" />
|
||||
}
|
||||
label={intl.formatMessage({
|
||||
id: 'publish.checkbox',
|
||||
defaultMessage: 'Enable public sharing',
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<div style={!model ? { visibility: 'hidden' } : {}}>
|
||||
<TabContext value={activeTab}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<TabList onChange={handleTabChange}>
|
||||
<Tab
|
||||
label={intl.formatMessage({
|
||||
id: 'publish.public-url',
|
||||
defaultMessage: 'Public URL',
|
||||
})}
|
||||
value="1"
|
||||
/>
|
||||
<Tab
|
||||
label={intl.formatMessage({
|
||||
id: 'publish.embedded',
|
||||
defaultMessage: 'Embedded',
|
||||
})}
|
||||
value="2"
|
||||
/>
|
||||
</TabList>
|
||||
</Box>
|
||||
<TabPanel value="2">
|
||||
<Typography variant="subtitle2">
|
||||
<FormattedMessage
|
||||
id="publish.embedded-msg"
|
||||
defaultMessage="Copy this snippet of code to embed in your blog or page:"
|
||||
/>
|
||||
</Typography>
|
||||
<TextareaAutosize
|
||||
className={classes.textarea}
|
||||
readOnly={true}
|
||||
spellCheck={false}
|
||||
maxRows={6}
|
||||
defaultValue={`<iframe style="width:600px;height:400px;border:1px solid black" src="${baseUrl}/c/maps/${mapId}/embed?zoom=1.0"></iframe>`}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
<Typography variant="subtitle2">
|
||||
<FormattedMessage
|
||||
id="publish.public-url-msg"
|
||||
defaultMessage="Copy and paste the link below to share your map with colleagues:"
|
||||
/>
|
||||
</Typography>
|
||||
<TextareaAutosize
|
||||
className={classes.textarea}
|
||||
readOnly={true}
|
||||
spellCheck={false}
|
||||
maxRows={1}
|
||||
defaultValue={`${baseUrl}/c/maps/${mapId}/public`}
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
<div style={!model ? { visibility: 'hidden' } : {}}>
|
||||
<TabContext value={activeTab}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<TabList onChange={handleTabChange}>
|
||||
<Tab
|
||||
label={intl.formatMessage({
|
||||
id: 'publish.public-url',
|
||||
defaultMessage: 'Public URL',
|
||||
})}
|
||||
value="1"
|
||||
/>
|
||||
<Tab
|
||||
label={intl.formatMessage({
|
||||
id: 'publish.embedded',
|
||||
defaultMessage: 'Embedded',
|
||||
})}
|
||||
value="2"
|
||||
/>
|
||||
</TabList>
|
||||
</Box>
|
||||
<TabPanel value="2">
|
||||
<Typography variant="subtitle2">
|
||||
<FormattedMessage
|
||||
id="publish.embedded-msg"
|
||||
defaultMessage="Copy this snippet of code to embed in your blog or page:"
|
||||
/>
|
||||
</Typography>
|
||||
<TextareaAutosize
|
||||
className={classes.textarea}
|
||||
readOnly={true}
|
||||
spellCheck={false}
|
||||
maxRows={6}
|
||||
defaultValue={`<iframe style="width:600px;height:400px;border:1px solid black" src="${baseUrl}/c/maps/${mapId}/embed?zoom=1.0"></iframe>`}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
<Typography variant="subtitle2">
|
||||
<FormattedMessage
|
||||
id="publish.public-url-msg"
|
||||
defaultMessage="Copy and paste the link below to share your map with colleagues:"
|
||||
/>
|
||||
</Typography>
|
||||
<TextareaAutosize
|
||||
className={classes.textarea}
|
||||
readOnly={true}
|
||||
spellCheck={false}
|
||||
maxRows={1}
|
||||
defaultValue={`${baseUrl}/c/maps/${mapId}/public`}
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</div>
|
||||
);
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublishDialog;
|
||||
|
@ -10,104 +10,104 @@ import BaseDialog from '../base-dialog';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
|
||||
export type RenameModel = {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
const defaultModel: RenameModel = { title: '', description: '', id: -1 };
|
||||
const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const service: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<RenameModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const service: Client = useSelector(activeInstance);
|
||||
const [model, setModel] = React.useState<RenameModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const intl = useIntl();
|
||||
const queryClient = useQueryClient();
|
||||
const intl = useIntl();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>(
|
||||
(model: RenameModel) => {
|
||||
const { id, ...rest } = model;
|
||||
return service.renameMap(id, rest).then(() => model);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleOnMutationSuccess(onClose, queryClient);
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>(
|
||||
(model: RenameModel) => {
|
||||
const { id, ...rest } = model;
|
||||
return service.renameMap(id, rest).then(() => model);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleOnMutationSuccess(onClose, queryClient);
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||
};
|
||||
|
||||
const { map } = fetchMapById(mapId);
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
setModel(map);
|
||||
}
|
||||
}, [mapId]);
|
||||
const { map } = fetchMapById(mapId);
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
setModel(map);
|
||||
}
|
||||
}, [mapId]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'rename.description',
|
||||
defaultMessage: 'Please, fill the new map name and description.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
onSubmit={handleOnSubmit}
|
||||
error={error}
|
||||
title={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'rename.description',
|
||||
defaultMessage: 'Please, fill the new map name and description.',
|
||||
})}
|
||||
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||
>
|
||||
<FormControl fullWidth={true}>
|
||||
<Input
|
||||
name="title"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-name-placeholder',
|
||||
defaultMessage: 'Name',
|
||||
})}
|
||||
value={model.title}
|
||||
onChange={handleOnChange}
|
||||
error={error}
|
||||
fullWidth={true}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label={intl.formatMessage({
|
||||
id: 'action.rename-description-placeholder',
|
||||
defaultMessage: 'Description',
|
||||
})}
|
||||
value={model.description}
|
||||
onChange={handleOnChange}
|
||||
required={false}
|
||||
fullWidth={true}
|
||||
/>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RenameDialog;
|
||||
|
@ -26,247 +26,233 @@ import RoleIcon from '../../role-icon';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
|
||||
type ShareModel = {
|
||||
emails: string;
|
||||
role: 'editor' | 'viewer';
|
||||
message: string;
|
||||
emails: string;
|
||||
role: 'editor' | 'viewer';
|
||||
message: string;
|
||||
};
|
||||
|
||||
const defaultModel: ShareModel = { emails: '', role: 'editor', message: '' };
|
||||
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const classes = useStyles();
|
||||
const [showMessage, setShowMessage] = React.useState<boolean>(false);
|
||||
const [model, setModel] = React.useState<ShareModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
const intl = useIntl();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const queryClient = useQueryClient();
|
||||
const classes = useStyles();
|
||||
const [showMessage, setShowMessage] = React.useState<boolean>(false);
|
||||
const [model, setModel] = React.useState<ShareModel>(defaultModel);
|
||||
const [error, setError] = React.useState<ErrorInfo>();
|
||||
|
||||
const deleteMutation = useMutation(
|
||||
(email: string) => {
|
||||
return client.deleteMapPermission(mapId, email);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||
setModel(defaultModel);
|
||||
},
|
||||
onError: (error: ErrorInfo) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const splitEmail = (emails: string): string[] => {
|
||||
return emails.split(/,|;/)
|
||||
.map(e => e.trim().replace(/\s/g, ''))
|
||||
.filter(e => e.trim().length > 0);
|
||||
}
|
||||
|
||||
|
||||
const addMutation = useMutation(
|
||||
(model: ShareModel) => {
|
||||
const emails = splitEmail(model.emails);
|
||||
const permissions = emails.map((email: string) => {
|
||||
return { email: email, role: model.role };
|
||||
});
|
||||
return client.addMapPermissions(mapId, model.message, permissions);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||
setModel(defaultModel);
|
||||
},
|
||||
onError: (error: ErrorInfo) => {
|
||||
setError(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const handleOnClose = (): void => {
|
||||
// Invalidate cache ...
|
||||
const deleteMutation = useMutation(
|
||||
(email: string) => {
|
||||
return client.deleteMapPermission(mapId, email);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||
onClose();
|
||||
setModel(defaultModel);
|
||||
},
|
||||
onError: (error: ErrorInfo) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
};
|
||||
const splitEmail = (emails: string): string[] => {
|
||||
return emails
|
||||
.split(/,|;/)
|
||||
.map((e) => e.trim().replace(/\s/g, ''))
|
||||
.filter((e) => e.trim().length > 0);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
const addMutation = useMutation(
|
||||
(model: ShareModel) => {
|
||||
const emails = splitEmail(model.emails);
|
||||
const permissions = emails.map((email: string) => {
|
||||
return { email: email, role: model.role };
|
||||
});
|
||||
return client.addMapPermissions(mapId, model.message, permissions);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||
setModel(defaultModel);
|
||||
},
|
||||
onError: (error: ErrorInfo) => {
|
||||
setError(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof ShareModel]: value });
|
||||
event.stopPropagation();
|
||||
};
|
||||
const handleOnClose = (): void => {
|
||||
// Invalidate cache ...
|
||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleOnAddClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||
event.stopPropagation();
|
||||
addMutation.mutate(model);
|
||||
event.stopPropagation();
|
||||
};
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const handleOnDeleteClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||
email: string
|
||||
): void => {
|
||||
event.stopPropagation();
|
||||
deleteMutation.mutate(email);
|
||||
};
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof ShareModel]: value });
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
const { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(`perm-${mapId}`,
|
||||
() => {
|
||||
return client.fetchMapPermissions(mapId);
|
||||
}
|
||||
);
|
||||
const handleOnAddClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||
event.stopPropagation();
|
||||
addMutation.mutate(model);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
const formatName = (perm: Permission): string => {
|
||||
return perm.name ? `${perm.name}<${perm.email}>` : perm.email;
|
||||
};
|
||||
const handleOnDeleteClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||
email: string,
|
||||
): void => {
|
||||
event.stopPropagation();
|
||||
deleteMutation.mutate(email);
|
||||
};
|
||||
|
||||
// very basic email validation, just make sure the basic syntax is fine
|
||||
const isValid = splitEmail(model.emails)
|
||||
.every(str => /\S+@\S+\.\S+/.test((str || '')
|
||||
.trim()));
|
||||
const { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(
|
||||
`perm-${mapId}`,
|
||||
() => {
|
||||
return client.fetchMapPermissions(mapId);
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
title={intl.formatMessage({
|
||||
id: 'share.delete-title',
|
||||
defaultMessage: 'Share with people',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'share.delete-description',
|
||||
defaultMessage:
|
||||
'Invite people to collaborate with you in the creation of your mindmap. They will be notified by email. ',
|
||||
})}
|
||||
PaperProps={{ classes: { root: classes.paper } }}
|
||||
error={error}
|
||||
>
|
||||
<div className={classes.actionContainer}>
|
||||
<TextField
|
||||
id="emails"
|
||||
name="emails"
|
||||
required={true}
|
||||
style={{ width: '300px' }}
|
||||
size="small"
|
||||
type="email"
|
||||
variant="outlined"
|
||||
placeholder="Add collaborator email"
|
||||
label="Emails"
|
||||
onChange={handleOnChange}
|
||||
value={model.emails}
|
||||
/>
|
||||
const formatName = (perm: Permission): string => {
|
||||
return perm.name ? `${perm.name}<${perm.email}>` : perm.email;
|
||||
};
|
||||
|
||||
<Select
|
||||
variant="outlined"
|
||||
onChange={handleOnChange}
|
||||
value={model.role}
|
||||
name="role"
|
||||
style={{ margin: '0px 10px' }}
|
||||
>
|
||||
<MenuItem value="editor">
|
||||
<FormattedMessage id="share.can-edit" defaultMessage="Can edit" />
|
||||
</MenuItem>
|
||||
<MenuItem value="viewer">
|
||||
<FormattedMessage id="share.can-view" defaultMessage="Can view" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
// very basic email validation, just make sure the basic syntax is fine
|
||||
const isValid = splitEmail(model.emails).every((str) => /\S+@\S+\.\S+/.test((str || '').trim()));
|
||||
|
||||
<FormControlLabel
|
||||
value="start"
|
||||
onChange={(event, value) => {
|
||||
setShowMessage(value);
|
||||
}}
|
||||
style={{ fontSize: '5px' }}
|
||||
control={<Checkbox color="primary" />}
|
||||
label={
|
||||
<Typography variant="subtitle2">
|
||||
<FormattedMessage
|
||||
id="share.add-message"
|
||||
defaultMessage="Add message"
|
||||
/>
|
||||
</Typography>
|
||||
}
|
||||
labelPlacement="end"
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={handleOnClose}
|
||||
title={intl.formatMessage({
|
||||
id: 'share.delete-title',
|
||||
defaultMessage: 'Share with people',
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: 'share.delete-description',
|
||||
defaultMessage:
|
||||
'Invite people to collaborate with you in the creation of your mindmap. They will be notified by email. ',
|
||||
})}
|
||||
PaperProps={{ classes: { root: classes.paper } }}
|
||||
error={error}
|
||||
>
|
||||
<div className={classes.actionContainer}>
|
||||
<TextField
|
||||
id="emails"
|
||||
name="emails"
|
||||
required={true}
|
||||
style={{ width: '300px' }}
|
||||
size="small"
|
||||
type="email"
|
||||
variant="outlined"
|
||||
placeholder="Add collaborator email"
|
||||
label="Emails"
|
||||
onChange={handleOnChange}
|
||||
value={model.emails}
|
||||
/>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
type="button"
|
||||
variant="contained"
|
||||
disableElevation={true}
|
||||
onClick={handleOnAddClick}
|
||||
disabled={!isValid}
|
||||
>
|
||||
<FormattedMessage id="share.add-button" defaultMessage="Add" />
|
||||
</Button>
|
||||
<Select
|
||||
variant="outlined"
|
||||
onChange={handleOnChange}
|
||||
value={model.role}
|
||||
name="role"
|
||||
style={{ margin: '0px 10px' }}
|
||||
>
|
||||
<MenuItem value="editor">
|
||||
<FormattedMessage id="share.can-edit" defaultMessage="Can edit" />
|
||||
</MenuItem>
|
||||
<MenuItem value="viewer">
|
||||
<FormattedMessage id="share.can-view" defaultMessage="Can view" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
|
||||
{showMessage && (
|
||||
<TextField
|
||||
multiline
|
||||
rows={3}
|
||||
maxRows={3}
|
||||
className={classes.textArea}
|
||||
variant="filled"
|
||||
name="message"
|
||||
onChange={handleOnChange}
|
||||
value={model.message}
|
||||
label={intl.formatMessage({
|
||||
id: 'share.message',
|
||||
defaultMessage: 'Message',
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<FormControlLabel
|
||||
value="start"
|
||||
onChange={(event, value) => {
|
||||
setShowMessage(value);
|
||||
}}
|
||||
style={{ fontSize: '5px' }}
|
||||
control={<Checkbox color="primary" />}
|
||||
label={
|
||||
<Typography variant="subtitle2">
|
||||
<FormattedMessage id="share.add-message" defaultMessage="Add message" />
|
||||
</Typography>
|
||||
}
|
||||
labelPlacement="end"
|
||||
/>
|
||||
|
||||
{!isLoading && (
|
||||
<Paper elevation={1} className={classes.listPaper} variant="outlined">
|
||||
<List>
|
||||
{permissions &&
|
||||
permissions.map((permission) => {
|
||||
return (
|
||||
<ListItem
|
||||
key={permission.email}
|
||||
role={undefined}
|
||||
dense
|
||||
button
|
||||
>
|
||||
<ListItemText
|
||||
id={permission.email}
|
||||
primary={formatName(permission)}
|
||||
/>
|
||||
<Button
|
||||
color="primary"
|
||||
type="button"
|
||||
variant="contained"
|
||||
disableElevation={true}
|
||||
onClick={handleOnAddClick}
|
||||
disabled={!isValid}
|
||||
>
|
||||
<FormattedMessage id="share.add-button" defaultMessage="Add" />
|
||||
</Button>
|
||||
|
||||
<RoleIcon role={permission.role} />
|
||||
<ListItemSecondaryAction>
|
||||
<Tooltip
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="share.delete"
|
||||
defaultMessage="Delete collaborator"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
edge="end"
|
||||
disabled={permission.role == 'owner'}
|
||||
onClick={(e) =>
|
||||
handleOnDeleteClick(e, permission.email)
|
||||
}
|
||||
size="large">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Paper>
|
||||
)}
|
||||
</BaseDialog>
|
||||
{showMessage && (
|
||||
<TextField
|
||||
multiline
|
||||
rows={3}
|
||||
maxRows={3}
|
||||
className={classes.textArea}
|
||||
variant="filled"
|
||||
name="message"
|
||||
onChange={handleOnChange}
|
||||
value={model.message}
|
||||
label={intl.formatMessage({
|
||||
id: 'share.message',
|
||||
defaultMessage: 'Message',
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
{!isLoading && (
|
||||
<Paper elevation={1} className={classes.listPaper} variant="outlined">
|
||||
<List>
|
||||
{permissions &&
|
||||
permissions.map((permission) => {
|
||||
return (
|
||||
<ListItem key={permission.email} role={undefined} dense button>
|
||||
<ListItemText id={permission.email} primary={formatName(permission)} />
|
||||
|
||||
<RoleIcon role={permission.role} />
|
||||
<ListItemSecondaryAction>
|
||||
<Tooltip
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="share.delete"
|
||||
defaultMessage="Delete collaborator"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
edge="end"
|
||||
disabled={permission.role == 'owner'}
|
||||
onClick={(e) => handleOnDeleteClick(e, permission.email)}
|
||||
size="large"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Paper>
|
||||
)}
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShareDialog;
|
||||
|
@ -14,92 +14,85 @@ import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
|
||||
const HelpMenu = (): React.ReactElement => {
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const intl = useIntl();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const intl = useIntl();
|
||||
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}
|
||||
>
|
||||
<IconButton aria-haspopup="true" onClick={handleMenu} size="large">
|
||||
<Help />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="appbar-profile"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link
|
||||
color="textSecondary"
|
||||
href="https://www.wisemapping.com/termsofuse.html"
|
||||
target="help"
|
||||
>
|
||||
<ListItemIcon>
|
||||
<PolicyOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage
|
||||
id="footer.termsandconditions"
|
||||
defaultMessage="Term And Conditions"
|
||||
/>
|
||||
</Link>
|
||||
</MenuItem>
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}
|
||||
>
|
||||
<IconButton aria-haspopup="true" onClick={handleMenu} size="large">
|
||||
<Help />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="appbar-profile"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link
|
||||
color="textSecondary"
|
||||
href="https://www.wisemapping.com/termsofuse.html"
|
||||
target="help"
|
||||
>
|
||||
<ListItemIcon>
|
||||
<PolicyOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.termsandconditions" defaultMessage="Term And Conditions" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link color="textSecondary" href="mailto:team@wisemapping.com">
|
||||
<ListItemIcon>
|
||||
<EmailOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link color="textSecondary" href="mailto:team@wisemapping.com">
|
||||
<ListItemIcon>
|
||||
<EmailOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link color="textSecondary" href="mailto:feedback@wisemapping.com">
|
||||
<ListItemIcon>
|
||||
<FeedbackOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link color="textSecondary" href="mailto:feedback@wisemapping.com">
|
||||
<ListItemIcon>
|
||||
<FeedbackOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link
|
||||
color="textSecondary"
|
||||
href="https://www.wisemapping.com/aboutus.html"
|
||||
target="help"
|
||||
>
|
||||
<ListItemIcon>
|
||||
<EmojiPeopleOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</span>
|
||||
);
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Link color="textSecondary" href="https://www.wisemapping.com/aboutus.html" target="help">
|
||||
<ListItemIcon>
|
||||
<EmojiPeopleOutlined fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default HelpMenu;
|
||||
|
@ -18,134 +18,133 @@ import DialogActions from '@mui/material/DialogActions';
|
||||
import Divider from '@mui/material/Divider';
|
||||
|
||||
const LanguageMenu = (): React.ReactElement => {
|
||||
const queryClient = useQueryClient();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [openHelpDialog, setHelpDialogOpen] = React.useState<boolean>(false);
|
||||
const queryClient = useQueryClient();
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [openHelpDialog, setHelpDialogOpen] = React.useState<boolean>(false);
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const intl = useIntl();
|
||||
const open = Boolean(anchorEl);
|
||||
const intl = useIntl();
|
||||
|
||||
const mutation = useMutation((locale: LocaleCode) => client.updateAccountLanguage(locale), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('account');
|
||||
const mutation = useMutation((locale: LocaleCode) => client.updateAccountLanguage(locale), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('account');
|
||||
handleClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(`Unexpected error ${error}`);
|
||||
},
|
||||
});
|
||||
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleOnClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
const localeCode = event.target['id'];
|
||||
mutation.mutate(localeCode);
|
||||
};
|
||||
|
||||
const userLocale = AppI18n.getUserLocale();
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={intl.formatMessage({
|
||||
id: 'language.change',
|
||||
defaultMessage: 'Change Language',
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
disableElevation={true}
|
||||
color="primary"
|
||||
style={{ borderColor: 'gray', color: 'gray' }}
|
||||
onClick={handleMenu}
|
||||
startIcon={<TranslateTwoTone style={{ color: 'inherit' }} />}
|
||||
>
|
||||
{userLocale.label}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="appbar-language"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleOnClick} id={Locales.EN.code}>
|
||||
{Locales.EN.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.ES.code}>
|
||||
{Locales.ES.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.DE.code}>
|
||||
{Locales.DE.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.FR.code}>
|
||||
{Locales.FR.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.RU.code}>
|
||||
{Locales.RU.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.ZH.code}>
|
||||
{Locales.ZH.label}
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(`Unexpected error ${error}`);
|
||||
},
|
||||
});
|
||||
|
||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleOnClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
const localeCode = event.target['id'];
|
||||
mutation.mutate(localeCode);
|
||||
};
|
||||
|
||||
const userLocale = AppI18n.getUserLocale();
|
||||
return (
|
||||
<span>
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
title={intl.formatMessage({
|
||||
id: 'language.change',
|
||||
defaultMessage: 'Change Language',
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
disableElevation={true}
|
||||
color="primary"
|
||||
style={{ borderColor: 'gray', color: 'gray' }}
|
||||
onClick={handleMenu}
|
||||
startIcon={<TranslateTwoTone style={{ color: 'inherit' }} />}
|
||||
>
|
||||
{userLocale.label}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="appbar-language"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleOnClick} id={Locales.EN.code}>
|
||||
{Locales.EN.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.ES.code}>
|
||||
{Locales.ES.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.DE.code}>
|
||||
{Locales.DE.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.FR.code}>
|
||||
{Locales.FR.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.RU.code}>
|
||||
{Locales.RU.label}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleOnClick} id={Locales.ZH.code}>
|
||||
{Locales.ZH.label}
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setHelpDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="language.help" defaultMessage="Help to Translate" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
{openHelpDialog && <HelpUsToTranslateDialog onClose={() => setHelpDialogOpen(false)} />}
|
||||
</span>
|
||||
);
|
||||
setHelpDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="language.help" defaultMessage="Help to Translate" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
{openHelpDialog && <HelpUsToTranslateDialog onClose={() => setHelpDialogOpen(false)} />}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
type HelpUsToTranslateDialogProp = {
|
||||
onClose: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
const HelpUsToTranslateDialog = ({ onClose }: HelpUsToTranslateDialogProp) => {
|
||||
return (
|
||||
<Dialog open={true} onClose={onClose}>
|
||||
<DialogTitle>Help us to support more languages !</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
We need your help !. If you are interested, send us an email to
|
||||
team@wisemapping.com.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
return (
|
||||
<Dialog open={true} onClose={onClose}>
|
||||
<DialogTitle>Help us to support more languages !</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
We need your help !. If you are interested, send us an email to team@wisemapping.com.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageMenu;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,38 +8,52 @@ import BaseDialog from '../../action-dispatcher/base-dialog';
|
||||
import { Label } from '../../../../classes/client';
|
||||
|
||||
export type LabelDeleteConfirmType = {
|
||||
label: Label;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
label: Label;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
};
|
||||
|
||||
const LabelDeleteConfirm = ({ label, onClose, onConfirm }: LabelDeleteConfirmType): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const LabelDeleteConfirm = ({
|
||||
label,
|
||||
onClose,
|
||||
onConfirm,
|
||||
}: LabelDeleteConfirmType): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={onClose}
|
||||
onSubmit={onConfirm}
|
||||
title={intl.formatMessage({ id: 'label.delete-title', defaultMessage: 'Confirm label deletion' })}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>{intl.formatMessage({ id: 'label.delete-title', defaultMessage: 'Confirm label deletion' })}</AlertTitle>
|
||||
<span>
|
||||
<Typography fontWeight="bold" component="span">{label.title} </Typography>
|
||||
<FormattedMessage
|
||||
id="label.delete-description"
|
||||
defaultMessage="will be deleted, including its associations to all existing maps. Do you want to continue?"
|
||||
/>
|
||||
</span>
|
||||
</Alert>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
onClose={onClose}
|
||||
onSubmit={onConfirm}
|
||||
title={intl.formatMessage({
|
||||
id: 'label.delete-title',
|
||||
defaultMessage: 'Confirm label deletion',
|
||||
})}
|
||||
submitButton={intl.formatMessage({
|
||||
id: 'action.delete-title',
|
||||
defaultMessage: 'Delete',
|
||||
})}
|
||||
>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>
|
||||
{intl.formatMessage({
|
||||
id: 'label.delete-title',
|
||||
defaultMessage: 'Confirm label deletion',
|
||||
})}
|
||||
</AlertTitle>
|
||||
<span>
|
||||
<Typography fontWeight="bold" component="span">
|
||||
{label.title}{' '}
|
||||
</Typography>
|
||||
<FormattedMessage
|
||||
id="label.delete-description"
|
||||
defaultMessage="will be deleted, including its associations to all existing maps. Do you want to continue?"
|
||||
/>
|
||||
</span>
|
||||
</Alert>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LabelDeleteConfirm;
|
||||
|
@ -12,44 +12,44 @@ import AddLabelDialog from '../../action-dispatcher/add-label-dialog';
|
||||
import { LabelListContainer } from './styled';
|
||||
|
||||
export type LabelSelectorProps = {
|
||||
maps: MapInfo[];
|
||||
onChange: (label: Label, checked: boolean) => void;
|
||||
maps: MapInfo[];
|
||||
onChange: (label: Label, checked: boolean) => void;
|
||||
};
|
||||
|
||||
export function LabelSelector({ onChange, maps }: LabelSelectorProps): React.ReactElement {
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const { data: labels = [] } = useQuery<unknown, ErrorInfo, Label[]>('labels', async () =>
|
||||
client.fetchLabels()
|
||||
);
|
||||
const client: Client = useSelector(activeInstance);
|
||||
const { data: labels = [] } = useQuery<unknown, ErrorInfo, Label[]>('labels', async () =>
|
||||
client.fetchLabels(),
|
||||
);
|
||||
|
||||
const checkedLabelIds = labels
|
||||
.map((l) => l.id)
|
||||
.filter((labelId) => maps.every((m) => m.labels.find((l) => l.id === labelId)));
|
||||
const checkedLabelIds = labels
|
||||
.map((l) => l.id)
|
||||
.filter((labelId) => maps.every((m) => m.labels.find((l) => l.id === labelId)));
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<FormGroup>
|
||||
<AddLabelDialog onAdd={(label) => onChange(label, true)} />
|
||||
</FormGroup>
|
||||
<LabelListContainer>
|
||||
{labels.map(({ id, title, color }) => (
|
||||
<FormControlLabel
|
||||
key={id}
|
||||
control={
|
||||
<Checkbox
|
||||
id={`${id}`}
|
||||
checked={checkedLabelIds.includes(id)}
|
||||
onChange={(e) => {
|
||||
onChange({ id, title, color }, e.target.checked);
|
||||
}}
|
||||
name={title}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={<LabelComponent label={{ id, title, color }} size="big" />}
|
||||
/>
|
||||
))}
|
||||
</LabelListContainer>
|
||||
</Container>
|
||||
);
|
||||
return (
|
||||
<Container>
|
||||
<FormGroup>
|
||||
<AddLabelDialog onAdd={(label) => onChange(label, true)} />
|
||||
</FormGroup>
|
||||
<LabelListContainer>
|
||||
{labels.map(({ id, title, color }) => (
|
||||
<FormControlLabel
|
||||
key={id}
|
||||
control={
|
||||
<Checkbox
|
||||
id={`${id}`}
|
||||
checked={checkedLabelIds.includes(id)}
|
||||
onChange={(e) => {
|
||||
onChange({ id, title, color }, e.target.checked);
|
||||
}}
|
||||
name={title}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label={<LabelComponent label={{ id, title, color }} size="big" />}
|
||||
/>
|
||||
))}
|
||||
</LabelListContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -6,33 +6,40 @@ import LabelTwoTone from '@mui/icons-material/LabelTwoTone';
|
||||
import DeleteIcon from '@mui/icons-material/Clear';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
||||
|
||||
type LabelSize = 'small' | 'big';
|
||||
type LabelComponentProps = { label: Label; onDelete?: (label: Label) => void; size?: LabelSize };
|
||||
|
||||
export default function LabelComponent({ label, onDelete, size = 'small' }: LabelComponentProps): React.ReactElement<LabelComponentProps> {
|
||||
const iconSize = size === 'small' ? {
|
||||
height: '0.6em', width: '0.6em'
|
||||
} : { height: '0.9em', width: '0.9em' };
|
||||
export default function LabelComponent({
|
||||
label,
|
||||
onDelete,
|
||||
size = 'small',
|
||||
}: LabelComponentProps): React.ReactElement<LabelComponentProps> {
|
||||
const iconSize =
|
||||
size === 'small'
|
||||
? {
|
||||
height: '0.6em',
|
||||
width: '0.6em',
|
||||
}
|
||||
: { height: '0.9em', width: '0.9em' };
|
||||
|
||||
return (
|
||||
<LabelContainer color={label.color}>
|
||||
<LabelTwoTone htmlColor={label.color} style={iconSize} />
|
||||
<LabelText>{label.title}</LabelText>
|
||||
{onDelete && (
|
||||
<IconButton
|
||||
color="default"
|
||||
size="small"
|
||||
aria-label="delete tag"
|
||||
component="span"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(label);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon style={iconSize} />
|
||||
</IconButton>
|
||||
)}
|
||||
</LabelContainer>
|
||||
);
|
||||
return (
|
||||
<LabelContainer color={label.color}>
|
||||
<LabelTwoTone htmlColor={label.color} style={iconSize} />
|
||||
<LabelText>{label.title}</LabelText>
|
||||
{onDelete && (
|
||||
<IconButton
|
||||
color="default"
|
||||
size="small"
|
||||
aria-label="delete tag"
|
||||
component="span"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(label);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon style={iconSize} />
|
||||
</IconButton>
|
||||
)}
|
||||
</LabelContainer>
|
||||
);
|
||||
}
|
||||
|
@ -7,21 +7,22 @@ import DeleteIcon from '@mui/icons-material/Clear';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
||||
type Props = {
|
||||
labels: Label[],
|
||||
onDelete: (label: Label) => void,
|
||||
labels: Label[];
|
||||
onDelete: (label: Label) => void;
|
||||
};
|
||||
|
||||
export function LabelsCell({ labels, onDelete }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<>
|
||||
{labels.map(label => (
|
||||
<LabelContainer
|
||||
key={label.id}
|
||||
color={label.color}
|
||||
>
|
||||
{labels.map((label) => (
|
||||
<LabelContainer key={label.id} color={label.color}>
|
||||
<LabelTwoTone htmlColor={label.color} style={{ height: '0.6em', width: '0.6em' }} />
|
||||
<LabelText>{ label.title }</LabelText>
|
||||
<IconButton color="default" size='small' aria-label="delete tag" component="span"
|
||||
<LabelText>{label.title}</LabelText>
|
||||
<IconButton
|
||||
color="default"
|
||||
size="small"
|
||||
aria-label="delete tag"
|
||||
component="span"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(label);
|
||||
@ -29,7 +30,7 @@ export function LabelsCell({ labels, onDelete }: Props): React.ReactElement<Prop
|
||||
>
|
||||
<DeleteIcon style={{ height: '0.6em', width: '0.6em' }} />
|
||||
</IconButton>
|
||||
</LabelContainer>
|
||||
</LabelContainer>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -9,40 +9,31 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { Role } from '../../../classes/client';
|
||||
|
||||
type RoleIconProps = {
|
||||
role: Role;
|
||||
role: Role;
|
||||
};
|
||||
|
||||
const RoleIcon = ({ role }: RoleIconProps): React.ReactElement => {
|
||||
return (
|
||||
<span>
|
||||
{role == 'owner' && (
|
||||
<Tooltip
|
||||
title={<FormattedMessage id="role.owner" defaultMessage="Owner" />}
|
||||
arrow={true}
|
||||
>
|
||||
<PersonSharpIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
return (
|
||||
<span>
|
||||
{role == 'owner' && (
|
||||
<Tooltip title={<FormattedMessage id="role.owner" defaultMessage="Owner" />} arrow={true}>
|
||||
<PersonSharpIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{role == 'editor' && (
|
||||
<Tooltip
|
||||
title={<FormattedMessage id="role.editor" defaultMessage="Editor" />}
|
||||
arrow={true}
|
||||
>
|
||||
<EditSharpIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
{role == 'editor' && (
|
||||
<Tooltip title={<FormattedMessage id="role.editor" defaultMessage="Editor" />} arrow={true}>
|
||||
<EditSharpIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{role == 'viewer' && (
|
||||
<Tooltip
|
||||
title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />}
|
||||
arrow={true}
|
||||
>
|
||||
<VisibilitySharpIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
{role == 'viewer' && (
|
||||
<Tooltip title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />} arrow={true}>
|
||||
<VisibilitySharpIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleIcon;
|
||||
|
@ -20,161 +20,165 @@ import AppConfig from '../../classes/app-config';
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
export type Model = {
|
||||
email: string;
|
||||
lastname: string;
|
||||
firstname: string;
|
||||
password: string;
|
||||
recaptcha: string;
|
||||
email: string;
|
||||
lastname: string;
|
||||
firstname: string;
|
||||
password: string;
|
||||
recaptcha: string;
|
||||
};
|
||||
|
||||
const defaultModel: Model = { email: '', lastname: '', firstname: '', password: '', recaptcha: '' };
|
||||
const RegistrationForm = () => {
|
||||
const [model, setModel] = useState<Model>(defaultModel);
|
||||
const [error, setError] = useState<ErrorInfo>();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const [captcha, setCaptcha] = useState<any>();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
const [model, setModel] = useState<Model>(defaultModel);
|
||||
const [error, setError] = useState<ErrorInfo>();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const [captcha, setCaptcha] = useState<any>();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
|
||||
const Client: Client = useSelector(activeInstance);
|
||||
const mutation = useMutation<void, ErrorInfo, Model>(
|
||||
(model: Model) => Client.registerNewUser({ ...model }),
|
||||
{
|
||||
onSuccess: () => history.push('/c/registration-success'),
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
captcha.reset();
|
||||
},
|
||||
}
|
||||
);
|
||||
const Client: Client = useSelector(activeInstance);
|
||||
const mutation = useMutation<void, ErrorInfo, Model>(
|
||||
(model: Model) => Client.registerNewUser({ ...model }),
|
||||
{
|
||||
onSuccess: () => history.push('/c/registration-success'),
|
||||
onError: (error) => {
|
||||
setError(error);
|
||||
captcha.reset();
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||
event.preventDefault();
|
||||
mutation.mutate(model);
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
event.preventDefault();
|
||||
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof Model]: value });
|
||||
};
|
||||
const name = event.target.name;
|
||||
const value = event.target.value;
|
||||
setModel({ ...model, [name as keyof Model]: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage id="registration.title" defaultMessage="Become a member" />
|
||||
</Typography>
|
||||
return (
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage id="registration.title" defaultMessage="Become a member" />
|
||||
</Typography>
|
||||
|
||||
<Typography paragraph>
|
||||
<FormattedMessage
|
||||
id="registration.desc"
|
||||
defaultMessage="Signing up is free and just take a moment "
|
||||
/>
|
||||
</Typography>
|
||||
<Typography paragraph>
|
||||
<FormattedMessage
|
||||
id="registration.desc"
|
||||
defaultMessage="Signing up is free and just take a moment "
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<FormControl>
|
||||
<form onSubmit={handleOnSubmit}>
|
||||
<GlobalError error={error} />
|
||||
<FormControl>
|
||||
<form onSubmit={handleOnSubmit}>
|
||||
<GlobalError error={error} />
|
||||
|
||||
<Input
|
||||
name="email"
|
||||
type="email"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.email',
|
||||
defaultMessage: 'Email',
|
||||
})}
|
||||
autoComplete="email"
|
||||
error={error}
|
||||
/>
|
||||
<Input
|
||||
name="email"
|
||||
type="email"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.email',
|
||||
defaultMessage: 'Email',
|
||||
})}
|
||||
autoComplete="email"
|
||||
error={error}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="firstname"
|
||||
type="text"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.firstname',
|
||||
defaultMessage: 'First Name',
|
||||
})}
|
||||
autoComplete="given-name"
|
||||
error={error}
|
||||
/>
|
||||
<Input
|
||||
name="firstname"
|
||||
type="text"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.firstname',
|
||||
defaultMessage: 'First Name',
|
||||
})}
|
||||
autoComplete="given-name"
|
||||
error={error}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="lastname"
|
||||
type="text"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.lastname',
|
||||
defaultMessage: 'Last Name',
|
||||
})}
|
||||
autoComplete="family-name"
|
||||
error={error}
|
||||
/>
|
||||
<Input
|
||||
name="lastname"
|
||||
type="text"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.lastname',
|
||||
defaultMessage: 'Last Name',
|
||||
})}
|
||||
autoComplete="family-name"
|
||||
error={error}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.password',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
autoComplete="new-password"
|
||||
error={error}
|
||||
/>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
onChange={handleOnChange}
|
||||
label={intl.formatMessage({
|
||||
id: 'registration.password',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
autoComplete="new-password"
|
||||
error={error}
|
||||
/>
|
||||
|
||||
{AppConfig.isRecaptcha2Enabled() &&
|
||||
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>
|
||||
<ReCAPTCHA
|
||||
ref={el => setCaptcha(el)}
|
||||
sitekey={AppConfig.getRecaptcha2SiteKey()}
|
||||
onChange={(value: string) => {
|
||||
model.recaptcha = value;
|
||||
setModel(model);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
<div style={{ fontSize: '12px', padding: '10px 0px' }}>
|
||||
<FormattedMessage
|
||||
id="registration.termandconditions"
|
||||
defaultMessage="Terms of Client: Please check the WiseMapping Account information you've entered above, and review the Terms of Client here. By clicking on 'Register' below you are agreeing to the Terms of Client above and the Privacy Policy"
|
||||
/>
|
||||
</div>
|
||||
{AppConfig.isRecaptcha2Enabled() && (
|
||||
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>
|
||||
<ReCAPTCHA
|
||||
ref={(el) => setCaptcha(el)}
|
||||
sitekey={AppConfig.getRecaptcha2SiteKey()}
|
||||
onChange={(value: string) => {
|
||||
model.recaptcha = value;
|
||||
setModel(model);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ fontSize: '12px', padding: '10px 0px' }}>
|
||||
<FormattedMessage
|
||||
id="registration.termandconditions"
|
||||
defaultMessage="Terms of Client: Please check the WiseMapping Account information you've entered above, and review the Terms of Client here. By clicking on 'Register' below you are agreeing to the Terms of Client above and the Privacy Policy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SubmitButton
|
||||
value={intl.formatMessage({
|
||||
id: 'registration.register',
|
||||
defaultMessage: 'Register',
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</FormControl>
|
||||
</FormContainer>
|
||||
);
|
||||
<SubmitButton
|
||||
value={intl.formatMessage({
|
||||
id: 'registration.register',
|
||||
defaultMessage: 'Register',
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</FormControl>
|
||||
</FormContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const RegistationPage = (): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({
|
||||
id: 'registration.page-title',
|
||||
defaultMessage: 'Registration | WiseMapping',
|
||||
});
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Registration:Init' });
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({
|
||||
id: 'registration.page-title',
|
||||
defaultMessage: 'Registration | WiseMapping',
|
||||
});
|
||||
ReactGA.send({
|
||||
hitType: 'pageview',
|
||||
page: window.location.pathname,
|
||||
title: 'Registration:Init',
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header type="only-signin" />
|
||||
<RegistrationForm />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Header type="only-signin" />
|
||||
<RegistrationForm />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistationPage;
|
||||
|
@ -8,47 +8,53 @@ import Typography from '@mui/material/Typography';
|
||||
import Button from '@mui/material/Button';
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
|
||||
const RegistrationSuccessPage = (): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const intl = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({ id: 'registation.success-title', defaultMessage: 'Registation Success | WiseMapping' });
|
||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Registration:Success' });
|
||||
useEffect(() => {
|
||||
document.title = intl.formatMessage({
|
||||
id: 'registation.success-title',
|
||||
defaultMessage: 'Registation Success | WiseMapping',
|
||||
});
|
||||
ReactGA.send({
|
||||
hitType: 'pageview',
|
||||
page: window.location.pathname,
|
||||
title: 'Registration:Success',
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header type="none" />
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage
|
||||
id="resetpassword.success.title"
|
||||
defaultMessage="Your account has been created successfully"
|
||||
/>
|
||||
</Typography>
|
||||
return (
|
||||
<div>
|
||||
<Header type="none" />
|
||||
<FormContainer>
|
||||
<Typography variant="h4" component="h1">
|
||||
<FormattedMessage
|
||||
id="resetpassword.success.title"
|
||||
defaultMessage="Your account has been created successfully"
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Typography paragraph>
|
||||
<FormattedMessage
|
||||
id="registration.success.desc"
|
||||
defaultMessage="Click 'Sign In' button below and start creating mind maps."
|
||||
/>
|
||||
</Typography>
|
||||
<Typography paragraph>
|
||||
<FormattedMessage
|
||||
id="registration.success.desc"
|
||||
defaultMessage="Click 'Sign In' button below and start creating mind maps."
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to="/c/login"
|
||||
disableElevation={true}
|
||||
>
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</FormContainer>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
<Button
|
||||
color="primary"
|
||||
size="medium"
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to="/c/login"
|
||||
disableElevation={true}
|
||||
>
|
||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||
</Button>
|
||||
</FormContainer>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationSuccessPage;
|
||||
|
@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
|
||||
import App from './app';
|
||||
|
||||
async function bootstrapApplication() {
|
||||
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
|
||||
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
|
||||
}
|
||||
|
||||
bootstrapApplication();
|
||||
|
Loading…
Reference in New Issue
Block a user