mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-24 15:37:55 +01:00
Format CSS and TSX files.
This commit is contained in:
parent
0f4a8ee087
commit
ae02780a1a
@ -13,7 +13,7 @@
|
|||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
background-image: url("../img/bootstrap-colorpicker/saturation.png");
|
background-image: url('../img/bootstrap-colorpicker/saturation.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker-saturation i {
|
.colorpicker-saturation i {
|
||||||
@ -26,8 +26,8 @@
|
|||||||
margin: -4px 0 0 -4px;
|
margin: -4px 0 0 -4px;
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
-webkit-border-radius: 5px;
|
-webkit-border-radius: 5px;
|
||||||
-moz-border-radius: 5px;
|
-moz-border-radius: 5px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker-saturation i b {
|
.colorpicker-saturation i b {
|
||||||
@ -36,8 +36,8 @@
|
|||||||
height: 5px;
|
height: 5px;
|
||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
-webkit-border-radius: 5px;
|
-webkit-border-radius: 5px;
|
||||||
-moz-border-radius: 5px;
|
-moz-border-radius: 5px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker-hue,
|
.colorpicker-hue,
|
||||||
@ -64,12 +64,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker-hue {
|
.colorpicker-hue {
|
||||||
background-image: url("../img/bootstrap-colorpicker/hue.png");
|
background-image: url('../img/bootstrap-colorpicker/hue.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker-alpha {
|
.colorpicker-alpha {
|
||||||
display: none;
|
display: none;
|
||||||
background-image: url("../img/bootstrap-colorpicker/alpha.png");
|
background-image: url('../img/bootstrap-colorpicker/alpha.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker {
|
.colorpicker {
|
||||||
@ -80,8 +80,8 @@
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
-webkit-border-radius: 4px;
|
-webkit-border-radius: 4px;
|
||||||
-moz-border-radius: 4px;
|
-moz-border-radius: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
*zoom: 1;
|
*zoom: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@
|
|||||||
.colorpicker:after {
|
.colorpicker:after {
|
||||||
display: table;
|
display: table;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
content: "";
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker:after {
|
.colorpicker:after {
|
||||||
@ -135,7 +135,7 @@
|
|||||||
height: 10px;
|
height: 10px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
clear: both;
|
clear: both;
|
||||||
background-image: url("../img/bootstrap-colorpicker/alpha.png");
|
background-image: url('../img/bootstrap-colorpicker/alpha.png');
|
||||||
background-position: 0 100%;
|
background-position: 0 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,11 +194,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.colorpicker.colorpicker-horizontal .colorpicker-hue {
|
.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 {
|
.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 {
|
.colorpicker.colorpicker-hidden {
|
||||||
|
@ -6,4 +6,180 @@
|
|||||||
* Licensed under the Apache License v2.0
|
* Licensed under the Apache License v2.0
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
* 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-info,
|
||||||
.btn-warning,
|
.btn-warning,
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
|
||||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
-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, .15), 0 1px 1px rgba(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-default:active,
|
||||||
.btn-primary:active,
|
.btn-primary:active,
|
||||||
@ -26,8 +26,8 @@
|
|||||||
.btn-info.active,
|
.btn-info.active,
|
||||||
.btn-warning.active,
|
.btn-warning.active,
|
||||||
.btn-danger.active {
|
.btn-danger.active {
|
||||||
-webkit-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, .125);
|
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||||
}
|
}
|
||||||
.btn:active,
|
.btn:active,
|
||||||
.btn.active {
|
.btn.active {
|
||||||
@ -36,7 +36,7 @@
|
|||||||
.btn-default {
|
.btn-default {
|
||||||
text-shadow: 0 1px 0 #fff;
|
text-shadow: 0 1px 0 #fff;
|
||||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
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(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -55,7 +55,7 @@
|
|||||||
}
|
}
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
|
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(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -73,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
.btn-success {
|
.btn-success {
|
||||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
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(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -91,7 +91,7 @@
|
|||||||
}
|
}
|
||||||
.btn-info {
|
.btn-info {
|
||||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
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(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -109,7 +109,7 @@
|
|||||||
}
|
}
|
||||||
.btn-warning {
|
.btn-warning {
|
||||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
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(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -127,7 +127,7 @@
|
|||||||
}
|
}
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
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(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -145,14 +145,14 @@
|
|||||||
}
|
}
|
||||||
.thumbnail,
|
.thumbnail,
|
||||||
.img-thumbnail {
|
.img-thumbnail {
|
||||||
-webkit-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, .075);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
}
|
}
|
||||||
.dropdown-menu > li > a:hover,
|
.dropdown-menu > li > a:hover,
|
||||||
.dropdown-menu > li > a:focus {
|
.dropdown-menu > li > a:focus {
|
||||||
background-color: #e8e8e8;
|
background-color: #e8e8e8;
|
||||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
@ -161,50 +161,50 @@
|
|||||||
.dropdown-menu > .active > a:focus {
|
.dropdown-menu > .active > a:focus {
|
||||||
background-color: #357ebd;
|
background-color: #357ebd;
|
||||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.navbar-default {
|
.navbar-default {
|
||||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
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(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
-webkit-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, .15), 0 1px 5px rgba(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 {
|
.navbar-default .navbar-nav > .active > a {
|
||||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
-webkit-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, .075);
|
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||||
}
|
}
|
||||||
.navbar-brand,
|
.navbar-brand,
|
||||||
.navbar-nav > li > a {
|
.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 {
|
.navbar-inverse {
|
||||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
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(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.navbar-inverse .navbar-nav > .active > a {
|
.navbar-inverse .navbar-nav > .active > a {
|
||||||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
-webkit-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, .25);
|
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
.navbar-inverse .navbar-brand,
|
.navbar-inverse .navbar-brand,
|
||||||
.navbar-inverse .navbar-nav > li > a {
|
.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-static-top,
|
||||||
.navbar-fixed-top,
|
.navbar-fixed-top,
|
||||||
@ -212,136 +212,136 @@
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
.alert {
|
.alert {
|
||||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
-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, .25), 0 1px 2px rgba(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 {
|
.alert-success {
|
||||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-color: #b2dba1;
|
border-color: #b2dba1;
|
||||||
}
|
}
|
||||||
.alert-info {
|
.alert-info {
|
||||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-color: #9acfea;
|
border-color: #9acfea;
|
||||||
}
|
}
|
||||||
.alert-warning {
|
.alert-warning {
|
||||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-color: #f5e79e;
|
border-color: #f5e79e;
|
||||||
}
|
}
|
||||||
.alert-danger {
|
.alert-danger {
|
||||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-color: #dca7a7;
|
border-color: #dca7a7;
|
||||||
}
|
}
|
||||||
.progress {
|
.progress {
|
||||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.progress-bar-success {
|
.progress-bar-success {
|
||||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.progress-bar-info {
|
.progress-bar-info {
|
||||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.progress-bar-warning {
|
.progress-bar-warning {
|
||||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.progress-bar-danger {
|
.progress-bar-danger {
|
||||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.list-group {
|
.list-group {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
-webkit-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, .075);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
}
|
}
|
||||||
.list-group-item.active,
|
.list-group-item.active,
|
||||||
.list-group-item.active:hover,
|
.list-group-item.active:hover,
|
||||||
.list-group-item.active:focus {
|
.list-group-item.active:focus {
|
||||||
text-shadow: 0 -1px 0 #3071a9;
|
text-shadow: 0 -1px 0 #3071a9;
|
||||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-color: #3278b3;
|
border-color: #3278b3;
|
||||||
}
|
}
|
||||||
.panel {
|
.panel {
|
||||||
-webkit-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, .05);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
.panel-default > .panel-heading {
|
.panel-default > .panel-heading {
|
||||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.panel-primary > .panel-heading {
|
.panel-primary > .panel-heading {
|
||||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.panel-success > .panel-heading {
|
.panel-success > .panel-heading {
|
||||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.panel-info > .panel-heading {
|
.panel-info > .panel-heading {
|
||||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.panel-warning > .panel-heading {
|
.panel-warning > .panel-heading {
|
||||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.panel-danger > .panel-heading {
|
.panel-danger > .panel-heading {
|
||||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
}
|
}
|
||||||
.well {
|
.well {
|
||||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
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);
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
border-color: #dcdcdc;
|
border-color: #dcdcdc;
|
||||||
-webkit-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, .05), 0 1px 0 rgba(255, 255, 255, .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 */
|
/*# 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
16
packages/editor/src/bootstrap-fixes.css
vendored
16
packages/editor/src/bootstrap-fixes.css
vendored
@ -6,15 +6,15 @@
|
|||||||
/* bootstrap modal */
|
/* bootstrap modal */
|
||||||
|
|
||||||
.wise-editor .modal {
|
.wise-editor .modal {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-backdrop {
|
.modal-backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: rgba(0,0,0,0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
z-index: 1000;
|
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 {
|
div#header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height:50px;
|
height: 50px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index:1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#headerNotifier {
|
div#headerNotifier {
|
||||||
border: 1px solid rgb(241, 163, 39);
|
border: 1px solid rgb(241, 163, 39);
|
||||||
background-color: rgb(252, 235, 192);
|
background-color: rgb(252, 235, 192);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding: 5px 9px;
|
padding: 5px 9px;
|
||||||
color: back;
|
color: back;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
display: none;
|
display: none;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbarRight {
|
div#toolbarRight {
|
||||||
float: right;
|
float: right;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 6px 10px;
|
margin: 6px 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#account {
|
#account {
|
||||||
float: right;
|
float: right;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#account >img {
|
#account > img {
|
||||||
width: 36x;
|
width: 36x;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#accountSettingsPanel{
|
#accountSettingsPanel {
|
||||||
padding:10px 10px;
|
padding: 10px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#share {
|
#share {
|
||||||
margin: 0 30px;
|
margin: 0 30px;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionButton {
|
.actionButton {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 10px 25px;
|
padding: 10px 25px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
min-width: 64px;
|
min-width: 64px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
color: white;
|
color: white;
|
||||||
background-color: #ffa800;
|
background-color: #ffa800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionButton:hover {
|
.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 {
|
div#toolbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
min-width: 900px;
|
min-width: 900px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonContainer {
|
div#toolbar .buttonContainer {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
float: left;
|
float: left;
|
||||||
border-left: 1px solid lightgray;
|
border-left: 1px solid lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#mapName >span {
|
div#mapName > span {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
float: left;
|
float: left;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#backToList {
|
div#backToList {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
float: left;
|
float: left;
|
||||||
margin: 13px 20px;
|
margin: 13px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************************/
|
/******************************************************************************************/
|
||||||
@ -108,120 +109,120 @@ div#toolbar .buttonOn,
|
|||||||
div#toolbar .buttonOff,
|
div#toolbar .buttonOff,
|
||||||
div#toolbar .buttonActive,
|
div#toolbar .buttonActive,
|
||||||
div#toolbar .buttonOn:hover {
|
div#toolbar .buttonOn:hover {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
float: left;
|
float: left;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonOn:hover {
|
div#toolbar .buttonOn:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonOn {
|
div#toolbar .buttonOn {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonOff {
|
div#toolbar .buttonOff {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#exportAnchor {
|
div#exportAnchor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonExtOn,
|
div#toolbar .buttonExtOn,
|
||||||
div#toolbar .buttonExtOff,
|
div#toolbar .buttonExtOff,
|
||||||
div#toolbar .buttonExtActive,
|
div#toolbar .buttonExtActive,
|
||||||
div#toolbar .buttonExtOn:hover {
|
div#toolbar .buttonExtOn:hover {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
float: left;
|
float: left;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonExtOn:hover {
|
div#toolbar .buttonExtOn:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonExtActive {
|
div#toolbar .buttonExtActive {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonExtOn {
|
div#toolbar .buttonExtOn {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
cursor: pointer
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#toolbar .buttonExtOff {
|
div#toolbar .buttonExtOff {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#exportAnchor {
|
div#exportAnchor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************************************************/
|
/***************************************************************************************************/
|
||||||
/* Other toolbar styles */
|
/* Other toolbar styles */
|
||||||
/***************************************************************************************************/
|
/***************************************************************************************************/
|
||||||
.toolbarTip {
|
.toolbarTip {
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
padding: 5px 5px;
|
padding: 5px 5px;
|
||||||
color: #f5f5f5;
|
color: #f5f5f5;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#colorPalette {
|
div#colorPalette {
|
||||||
border: 1px solid #bbb4d6;
|
border: 1px solid #bbb4d6;
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
width: 160px;
|
width: 160px;
|
||||||
top: 89px;
|
top: 89px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.toolbarPanelLink,
|
div.toolbarPanelLink,
|
||||||
div.toolbarPanelLinkSelectedLink {
|
div.toolbarPanelLinkSelectedLink {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: black;
|
color: black;
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.toolbarPanelLink:hover,
|
div.toolbarPanelLink:hover,
|
||||||
div.toolbarPanelLinkSelectedLink {
|
div.toolbarPanelLinkSelectedLink {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbarPaneTip {
|
.toolbarPaneTip {
|
||||||
background-color: rgb(228, 226, 210);
|
background-color: rgb(228, 226, 210);
|
||||||
padding: 5px 5px;
|
padding: 5px 5px;
|
||||||
color: #f5f5f5;
|
color: #f5f5f5;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
||||||
border: 3px double rgb(190, 190, 190);
|
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 ToolbarActionType = 'export' | 'publish' | 'history' | 'print' | 'share' | 'info';
|
||||||
|
|
||||||
export type ToolbarPropsType = {
|
export type ToolbarPropsType = {
|
||||||
editorMode: EditorRenderMode;
|
editorMode: EditorRenderMode;
|
||||||
onAction: (action: ToolbarActionType) => void;
|
onAction: (action: ToolbarActionType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Toolbar({
|
export default function Toolbar({
|
||||||
editorMode: editorMode,
|
editorMode: editorMode,
|
||||||
onAction,
|
onAction,
|
||||||
}: ToolbarPropsType): React.ReactElement {
|
}: ToolbarPropsType): React.ReactElement {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
return (
|
return (
|
||||||
<HeaderContainer className="wise-editor">
|
<HeaderContainer className="wise-editor">
|
||||||
<div id="toolbar">
|
<div id="toolbar">
|
||||||
<div id="backToList">
|
<div id="backToList">
|
||||||
<img src={BackIconSvg} />
|
<img src={BackIconSvg} />
|
||||||
</div>
|
</div>
|
||||||
{(editorMode === 'edition-editor' || editorMode === 'edition-owner') && (
|
{(editorMode === 'edition-editor' || editorMode === 'edition-owner') && (
|
||||||
<div id="persist" className="buttonContainer">
|
<div id="persist" className="buttonContainer">
|
||||||
<ToolbarButton id="save" className="buttonOn">
|
<ToolbarButton id="save" className="buttonOn">
|
||||||
<img src={SaveSvg} />
|
<img src={SaveSvg} />
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(editorMode === 'edition-editor' || editorMode === 'edition-owner' || editorMode === 'showcase') && (
|
{(editorMode === 'edition-editor' ||
|
||||||
<>
|
editorMode === 'edition-owner' ||
|
||||||
<div id="edit" className="buttonContainer">
|
editorMode === 'showcase') && (
|
||||||
<ToolbarButton id="undoEdition" className="buttonOn">
|
<>
|
||||||
<img src={UndoSvg} />
|
<div id="edit" className="buttonContainer">
|
||||||
</ToolbarButton>
|
<ToolbarButton id="undoEdition" className="buttonOn">
|
||||||
<ToolbarButton id="redoEdition" className="buttonOn">
|
<img src={UndoSvg} />
|
||||||
<img src={RedoSvg} />
|
</ToolbarButton>
|
||||||
</ToolbarButton>
|
<ToolbarButton id="redoEdition" className="buttonOn">
|
||||||
</div>
|
<img src={RedoSvg} />
|
||||||
<div id="nodeStyle" className="buttonContainer">
|
</ToolbarButton>
|
||||||
<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>
|
</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 */
|
/* Header & Toolbar Styles */
|
||||||
/********************************************************************************/
|
/********************************************************************************/
|
||||||
@import "bootstrap-prefix.min.css";
|
@import 'bootstrap-prefix.min.css';
|
||||||
@import "bootstrap-fixes.css";
|
@import 'bootstrap-fixes.css';
|
||||||
|
|
||||||
html {
|
html {
|
||||||
/* avoid bootstrap overriding font-size and breaking Mui */
|
/* avoid bootstrap overriding font-size and breaking Mui */
|
||||||
font-size: initial;
|
font-size: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
min-width: 100vw;
|
min-width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mindplot-root {
|
.mindplot-root {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#mindplot {
|
div#mindplot {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
background-image: linear-gradient(#ebe9e7 1px, transparent 1px), linear-gradient(to right, #ebe9e7 1px, #f2f2f2 1px);
|
background-image: linear-gradient(#ebe9e7 1px, transparent 1px),
|
||||||
background-size: 50px 50px;
|
linear-gradient(to right, #ebe9e7 1px, #f2f2f2 1px);
|
||||||
|
background-size: 50px 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notesTip {
|
.notesTip {
|
||||||
background-color: #dfcf3c;
|
background-color: #dfcf3c;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
/*font-weight: bold;*/
|
/*font-weight: bold;*/
|
||||||
/*width: 100px;*/
|
/*width: 100px;*/
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
-moz-border-radius: 3px;
|
-moz-border-radius: 3px;
|
||||||
-webkit-border-radius: 3px;
|
-webkit-border-radius: 3px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.linkTip {
|
.linkTip {
|
||||||
background-color: #dfcf3c;
|
background-color: #dfcf3c;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
/*font-weight: bold;*/
|
/*font-weight: bold;*/
|
||||||
/*width: 100px;*/
|
/*width: 100px;*/
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
-moz-border-radius: 3px;
|
-moz-border-radius: 3px;
|
||||||
-webkit-border-radius: 3px;
|
-webkit-border-radius: 3px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboardShortcutTip {
|
.keyboardShortcutTip {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
/* Modal dialogs definitions */
|
/* Modal dialogs definitions */
|
||||||
|
|
||||||
div.modalDialog {
|
div.modalDialog {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
z-index: 11000;
|
z-index: 11000;
|
||||||
width: 500px;
|
width: 500px;
|
||||||
margin: -250px 0 0 -250px;
|
margin: -250px 0 0 -250px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
-webkit-background-clip: padding-box;
|
-webkit-background-clip: padding-box;
|
||||||
-moz-background-clip: padding-box;
|
-moz-background-clip: padding-box;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.modalDialog .content {
|
div.modalDialog .content {
|
||||||
padding: 5px 5px;
|
padding: 5px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.modalDialog .title {
|
div.modalDialog .title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-shadow: 1px 1px 0 #fff;
|
text-shadow: 1px 1px 0 #fff;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--- End Modal Dialog Form ---*/
|
/*--- End Modal Dialog Form ---*/
|
||||||
|
|
||||||
.publishModalDialog .content {
|
.publishModalDialog .content {
|
||||||
height: 420px;
|
height: 420px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.exportModalDialog .content {
|
.exportModalDialog .content {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shareModalDialog .content {
|
.shareModalDialog .content {
|
||||||
height: 440px;
|
height: 440px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.shareModalDialog {
|
div.shareModalDialog {
|
||||||
width: 550px;
|
width: 550px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelIcon {
|
.panelIcon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
cursor: pointer
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelIcon:hover {
|
.panelIcon:hover {
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wise-editor .popover {
|
.wise-editor .popover {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#floating-panel {
|
#floating-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#position {
|
div#position {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#position-button {
|
#position-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: solid black 1px;
|
border: solid black 1px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 40px 40px;
|
background-size: 40px 40px;
|
||||||
background-color: #FFF;
|
background-color: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zoom-button {
|
#zoom-button {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zoom-plus,
|
#zoom-plus,
|
||||||
#zoom-minus {
|
#zoom-minus {
|
||||||
border: solid black 1px;
|
border: solid black 1px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 40px 40px;
|
background-size: 40px 40px;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #FFF;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zoom-plus {
|
#zoom-plus {
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zoom-minus {
|
#zoom-minus {
|
||||||
border-radius: 0 0 8px 8px;
|
border-radius: 0 0 8px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#shotcuts > img{
|
div#shotcuts > img {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyboardTable {
|
#keyboardTable {
|
||||||
font-family: Arial, verdana, serif;
|
font-family: Arial, verdana, serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyboardTable td {
|
#keyboardTable td {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyboardTable th {
|
#keyboardTable th {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keyboardTable th {
|
#keyboardTable th {
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanel {
|
.tryInfoPanel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: solid 2px #ffa800;
|
border: solid 2px #ffa800;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 99%;
|
width: 99%;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
width: 96%;
|
width: 96%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) {
|
@media (min-width: 600px) {
|
||||||
.tryInfoPanel {
|
.tryInfoPanel {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.tryInfoPanel {
|
.tryInfoPanel {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanel .tryInfoPanelInner {
|
.tryInfoPanel .tryInfoPanelInner {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanel .tryInfoPanelInner .closeButton {
|
.tryInfoPanel .tryInfoPanelInner .closeButton {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanel .tryInfoPanelInner .closeButton button {
|
.tryInfoPanel .tryInfoPanelInner .closeButton button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-style: hidden;
|
border-style: hidden;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanel .tryInfoPanelInner .closeButton button img {
|
.tryInfoPanel .tryInfoPanelInner .closeButton button img {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
filter: invert(73%) sepia(21%) saturate(4699%) hue-rotate(357deg) brightness(98%) contrast(108%);
|
filter: invert(73%) sepia(21%) saturate(4699%) hue-rotate(357deg) brightness(98%) contrast(108%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanelWithToolbar {
|
.tryInfoPanelWithToolbar {
|
||||||
top: 55px;
|
top: 55px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanelWithoutToolbar {
|
.tryInfoPanelWithoutToolbar {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanelClosed {
|
.tryInfoPanelClosed {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tryInfoPanel > p {
|
.tryInfoPanel > p {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
@ -1,33 +1,33 @@
|
|||||||
/* Overwrite some styles */
|
/* Overwrite some styles */
|
||||||
div#footer {
|
div#footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: #ffa800;
|
background-color: #ffa800;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#footer-desc {
|
div#footer-desc {
|
||||||
float: left;
|
float: left;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
padding: 0px 10px;
|
padding: 0px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#footer-logo {
|
div#footer-logo {
|
||||||
float: left;
|
float: left;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#floating-panel {
|
#floating-panel {
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#mindplot {
|
div#mindplot {
|
||||||
top:0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#toolbar {
|
#toolbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
@ -21,7 +21,6 @@ import Editor, { EditorOptions } from '../../../../src/index';
|
|||||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||||
|
|
||||||
const initialization = (designer: Designer) => {
|
const initialization = (designer: Designer) => {
|
||||||
|
|
||||||
designer.addEvent('loadSuccess', () => {
|
designer.addEvent('loadSuccess', () => {
|
||||||
const elem = document.getElementById('mindplot');
|
const elem = document.getElementById('mindplot');
|
||||||
if (elem) {
|
if (elem) {
|
||||||
@ -35,10 +34,10 @@ const mapId = 'welcome';
|
|||||||
const options: EditorOptions = {
|
const options: EditorOptions = {
|
||||||
zoom: 0.8,
|
zoom: 0.8,
|
||||||
locked: false,
|
locked: false,
|
||||||
mapTitle: "Develop WiseMapping",
|
mapTitle: 'Develop WiseMapping',
|
||||||
mode: 'edition-owner',
|
mode: 'edition-owner',
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
enableKeyboardEvents: true
|
enableKeyboardEvents: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
|
@ -5,7 +5,6 @@ import Editor, { EditorOptions } from '../../../../src/index';
|
|||||||
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||||
|
|
||||||
const initialization = (designer: Designer) => {
|
const initialization = (designer: Designer) => {
|
||||||
|
|
||||||
designer.addEvent('loadSuccess', () => {
|
designer.addEvent('loadSuccess', () => {
|
||||||
const elem = document.getElementById('mindplot');
|
const elem = document.getElementById('mindplot');
|
||||||
if (elem) {
|
if (elem) {
|
||||||
@ -25,7 +24,6 @@ const initialization = (designer: Designer) => {
|
|||||||
option.selected = option.value === mapId;
|
option.selected = option.value === mapId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,10 +34,10 @@ const persistence = new LocalStorageManager('samples/{id}.wxml', false);
|
|||||||
const options: EditorOptions = {
|
const options: EditorOptions = {
|
||||||
zoom: 0.8,
|
zoom: 0.8,
|
||||||
locked: false,
|
locked: false,
|
||||||
mapTitle: "Develop WiseMapping",
|
mapTitle: 'Develop WiseMapping',
|
||||||
mode: 'viewonly',
|
mode: 'viewonly',
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
enableKeyboardEvents: true
|
enableKeyboardEvents: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
body
|
body {
|
||||||
{
|
font-size: 1em !important;
|
||||||
font-size: 1em !important;
|
color: #000 !important;
|
||||||
color: #000 !important;
|
font-family: Arial !important;
|
||||||
font-family: Arial !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
border: 1px solid darkgray;
|
border: 1px solid darkgray;
|
||||||
border-spacing: 0px;
|
border-spacing: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
border: 1px solid darkgray;
|
border: 1px solid darkgray;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr td:first-child {
|
tbody tr td:first-child {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eventForm {
|
.eventForm {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
@ -11,45 +11,42 @@ import Button from '@mui/material/Button';
|
|||||||
import AlertTitle from '@mui/material/AlertTitle';
|
import AlertTitle from '@mui/material/AlertTitle';
|
||||||
|
|
||||||
const ClientHealthSentinel = (): React.ReactElement => {
|
const ClientHealthSentinel = (): React.ReactElement => {
|
||||||
const status: ClientStatus = useSelector(activeInstanceStatus);
|
const status: ClientStatus = useSelector(activeInstanceStatus);
|
||||||
|
|
||||||
const handleOnClose = () => {
|
const handleOnClose = () => {
|
||||||
window.location.href = '/c/login';
|
window.location.href = '/c/login';
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
open={status.state != 'healthy'}
|
open={status.state != 'healthy'}
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
maxWidth="sm"
|
maxWidth="sm"
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
>
|
>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<FormattedMessage
|
<FormattedMessage id="expired.title" defaultMessage="Your session has expired" />
|
||||||
id="expired.title"
|
</DialogTitle>
|
||||||
defaultMessage="Your session has expired"
|
|
||||||
/>
|
|
||||||
</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Alert severity="error">
|
<Alert severity="error">
|
||||||
<AlertTitle>
|
<AlertTitle>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="expired.description"
|
id="expired.description"
|
||||||
defaultMessage="Your current session has expired. Please, sign in and try again."
|
defaultMessage="Your current session has expired. Please, sign in and try again."
|
||||||
/>
|
/>
|
||||||
</AlertTitle>
|
</AlertTitle>
|
||||||
</Alert>
|
</Alert>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button type="button" color="primary" size="medium" onClick={handleOnClose}>
|
<Button type="button" color="primary" size="medium" onClick={handleOnClose}>
|
||||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default ClientHealthSentinel;
|
export default ClientHealthSentinel;
|
||||||
|
@ -13,78 +13,82 @@ import { activeInstance, fetchAccount, fetchMapById } from '../../redux/clientSl
|
|||||||
import EditorOptionsBuilder from './EditorOptionsBuilder';
|
import EditorOptionsBuilder from './EditorOptionsBuilder';
|
||||||
|
|
||||||
export type EditorPropsType = {
|
export type EditorPropsType = {
|
||||||
isTryMode: boolean;
|
isTryMode: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
||||||
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
||||||
const hotkey = useSelector(hotkeysEnabled);
|
const hotkey = useSelector(hotkeysEnabled);
|
||||||
const userLocale = AppI18n.getUserLocale();
|
const userLocale = AppI18n.getUserLocale();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const findEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
const findEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
||||||
let result: EditorRenderMode = null;
|
let result: EditorRenderMode = null;
|
||||||
if (isTryMode) {
|
if (isTryMode) {
|
||||||
result = 'showcase';
|
result = 'showcase';
|
||||||
} else if (global.mindmapLocked) {
|
} else if (global.mindmapLocked) {
|
||||||
result = 'viewonly';
|
result = 'viewonly';
|
||||||
} else {
|
} else {
|
||||||
const fetchResult = fetchMapById(mapId);
|
const fetchResult = fetchMapById(mapId);
|
||||||
if (!fetchResult.isLoading) {
|
if (!fetchResult.isLoading) {
|
||||||
if (fetchResult.error) {
|
if (fetchResult.error) {
|
||||||
throw new Error(`Map info could not be loaded: ${JSON.stringify(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}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 ?
|
// What is the role ?
|
||||||
const mapId = EditorOptionsBuilder.loadMapId();
|
const mapId = EditorOptionsBuilder.loadMapId();
|
||||||
const mode = findEditorMode(isTryMode, mapId);
|
const mode = findEditorMode(isTryMode, mapId);
|
||||||
|
|
||||||
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
||||||
// at the i18n resource loading.
|
// at the i18n resource loading.
|
||||||
const isAccountLoaded = mode === 'showcase' || fetchAccount;
|
const isAccountLoaded = mode === 'showcase' || fetchAccount;
|
||||||
const loadCompleted = mode && isAccountLoaded;
|
const loadCompleted = mode && isAccountLoaded;
|
||||||
|
|
||||||
let options, persistence: PersistenceManager;
|
let options, persistence: PersistenceManager;
|
||||||
if (loadCompleted) {
|
if (loadCompleted) {
|
||||||
options = EditorOptionsBuilder.build(userLocale.code, mode, hotkey);
|
options = EditorOptionsBuilder.build(userLocale.code, mode, hotkey);
|
||||||
persistence = client.buildPersistenceManager(mode);
|
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>) : <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
export default EditorPage;
|
||||||
|
|
||||||
|
@ -18,78 +18,85 @@ import Typography from '@mui/material/Typography';
|
|||||||
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
||||||
|
|
||||||
const ForgotPassword = () => {
|
const ForgotPassword = () => {
|
||||||
const [email, setEmail] = useState<string>('');
|
const [email, setEmail] = useState<string>('');
|
||||||
const [error, setError] = useState<ErrorInfo>();
|
const [error, setError] = useState<ErrorInfo>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const service: Client = useSelector(activeInstance);
|
const service: Client = useSelector(activeInstance);
|
||||||
const mutation = useMutation<void, ErrorInfo, string>(
|
const mutation = useMutation<void, ErrorInfo, string>(
|
||||||
(email: string) => service.resetPassword(email),
|
(email: string) => service.resetPassword(email),
|
||||||
{
|
{
|
||||||
onSuccess: () => history.push('/c/forgot-password-success'),
|
onSuccess: () => history.push('/c/forgot-password-success'),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
mutation.mutate(email);
|
mutation.mutate(email);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage id="forgot.title" defaultMessage="Reset your password" />
|
<FormattedMessage id="forgot.title" defaultMessage="Reset your password" />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="forgot.desc"
|
id="forgot.desc"
|
||||||
defaultMessage="We will send you an email to reset your password."
|
defaultMessage="We will send you an email to reset your password."
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<GlobalError error={error} />
|
<GlobalError error={error} />
|
||||||
|
|
||||||
<form onSubmit={handleOnSubmit}>
|
<form onSubmit={handleOnSubmit}>
|
||||||
<input type='hidden' value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
<input type="hidden" value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
||||||
<Input
|
<Input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
label={intl.formatMessage({ id: 'forgot.email', defaultMessage: 'Email' })}
|
label={intl.formatMessage({ id: 'forgot.email', defaultMessage: 'Email' })}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
value={intl.formatMessage({
|
value={intl.formatMessage({
|
||||||
id: 'forgot.register',
|
id: 'forgot.register',
|
||||||
defaultMessage: 'Send recovery link',
|
defaultMessage: 'Send recovery link',
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ForgotPasswordPage = (): React.ReactElement => {
|
const ForgotPasswordPage = (): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = intl.formatMessage({ id: 'forgot.page-title', defaultMessage: 'Forgot Password | WiseMapping' });
|
document.title = intl.formatMessage({
|
||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'ForgotPassword:Init' });
|
id: 'forgot.page-title',
|
||||||
}, []);
|
defaultMessage: 'Forgot Password | WiseMapping',
|
||||||
|
});
|
||||||
|
ReactGA.send({
|
||||||
|
hitType: 'pageview',
|
||||||
|
page: window.location.pathname,
|
||||||
|
title: 'ForgotPassword:Init',
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type="only-signin" />
|
<Header type="only-signin" />
|
||||||
<ForgotPassword />
|
<ForgotPassword />
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ForgotPasswordPage };
|
export { ForgotPasswordPage };
|
||||||
|
@ -9,45 +9,52 @@ import Button from '@mui/material/Button';
|
|||||||
import ReactGA from 'react-ga4';
|
import ReactGA from 'react-ga4';
|
||||||
|
|
||||||
const ForgotPasswordSuccessPage = (): React.ReactElement => {
|
const ForgotPasswordSuccessPage = (): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = intl.formatMessage({ id: 'forgotsuccess.page-title', defaultMessage: 'Password Recovered | WiseMapping' });
|
document.title = intl.formatMessage({
|
||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'ForgotPassword:Success' });
|
id: 'forgotsuccess.page-title',
|
||||||
|
defaultMessage: 'Password Recovered | WiseMapping',
|
||||||
});
|
});
|
||||||
|
ReactGA.send({
|
||||||
|
hitType: 'pageview',
|
||||||
|
page: window.location.pathname,
|
||||||
|
title: 'ForgotPassword:Success',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type="none" />
|
<Header type="none" />
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="forgot.success.title"
|
id="forgot.success.title"
|
||||||
defaultMessage="Your temporal password has been sent."
|
defaultMessage="Your temporal password has been sent."
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="forgot.success.desc"
|
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."
|
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>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to="/c/login"
|
to="/c/login"
|
||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ForgotPasswordSuccessPage;
|
export default ForgotPasswordSuccessPage;
|
||||||
|
@ -3,19 +3,19 @@ import { ErrorInfo } from '../../../classes/client';
|
|||||||
import StyledAlert from './styled';
|
import StyledAlert from './styled';
|
||||||
|
|
||||||
type GlobalErrorProps = {
|
type GlobalErrorProps = {
|
||||||
error?: ErrorInfo;
|
error?: ErrorInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GlobalError = (props: GlobalErrorProps): React.ReactElement | null => {
|
const GlobalError = (props: GlobalErrorProps): React.ReactElement | null => {
|
||||||
const error = props.error;
|
const error = props.error;
|
||||||
const hasError = Boolean(error?.msg);
|
const hasError = Boolean(error?.msg);
|
||||||
const errorMsg = error?.msg;
|
const errorMsg = error?.msg;
|
||||||
|
|
||||||
return hasError ? (
|
return hasError ? (
|
||||||
<StyledAlert severity="error" variant="filled" hidden={!hasError}>
|
<StyledAlert severity="error" variant="filled" hidden={!hasError}>
|
||||||
{errorMsg}
|
{errorMsg}
|
||||||
</StyledAlert>
|
</StyledAlert>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default GlobalError;
|
export default GlobalError;
|
||||||
|
@ -3,51 +3,51 @@ import React, { ChangeEvent } from 'react';
|
|||||||
import { ErrorInfo } from '../../../classes/client';
|
import { ErrorInfo } from '../../../classes/client';
|
||||||
|
|
||||||
type InputProps = {
|
type InputProps = {
|
||||||
name: string;
|
name: string;
|
||||||
error?: ErrorInfo;
|
error?: ErrorInfo;
|
||||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
label: string;
|
label: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
autoComplete?: string;
|
autoComplete?: string;
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
maxLength?: number,
|
maxLength?: number;
|
||||||
rows?: number
|
rows?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Input = ({
|
const Input = ({
|
||||||
name,
|
name,
|
||||||
error,
|
error,
|
||||||
onChange,
|
onChange,
|
||||||
required = true,
|
required = true,
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
autoComplete,
|
autoComplete,
|
||||||
fullWidth = true,
|
fullWidth = true,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
maxLength = 254,
|
maxLength = 254,
|
||||||
}: InputProps): React.ReactElement => {
|
}: InputProps): React.ReactElement => {
|
||||||
const fieldError = error?.fields?.[name];
|
const fieldError = error?.fields?.[name];
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
name={name}
|
name={name}
|
||||||
type={type}
|
type={type}
|
||||||
label={label}
|
label={label}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
error={Boolean(fieldError)}
|
error={Boolean(fieldError)}
|
||||||
helperText={fieldError}
|
helperText={fieldError}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
required={required}
|
required={required}
|
||||||
fullWidth={fullWidth}
|
fullWidth={fullWidth}
|
||||||
margin="dense"
|
margin="dense"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
autoComplete={autoComplete}
|
autoComplete={autoComplete}
|
||||||
inputProps={{ maxLength: maxLength }}
|
inputProps={{ maxLength: maxLength }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default Input;
|
export default Input;
|
||||||
|
@ -3,37 +3,37 @@ import React, { useState } from 'react';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
type SubmitButton = {
|
type SubmitButton = {
|
||||||
value: string;
|
value: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
const SubmitButton = (props: SubmitButton): React.ReactElement => {
|
const SubmitButton = (props: SubmitButton): React.ReactElement => {
|
||||||
const [disabled] = useState(props.disabled ? true : false);
|
const [disabled] = useState(props.disabled ? true : false);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
let valueTxt = props.value;
|
let valueTxt = props.value;
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
valueTxt = intl.formatMessage({ id: 'common.wait', defaultMessage: 'Please wait ...' });
|
valueTxt = intl.formatMessage({ id: 'common.wait', defaultMessage: 'Please wait ...' });
|
||||||
}
|
}
|
||||||
const [value] = useState(valueTxt);
|
const [value] = useState(valueTxt);
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
type="submit"
|
type="submit"
|
||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
style={{
|
style={{
|
||||||
width: '350px',
|
width: '350px',
|
||||||
height: '53px',
|
height: '53px',
|
||||||
padding: '0px 20px',
|
padding: '0px 20px',
|
||||||
margin: '7px 0px',
|
margin: '7px 0px',
|
||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SubmitButton;
|
export default SubmitButton;
|
||||||
|
@ -5,79 +5,70 @@ import poweredByIcon from './pwrdby-white.svg';
|
|||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const Footer = (): React.ReactElement => {
|
const Footer = (): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<StyledFooter>
|
<StyledFooter>
|
||||||
<div style={{ padding: 0, margin: 0 }}>
|
<div style={{ padding: 0, margin: 0 }}>
|
||||||
<a href="http://www.wisemapping.org/">
|
<a href="http://www.wisemapping.org/">
|
||||||
<img src={poweredByIcon} alt="Powered By WiseMapping" />
|
<img src={poweredByIcon} alt="Powered By WiseMapping" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>
|
<h4>
|
||||||
<FormattedMessage id="footer.faqandhelp" defaultMessage="Help & FAQ" />
|
<FormattedMessage id="footer.faqandhelp" defaultMessage="Help & FAQ" />
|
||||||
</h4>
|
</h4>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://www.wisemapping.com/aboutus.html">
|
<a href="https://www.wisemapping.com/aboutus.html">
|
||||||
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://www.wisemapping.com/faq.html">
|
<a href="https://www.wisemapping.com/faq.html">
|
||||||
<FormattedMessage id="footer.faq" defaultMessage="F.A.Q." />
|
<FormattedMessage id="footer.faq" defaultMessage="F.A.Q." />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://www.wisemapping.com/termsofuse.html">
|
<a href="https://www.wisemapping.com/termsofuse.html">
|
||||||
<FormattedMessage
|
<FormattedMessage id="footer.termsandconditions" defaultMessage="Term And Conditions" />
|
||||||
id="footer.termsandconditions"
|
</a>
|
||||||
defaultMessage="Term And Conditions"
|
</div>
|
||||||
/>
|
</div>
|
||||||
</a>
|
<div>
|
||||||
</div>
|
<h4>
|
||||||
</div>
|
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
||||||
<div>
|
</h4>
|
||||||
<h4>
|
<div>
|
||||||
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
<a href="mailto:support@wisemapping.com">
|
||||||
</h4>
|
<FormattedMessage id="footer.support" defaultMessage="Support" />
|
||||||
<div>
|
</a>
|
||||||
<a href="mailto:support@wisemapping.com">
|
</div>
|
||||||
<FormattedMessage id="footer.support" defaultMessage="Support" />
|
<div>
|
||||||
</a>
|
<a href="mailto:feedback@wisemapping.com">
|
||||||
</div>
|
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
||||||
<div>
|
</a>
|
||||||
<a href="mailto:feedback@wisemapping.com">
|
</div>
|
||||||
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
<div>
|
||||||
</a>
|
<a href="mailto:team@wisemapping.com">
|
||||||
</div>
|
<FormattedMessage id="footer.team" defaultMessage="Our Team" />
|
||||||
<div>
|
</a>
|
||||||
<a href="mailto:team@wisemapping.com">
|
</div>
|
||||||
<FormattedMessage id="footer.team" defaultMessage="Our Team" />
|
</div>
|
||||||
</a>
|
<div>
|
||||||
</div>
|
<h4>
|
||||||
</div>
|
<FormattedMessage id="footer.others" defaultMessage="Others" />
|
||||||
<div>
|
</h4>
|
||||||
<h4>
|
<div>
|
||||||
<FormattedMessage id="footer.others" defaultMessage="Others" />
|
<a href="https://www.paypal.com/donate/?hosted_button_id=CF7GJ7T6E4RS4">
|
||||||
</h4>
|
<FormattedMessage id="footer.donations" defaultMessage="Donations" />
|
||||||
<div>
|
</a>
|
||||||
<a href="https://www.paypal.com/donate/?hosted_button_id=CF7GJ7T6E4RS4">
|
</div>
|
||||||
<FormattedMessage
|
<div>
|
||||||
id="footer.donations"
|
<a href="http://www.wisemapping.org/">
|
||||||
defaultMessage="Donations"
|
<FormattedMessage id="footer.opensource" defaultMessage="Open Source" />
|
||||||
/>
|
</a>
|
||||||
</a>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</StyledFooter>
|
||||||
<a href="http://www.wisemapping.org/">
|
);
|
||||||
<FormattedMessage
|
|
||||||
id="footer.opensource"
|
|
||||||
defaultMessage="Open Source"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</StyledFooter>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
@ -2,11 +2,11 @@ import Container from '@mui/material/Container';
|
|||||||
import withStyles from '@mui/styles/withStyles';
|
import withStyles from '@mui/styles/withStyles';
|
||||||
|
|
||||||
const FormContainer = withStyles({
|
const FormContainer = withStyles({
|
||||||
root: {
|
root: {
|
||||||
padding: '20px 10px 0px 20px',
|
padding: '20px 10px 0px 20px',
|
||||||
maxWidth: '380px',
|
maxWidth: '380px',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
})(Container);
|
})(Container);
|
||||||
|
|
||||||
export default FormContainer;
|
export default FormContainer;
|
||||||
|
@ -8,89 +8,83 @@ import Button from '@mui/material/Button';
|
|||||||
import logo from './logo-small.svg';
|
import logo from './logo-small.svg';
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
type: 'only-signup' | 'only-signin' | 'none';
|
type: 'only-signup' | 'only-signin' | 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Header = ({ type }: HeaderProps): React.ReactElement => {
|
export const Header = ({ type }: HeaderProps): React.ReactElement => {
|
||||||
let signUpButton;
|
let signUpButton;
|
||||||
let text;
|
let text;
|
||||||
let signInButton;
|
let signInButton;
|
||||||
if (type === 'only-signup') {
|
if (type === 'only-signup') {
|
||||||
text = (
|
text = (
|
||||||
<span className="header-area-content-span">
|
<span className="header-area-content-span">
|
||||||
<span>
|
<span>
|
||||||
<FormattedMessage
|
<FormattedMessage id="header.donthaveaccount" defaultMessage="Don't have an account ?" />
|
||||||
id="header.donthaveaccount"
|
</span>
|
||||||
defaultMessage="Don't have an account ?"
|
</span>
|
||||||
/>
|
|
||||||
</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>
|
|
||||||
);
|
);
|
||||||
|
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 {
|
interface ButtonProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SignInButton = (props: ButtonProps): React.ReactElement => {
|
export const SignInButton = (props: ButtonProps): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<span className={`${props.className}`}>
|
<span className={`${props.className}`}>
|
||||||
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/login">
|
<Button color="primary" size="medium" variant="outlined" component={Link} to="/c/login">
|
||||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SignUpButton = (props: ButtonProps): React.ReactElement => {
|
const SignUpButton = (props: ButtonProps): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<span className={`${props.className}`}>
|
<span className={`${props.className}`}>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
component={Link}
|
component={Link}
|
||||||
to="/c/registration"
|
to="/c/registration"
|
||||||
>
|
>
|
||||||
<FormattedMessage id="login.signup" defaultMessage="Sign Up" />
|
<FormattedMessage id="login.signup" defaultMessage="Sign Up" />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Header;
|
export default Header;
|
||||||
|
@ -14,130 +14,130 @@ import ReactGA from 'react-ga4';
|
|||||||
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
|
||||||
|
|
||||||
type ConfigStatusProps = {
|
type ConfigStatusProps = {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.ReactElement => {
|
const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.ReactElement => {
|
||||||
let result;
|
let result;
|
||||||
if (enabled === true) {
|
if (enabled === true) {
|
||||||
result = (
|
result = (
|
||||||
<div className="db-warn-msg">
|
<div className="db-warn-msg">
|
||||||
<p>
|
<p>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="login.hsqldbcofig"
|
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"
|
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"
|
description="Missing production database configured"
|
||||||
/>
|
/>
|
||||||
<a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration">
|
<a href="https://wisemapping.atlassian.net/wiki/display/WS/Database+Configuration">
|
||||||
{' '}
|
{' '}
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result || null;
|
return result || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoginError = () => {
|
const LoginError = () => {
|
||||||
// @Todo: This must be reviewed to be based on navigation state.
|
// @Todo: This must be reviewed to be based on navigation state.
|
||||||
// Login error example: http://localhost:8080/c/login?login.error=2
|
// Login error example: http://localhost:8080/c/login?login.error=2
|
||||||
const errorCode = new URLSearchParams(window.location.search).get('login_error');
|
const errorCode = new URLSearchParams(window.location.search).get('login_error');
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
let msg: null | string = null;
|
let msg: null | string = null;
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
case '3':
|
case '3':
|
||||||
msg = intl.formatMessage({
|
msg = intl.formatMessage({
|
||||||
id: 'login.userinactive',
|
id: 'login.userinactive',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
"Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!.",
|
"Sorry, your account has not been activated yet. You'll receive a notification email when it becomes active. Stay tuned!.",
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
msg = intl.formatMessage({
|
msg = intl.formatMessage({
|
||||||
id: 'login.error',
|
id: 'login.error',
|
||||||
defaultMessage: 'The email address or password you entered is not valid.',
|
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 LoginPage = (): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = intl.formatMessage({ id: 'login.page-title', defaultMessage: 'Login | WiseMapping' });
|
document.title = intl.formatMessage({
|
||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Login' });
|
id: 'login.page-title',
|
||||||
}, []);
|
defaultMessage: 'Login | WiseMapping',
|
||||||
|
});
|
||||||
|
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Login' });
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type="only-signup" />
|
<Header type="only-signup" />
|
||||||
|
|
||||||
<FormContainer maxWidth="xs">
|
<FormContainer maxWidth="xs">
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage id="login.title" defaultMessage="Welcome" />
|
<FormattedMessage id="login.title" defaultMessage="Welcome" />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage id="login.desc" defaultMessage="Log into your account" />
|
<FormattedMessage id="login.desc" defaultMessage="Log into your account" />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<LoginError />
|
<LoginError />
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<form action="/c/perform-login" method="POST">
|
<form action="/c/perform-login" method="POST">
|
||||||
<input type='hidden' value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
<input type="hidden" value={getCsrfToken()} name={getCsrfTokenParameter()} />
|
||||||
<Input
|
<Input
|
||||||
name="username"
|
name="username"
|
||||||
type="email"
|
type="email"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'login.email',
|
id: 'login.email',
|
||||||
defaultMessage: 'Email',
|
defaultMessage: 'Email',
|
||||||
})}
|
})}
|
||||||
required
|
required
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'login.password',
|
id: 'login.password',
|
||||||
defaultMessage: 'Password',
|
defaultMessage: 'Password',
|
||||||
})}
|
})}
|
||||||
required
|
required
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<input name="remember-me" id="remember-me" type="checkbox" />
|
<input name="remember-me" id="remember-me" type="checkbox" />
|
||||||
<label htmlFor="remember-me">
|
<label htmlFor="remember-me">
|
||||||
<FormattedMessage
|
<FormattedMessage id="login.remberme" defaultMessage="Remember me" />
|
||||||
id="login.remberme"
|
</label>
|
||||||
defaultMessage="Remember me"
|
</div>
|
||||||
/>
|
<SubmitButton
|
||||||
</label>
|
value={intl.formatMessage({
|
||||||
</div>
|
id: 'login.signin',
|
||||||
<SubmitButton
|
defaultMessage: 'Sign In',
|
||||||
value={intl.formatMessage({
|
})}
|
||||||
id: 'login.signin',
|
/>
|
||||||
defaultMessage: 'Sign In',
|
</form>
|
||||||
})}
|
</FormControl>
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<Link component={RouterLink} to="/c/forgot-password">
|
<Link component={RouterLink} to="/c/forgot-password">
|
||||||
<FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" />
|
<FormattedMessage id="login.forgotpwd" defaultMessage="Forgot Password ?" />
|
||||||
</Link>
|
</Link>
|
||||||
<ConfigStatusMessage />
|
<ConfigStatusMessage />
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
@ -14,165 +14,168 @@ import FormGroup from '@mui/material/FormGroup';
|
|||||||
import Switch from '@mui/material/Switch';
|
import Switch from '@mui/material/Switch';
|
||||||
|
|
||||||
type AccountInfoDialogProps = {
|
type AccountInfoDialogProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AccountInfoModel = {
|
type AccountInfoModel = {
|
||||||
email: string;
|
email: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: AccountInfoModel = { firstname: '', lastname: '', email: '' };
|
const defaultModel: AccountInfoModel = { firstname: '', lastname: '', email: '' };
|
||||||
const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps): React.ReactElement => {
|
const AccountInfoDialog = ({ onClose }: AccountInfoDialogProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [remove, setRemove] = React.useState<boolean>(false);
|
const [remove, setRemove] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const [model, setModel] = React.useState<AccountInfoModel>(defaultModel);
|
const [model, setModel] = React.useState<AccountInfoModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const mutationChangeName = useMutation<void, ErrorInfo, AccountInfoModel>(
|
const mutationChangeName = useMutation<void, ErrorInfo, AccountInfoModel>(
|
||||||
(model: AccountInfoModel) => {
|
(model: AccountInfoModel) => {
|
||||||
return client.updateAccountInfo(model.firstname, model.lastname);
|
return client.updateAccountInfo(model.firstname, model.lastname);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries('account');
|
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 => {
|
|
||||||
onClose();
|
onClose();
|
||||||
setModel(defaultModel);
|
},
|
||||||
setError(undefined);
|
onError: (error) => {
|
||||||
};
|
setError(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const mutationRemove = useMutation<void, ErrorInfo, void>(
|
||||||
event.preventDefault();
|
() => {
|
||||||
if (remove) {
|
return client.deleteAccount();
|
||||||
mutationRemove.mutate();
|
},
|
||||||
} else {
|
{
|
||||||
mutationChangeName.mutate(model);
|
onSuccess: () => {
|
||||||
}
|
window.location.href = '/c/login';
|
||||||
};
|
onClose();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setError(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const account = fetchAccount();
|
||||||
event.preventDefault();
|
useEffect(() => {
|
||||||
|
if (account) {
|
||||||
|
setModel({
|
||||||
|
email: account?.email,
|
||||||
|
lastname: account?.lastname,
|
||||||
|
firstname: account?.firstname,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [account?.email]);
|
||||||
|
|
||||||
const name = event.target.name;
|
const handleOnClose = (): void => {
|
||||||
const value = event.target.value;
|
onClose();
|
||||||
setModel({ ...model, [name as keyof AccountInfoModel]: value });
|
setModel(defaultModel);
|
||||||
};
|
setError(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnRemoveChange = (event) => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
setRemove(event.target.checked);
|
event.preventDefault();
|
||||||
};
|
if (remove) {
|
||||||
|
mutationRemove.mutate();
|
||||||
|
} else {
|
||||||
|
mutationChangeName.mutate(model);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
<BaseDialog
|
event.preventDefault();
|
||||||
onClose={handleOnClose}
|
|
||||||
onSubmit={handleOnSubmit}
|
const name = event.target.name;
|
||||||
error={error}
|
const value = event.target.value;
|
||||||
title={intl.formatMessage({ id: 'accountinfo.title', defaultMessage: 'Account info' })}
|
setModel({ ...model, [name as keyof AccountInfoModel]: value });
|
||||||
submitButton={intl.formatMessage({
|
};
|
||||||
id: 'accountinfo.button',
|
|
||||||
defaultMessage: 'Accept',
|
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}>
|
</FormGroup>
|
||||||
<Input
|
</FormControl>
|
||||||
name="email"
|
</BaseDialog>
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
export default AccountInfoDialog;
|
export default AccountInfoDialog;
|
||||||
|
@ -9,108 +9,108 @@ import { useSelector } from 'react-redux';
|
|||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice';
|
||||||
|
|
||||||
type ChangePasswordDialogProps = {
|
type ChangePasswordDialogProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChangePasswordModel = {
|
type ChangePasswordModel = {
|
||||||
password: string;
|
password: string;
|
||||||
retryPassword: string;
|
retryPassword: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: ChangePasswordModel = { password: '', retryPassword: '' };
|
const defaultModel: ChangePasswordModel = { password: '', retryPassword: '' };
|
||||||
const ChangePasswordDialog = ({ onClose }: ChangePasswordDialogProps): React.ReactElement => {
|
const ChangePasswordDialog = ({ onClose }: ChangePasswordDialogProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<ChangePasswordModel>(defaultModel);
|
const [model, setModel] = React.useState<ChangePasswordModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const mutation = useMutation<void, ErrorInfo, ChangePasswordModel>(
|
const mutation = useMutation<void, ErrorInfo, ChangePasswordModel>(
|
||||||
(model: ChangePasswordModel) => {
|
(model: ChangePasswordModel) => {
|
||||||
return client.updateAccountPassword(model.password);
|
return client.updateAccountPassword(model.password);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
onClose();
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
setError(error);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
|
||||||
onClose();
|
onClose();
|
||||||
setModel(defaultModel);
|
},
|
||||||
setError(undefined);
|
onError: (error) => {
|
||||||
};
|
setError(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnClose = (): void => {
|
||||||
event.preventDefault();
|
onClose();
|
||||||
|
setModel(defaultModel);
|
||||||
|
setError(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
// Check password are equal ...
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
if (model.password != model.retryPassword) {
|
event.preventDefault();
|
||||||
setError({
|
|
||||||
msg: intl.formatMessage({
|
|
||||||
id: 'changepwd.password-match',
|
|
||||||
defaultMessage: 'Password do not match. Please, try again.',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 => {
|
mutation.mutate(model);
|
||||||
event.preventDefault();
|
};
|
||||||
|
|
||||||
const name = event.target.name;
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
const value = event.target.value;
|
event.preventDefault();
|
||||||
setModel({ ...model, [name as keyof ChangePasswordModel]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const name = event.target.name;
|
||||||
<BaseDialog
|
const value = event.target.value;
|
||||||
onClose={handleOnClose}
|
setModel({ ...model, [name as keyof ChangePasswordModel]: value });
|
||||||
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
|
return (
|
||||||
name="retryPassword"
|
<BaseDialog
|
||||||
type="password"
|
onClose={handleOnClose}
|
||||||
label={intl.formatMessage({
|
onSubmit={handleOnSubmit}
|
||||||
id: 'changepwd.confirm-password',
|
error={error}
|
||||||
defaultMessage: 'Confirm Password',
|
title={intl.formatMessage({ id: 'changepwd.title', defaultMessage: 'Change Password' })}
|
||||||
})}
|
description={intl.formatMessage({
|
||||||
value={model.retryPassword}
|
id: 'changepwd.description',
|
||||||
onChange={handleOnChange}
|
defaultMessage: 'Please, provide the new password for your account.',
|
||||||
required={true}
|
})}
|
||||||
fullWidth={true}
|
submitButton={intl.formatMessage({ id: 'changepwd.button', defaultMessage: 'Change' })}
|
||||||
autoComplete="new-password"
|
>
|
||||||
/>
|
<FormControl fullWidth={true}>
|
||||||
</FormControl>
|
<Input
|
||||||
</BaseDialog>
|
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;
|
export default ChangePasswordDialog;
|
||||||
|
@ -16,90 +16,86 @@ import ExitToAppOutlined from '@mui/icons-material/ExitToAppOutlined';
|
|||||||
|
|
||||||
type ActionType = 'change-password' | 'account-info' | undefined;
|
type ActionType = 'change-password' | 'account-info' | undefined;
|
||||||
const AccountMenu = (): React.ReactElement => {
|
const AccountMenu = (): React.ReactElement => {
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const [action, setAction] = React.useState<ActionType>(undefined);
|
const [action, setAction] = React.useState<ActionType>(undefined);
|
||||||
|
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLogout = (event: MouseEvent) => {
|
const handleLogout = (event: MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const elem = document.getElementById('logoutFrom') as HTMLFormElement;
|
const elem = document.getElementById('logoutFrom') as HTMLFormElement;
|
||||||
elem.submit();
|
elem.submit();
|
||||||
};
|
};
|
||||||
|
|
||||||
const account = fetchAccount();
|
const account = fetchAccount();
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow={true}
|
arrow={true}
|
||||||
title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}
|
title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}
|
||||||
>
|
>
|
||||||
<IconButton onClick={handleMenu} size="large">
|
<IconButton onClick={handleMenu} size="large">
|
||||||
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu
|
<Menu
|
||||||
id="appbar-profile"
|
id="appbar-profile"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'bottom',
|
vertical: 'bottom',
|
||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleClose(), setAction('account-info');
|
handleClose(), setAction('account-info');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<SettingsApplicationsOutlined fontSize="small" />
|
<SettingsApplicationsOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="menu.account" defaultMessage="Account" />
|
<FormattedMessage id="menu.account" defaultMessage="Account" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleClose(), setAction('change-password');
|
handleClose(), setAction('change-password');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<LockOpenOutlined fontSize="small" />
|
<LockOpenOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="menu.change-password" defaultMessage="Change password" />
|
<FormattedMessage id="menu.change-password" defaultMessage="Change password" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<form action="/c/logout" method='POST' id="logoutFrom"></form>
|
<form action="/c/logout" method="POST" id="logoutFrom"></form>
|
||||||
<Link color="textSecondary" href="/c/logout" onClick={(e) => handleLogout(e)}>
|
<Link color="textSecondary" href="/c/logout" onClick={(e) => handleLogout(e)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<ExitToAppOutlined fontSize="small" />
|
<ExitToAppOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="menu.signout" defaultMessage="Sign Out" />
|
<FormattedMessage id="menu.signout" defaultMessage="Sign Out" />
|
||||||
</Link>
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
{
|
{action == 'change-password' && <ChangePasswordDialog onClose={() => setAction(undefined)} />}
|
||||||
action == 'change-password' && (
|
{action == 'account-info' && <AccountInfoDialog onClose={() => setAction(undefined)} />}
|
||||||
<ChangePasswordDialog onClose={() => setAction(undefined)} />
|
</span>
|
||||||
)
|
);
|
||||||
}
|
|
||||||
{action == 'account-info' && <AccountInfoDialog onClose={() => setAction(undefined)} />}
|
|
||||||
</span >
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AccountMenu;
|
export default AccountMenu;
|
||||||
|
@ -18,139 +18,139 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
export type ActionType =
|
export type ActionType =
|
||||||
| 'open'
|
| 'open'
|
||||||
| 'share'
|
| 'share'
|
||||||
| 'import'
|
| 'import'
|
||||||
| 'delete'
|
| 'delete'
|
||||||
| 'info'
|
| 'info'
|
||||||
| 'create'
|
| 'create'
|
||||||
| 'duplicate'
|
| 'duplicate'
|
||||||
| 'export'
|
| 'export'
|
||||||
| 'label'
|
| 'label'
|
||||||
| 'rename'
|
| 'rename'
|
||||||
| 'print'
|
| 'print'
|
||||||
| 'info'
|
| 'info'
|
||||||
| 'publish'
|
| 'publish'
|
||||||
| 'history'
|
| 'history'
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
interface ActionProps {
|
interface ActionProps {
|
||||||
onClose: (action: ActionType) => void;
|
onClose: (action: ActionType) => void;
|
||||||
anchor?: HTMLElement;
|
anchor?: HTMLElement;
|
||||||
mapId?: number;
|
mapId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActionChooser = (props: ActionProps): React.ReactElement => {
|
const ActionChooser = (props: ActionProps): React.ReactElement => {
|
||||||
const { anchor, onClose, mapId } = props;
|
const { anchor, onClose, mapId } = props;
|
||||||
|
|
||||||
const handleOnClose = (
|
const handleOnClose = (
|
||||||
action: ActionType
|
action: ActionType,
|
||||||
): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
): ((event: React.MouseEvent<HTMLLIElement>) => void) => {
|
||||||
return (event): void => {
|
return (event): void => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
onClose(action);
|
onClose(action);
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined;
|
const role = mapId ? fetchMapById(mapId)?.map?.role : undefined;
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={anchor}
|
anchorEl={anchor}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={Boolean(anchor)}
|
open={Boolean(anchor)}
|
||||||
onClose={handleOnClose(undefined)}
|
onClose={handleOnClose(undefined)}
|
||||||
elevation={1}
|
elevation={1}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={handleOnClose('open')} style={{ width: '220px' }}>
|
<MenuItem onClick={handleOnClose('open')} style={{ width: '220px' }}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DescriptionOutlinedIcon />
|
<DescriptionOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.open" defaultMessage="Open" />
|
<FormattedMessage id="action.open" defaultMessage="Open" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('duplicate')}>
|
<MenuItem onClick={handleOnClose('duplicate')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<FileCopyOutlinedIcon />
|
<FileCopyOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
<FormattedMessage id="action.duplicate" defaultMessage="Duplicate" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{role == 'owner' && (
|
{role == 'owner' && (
|
||||||
<MenuItem onClick={handleOnClose('rename')}>
|
<MenuItem onClick={handleOnClose('rename')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<EditOutlinedIcon />
|
<EditOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.rename" defaultMessage="Rename" />
|
<FormattedMessage id="action.rename" defaultMessage="Rename" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('label')}>
|
<MenuItem onClick={handleOnClose('label')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<LabelOutlined />
|
<LabelOutlined />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.label" defaultMessage="Add Label" />
|
<FormattedMessage id="action.label" defaultMessage="Add Label" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('delete')}>
|
<MenuItem onClick={handleOnClose('delete')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DeleteOutlinedIcon />
|
<DeleteOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.delete" defaultMessage="Delete" />
|
<FormattedMessage id="action.delete" defaultMessage="Delete" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('export')}>
|
<MenuItem onClick={handleOnClose('export')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<CloudDownloadOutlinedIcon />
|
<CloudDownloadOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.export" defaultMessage="Export" />
|
<FormattedMessage id="action.export" defaultMessage="Export" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('print')}>
|
<MenuItem onClick={handleOnClose('print')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<PrintOutlinedIcon />
|
<PrintOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.print" defaultMessage="Print" />
|
<FormattedMessage id="action.print" defaultMessage="Print" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{role == 'owner' && (
|
{role == 'owner' && (
|
||||||
<MenuItem onClick={handleOnClose('publish')}>
|
<MenuItem onClick={handleOnClose('publish')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<PublicOutlinedIcon />
|
<PublicOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.publish" defaultMessage="Publish" />
|
<FormattedMessage id="action.publish" defaultMessage="Publish" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{role == 'owner' && (
|
{role == 'owner' && (
|
||||||
<MenuItem onClick={handleOnClose('share')}>
|
<MenuItem onClick={handleOnClose('share')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<ShareOutlinedIcon />
|
<ShareOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.share" defaultMessage="Share" />
|
<FormattedMessage id="action.share" defaultMessage="Share" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<MenuItem onClick={handleOnClose('info')}>
|
<MenuItem onClick={handleOnClose('info')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<InfoOutlinedIcon />
|
<InfoOutlinedIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.info" defaultMessage="Info" />
|
<FormattedMessage id="action.info" defaultMessage="Info" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{role != 'viewer' && (
|
{role != 'viewer' && (
|
||||||
<MenuItem onClick={handleOnClose('history')}>
|
<MenuItem onClick={handleOnClose('history')}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<HistoryOutlined/>
|
<HistoryOutlined />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="action.history" defaultMessage="History" />
|
<FormattedMessage id="action.history" defaultMessage="History" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ActionChooser;
|
export default ActionChooser;
|
||||||
|
@ -8,89 +8,89 @@ import { StyledButton, NewLabelContainer, NewLabelColor, CreateLabel } from './s
|
|||||||
import { Tooltip } from '@mui/material';
|
import { Tooltip } from '@mui/material';
|
||||||
|
|
||||||
const labelColors = [
|
const labelColors = [
|
||||||
'#00b327',
|
'#00b327',
|
||||||
'#0565ff',
|
'#0565ff',
|
||||||
'#2d2dd6',
|
'#2d2dd6',
|
||||||
'#6a00ba',
|
'#6a00ba',
|
||||||
'#ad1599',
|
'#ad1599',
|
||||||
'#ff1e35',
|
'#ff1e35',
|
||||||
'#ff6600',
|
'#ff6600',
|
||||||
'#ffff47',
|
'#ffff47',
|
||||||
];
|
];
|
||||||
|
|
||||||
type AddLabelFormProps = {
|
type AddLabelFormProps = {
|
||||||
onAdd: (newLabel: Label) => void;
|
onAdd: (newLabel: Label) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddLabelDialog = ({ onAdd }: AddLabelFormProps): React.ReactElement => {
|
const AddLabelDialog = ({ onAdd }: AddLabelFormProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [createLabelColorIndex, setCreateLabelColorIndex] = React.useState(
|
const [createLabelColorIndex, setCreateLabelColorIndex] = React.useState(
|
||||||
Math.floor(Math.random() * labelColors.length)
|
Math.floor(Math.random() * labelColors.length),
|
||||||
);
|
);
|
||||||
const [newLabelTitle, setNewLabelTitle] = React.useState('');
|
const [newLabelTitle, setNewLabelTitle] = React.useState('');
|
||||||
|
|
||||||
const newLabelColor = labelColors[createLabelColorIndex];
|
const newLabelColor = labelColors[createLabelColorIndex];
|
||||||
|
|
||||||
const setNextLabelColorIndex = () => {
|
const setNextLabelColorIndex = () => {
|
||||||
const nextIndex = labelColors[createLabelColorIndex + 1] ? createLabelColorIndex + 1 : 0;
|
const nextIndex = labelColors[createLabelColorIndex + 1] ? createLabelColorIndex + 1 : 0;
|
||||||
setCreateLabelColorIndex(nextIndex);
|
setCreateLabelColorIndex(nextIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitNew = () => {
|
const handleSubmitNew = () => {
|
||||||
onAdd({
|
onAdd({
|
||||||
title: newLabelTitle,
|
title: newLabelTitle,
|
||||||
color: newLabelColor,
|
color: newLabelColor,
|
||||||
id: 0,
|
id: 0,
|
||||||
});
|
});
|
||||||
setNewLabelTitle('');
|
setNewLabelTitle('');
|
||||||
setNextLabelColorIndex();
|
setNextLabelColorIndex();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CreateLabel>
|
<CreateLabel>
|
||||||
<NewLabelContainer>
|
<NewLabelContainer>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow={true}
|
arrow={true}
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id: 'label.change-color',
|
id: 'label.change-color',
|
||||||
defaultMessage: 'Change label color',
|
defaultMessage: 'Change label color',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<NewLabelColor
|
<NewLabelColor
|
||||||
htmlColor={newLabelColor}
|
htmlColor={newLabelColor}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setNextLabelColorIndex();
|
setNextLabelColorIndex();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<TextField
|
<TextField
|
||||||
variant="standard"
|
variant="standard"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'label.add-placeholder',
|
id: 'label.add-placeholder',
|
||||||
defaultMessage: 'Label title',
|
defaultMessage: 'Label title',
|
||||||
})}
|
})}
|
||||||
onChange={(e) => setNewLabelTitle(e.target.value)}
|
onChange={(e) => setNewLabelTitle(e.target.value)}
|
||||||
onKeyPress={(e) => {
|
onKeyPress={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
handleSubmitNew();
|
handleSubmitNew();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
value={newLabelTitle}
|
value={newLabelTitle}
|
||||||
/>
|
/>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
onClick={() => handleSubmitNew()}
|
onClick={() => handleSubmitNew()}
|
||||||
disabled={!newLabelTitle.length}
|
disabled={!newLabelTitle.length}
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
id: 'label.add-button',
|
id: 'label.add-button',
|
||||||
defaultMessage: 'Add label',
|
defaultMessage: 'Add label',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</NewLabelContainer>
|
</NewLabelContainer>
|
||||||
</CreateLabel>
|
</CreateLabel>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AddLabelDialog;
|
export default AddLabelDialog;
|
||||||
|
@ -10,85 +10,82 @@ import { useDispatch } from 'react-redux';
|
|||||||
import { disableHotkeys, enableHotkeys } from '../../../../redux/editorSlice';
|
import { disableHotkeys, enableHotkeys } from '../../../../redux/editorSlice';
|
||||||
|
|
||||||
export type DialogProps = {
|
export type DialogProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
|
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
|
||||||
children: unknown;
|
children: unknown;
|
||||||
error?: ErrorInfo;
|
error?: ErrorInfo;
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
submitButton?: string;
|
submitButton?: string;
|
||||||
actionUrl?: string;
|
actionUrl?: string;
|
||||||
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
||||||
PaperProps?: Partial<PaperProps>;
|
PaperProps?: Partial<PaperProps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(disableHotkeys());
|
dispatch(disableHotkeys());
|
||||||
return () => {
|
return () => {
|
||||||
dispatch(enableHotkeys())
|
dispatch(enableHotkeys());
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props;
|
|
||||||
|
|
||||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (onSubmit) {
|
|
||||||
onSubmit(e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
}, []);
|
||||||
|
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props;
|
||||||
|
|
||||||
const description = props.description ? (
|
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
<DialogContentText>{props.description}</DialogContentText>
|
e.preventDefault();
|
||||||
) : null;
|
if (onSubmit) {
|
||||||
return (
|
onSubmit(e);
|
||||||
<div>
|
}
|
||||||
<StyledDialog
|
};
|
||||||
open={true}
|
|
||||||
onClose={onClose}
|
|
||||||
maxWidth={maxWidth}
|
|
||||||
PaperProps={PaperProps}
|
|
||||||
fullWidth={true}
|
|
||||||
>
|
|
||||||
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
|
||||||
<StyledDialogTitle>{props.title}</StyledDialogTitle>
|
|
||||||
|
|
||||||
<StyledDialogContent>
|
const description = props.description ? (
|
||||||
{description}
|
<DialogContentText>{props.description}</DialogContentText>
|
||||||
<GlobalError error={props.error} />
|
) : null;
|
||||||
{props.children}
|
return (
|
||||||
</StyledDialogContent>
|
<div>
|
||||||
|
<StyledDialog
|
||||||
|
open={true}
|
||||||
|
onClose={onClose}
|
||||||
|
maxWidth={maxWidth}
|
||||||
|
PaperProps={PaperProps}
|
||||||
|
fullWidth={true}
|
||||||
|
>
|
||||||
|
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
||||||
|
<StyledDialogTitle>{props.title}</StyledDialogTitle>
|
||||||
|
|
||||||
<StyledDialogActions>
|
<StyledDialogContent>
|
||||||
<Button type="button" color="primary" size="medium" onClick={onClose}>
|
{description}
|
||||||
{onSubmit ? (
|
<GlobalError error={props.error} />
|
||||||
<FormattedMessage
|
{props.children}
|
||||||
id="action.cancel-button"
|
</StyledDialogContent>
|
||||||
defaultMessage="Cancel"
|
|
||||||
/>
|
<StyledDialogActions>
|
||||||
) : (
|
<Button type="button" color="primary" size="medium" onClick={onClose}>
|
||||||
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
{onSubmit ? (
|
||||||
)}
|
<FormattedMessage id="action.cancel-button" defaultMessage="Cancel" />
|
||||||
</Button>
|
) : (
|
||||||
{onSubmit && (
|
<FormattedMessage id="action.close-button" defaultMessage="Close" />
|
||||||
<Button
|
)}
|
||||||
color="primary"
|
</Button>
|
||||||
size="medium"
|
{onSubmit && (
|
||||||
variant="contained"
|
<Button
|
||||||
type="submit"
|
color="primary"
|
||||||
disableElevation={true}
|
size="medium"
|
||||||
>
|
variant="contained"
|
||||||
{props.submitButton}
|
type="submit"
|
||||||
</Button>
|
disableElevation={true}
|
||||||
)}
|
>
|
||||||
</StyledDialogActions>
|
{props.submitButton}
|
||||||
</form>
|
</Button>
|
||||||
</StyledDialog>
|
)}
|
||||||
</div>
|
</StyledDialogActions>
|
||||||
);
|
</form>
|
||||||
|
</StyledDialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BaseDialog;
|
export default BaseDialog;
|
||||||
|
@ -10,102 +10,102 @@ import Input from '../../../form/input';
|
|||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog';
|
||||||
|
|
||||||
export type CreateModel = {
|
export type CreateModel = {
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateProps = {
|
export type CreateProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: CreateModel = { title: '', description: '' };
|
const defaultModel: CreateModel = { title: '', description: '' };
|
||||||
const CreateDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
const CreateDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<CreateModel>(defaultModel);
|
const [model, setModel] = React.useState<CreateModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const mutation = useMutation<number, ErrorInfo, CreateModel>(
|
const mutation = useMutation<number, ErrorInfo, CreateModel>(
|
||||||
(model: CreateModel) => {
|
(model: CreateModel) => {
|
||||||
return client.createMap(model);
|
return client.createMap(model);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (mapId: number) => {
|
onSuccess: (mapId: number) => {
|
||||||
window.location.href = `/c/maps/${mapId}/edit`;
|
window.location.href = `/c/maps/${mapId}/edit`;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
setModel(defaultModel);
|
setModel(defaultModel);
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
mutation.mutate(model);
|
mutation.mutate(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name;
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
error={error}
|
error={error}
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
id: 'create.title',
|
id: 'create.title',
|
||||||
defaultMessage: 'Create a new mindmap',
|
defaultMessage: 'Create a new mindmap',
|
||||||
})}
|
})}
|
||||||
description={intl.formatMessage({
|
description={intl.formatMessage({
|
||||||
id: 'create.description',
|
id: 'create.description',
|
||||||
defaultMessage: 'Please, fill the new map name and description.',
|
defaultMessage: 'Please, fill the new map name and description.',
|
||||||
})}
|
})}
|
||||||
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}
|
submitButton={intl.formatMessage({ id: 'create.button', defaultMessage: 'Create' })}
|
||||||
>
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input
|
<Input
|
||||||
name="title"
|
name="title"
|
||||||
type="text"
|
type="text"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'action.rename-name-placeholder',
|
id: 'action.rename-name-placeholder',
|
||||||
defaultMessage: 'Name',
|
defaultMessage: 'Name',
|
||||||
})}
|
})}
|
||||||
value={model.title}
|
value={model.title}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
error={error}
|
error={error}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="description"
|
name="description"
|
||||||
type="text"
|
type="text"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'action.rename-description-placeholder',
|
id: 'action.rename-description-placeholder',
|
||||||
defaultMessage: 'Description',
|
defaultMessage: 'Description',
|
||||||
})}
|
})}
|
||||||
value={model.description}
|
value={model.description}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
required={false}
|
required={false}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CreateDialog;
|
export default CreateDialog;
|
||||||
|
@ -10,50 +10,53 @@ import Alert from '@mui/material/Alert';
|
|||||||
import AlertTitle from '@mui/material/AlertTitle';
|
import AlertTitle from '@mui/material/AlertTitle';
|
||||||
|
|
||||||
const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const mutation = useMutation((id: number) => client.deleteMap(id), {
|
const mutation = useMutation((id: number) => client.deleteMap(id), {
|
||||||
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
||||||
onError: (error: ErrorInfo) => {
|
onError: (error: ErrorInfo) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnSubmit = (): void => {
|
||||||
mutation.mutate(mapId);
|
mutation.mutate(mapId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { map } = fetchMapById(mapId)
|
const { map } = fetchMapById(mapId);
|
||||||
const alertTitle = `${intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })} ${map?.title}`;
|
const alertTitle = `${intl.formatMessage({
|
||||||
return (
|
id: 'action.delete-title',
|
||||||
<div>
|
defaultMessage: 'Delete',
|
||||||
<BaseDialog
|
})} ${map?.title}`;
|
||||||
error={error}
|
return (
|
||||||
onClose={handleOnClose}
|
<div>
|
||||||
onSubmit={handleOnSubmit}
|
<BaseDialog
|
||||||
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
error={error}
|
||||||
submitButton={intl.formatMessage({
|
onClose={handleOnClose}
|
||||||
id: 'action.delete-title',
|
onSubmit={handleOnSubmit}
|
||||||
defaultMessage: 'Delete',
|
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
||||||
})}
|
submitButton={intl.formatMessage({
|
||||||
>
|
id: 'action.delete-title',
|
||||||
<Alert severity="warning">
|
defaultMessage: 'Delete',
|
||||||
<AlertTitle>{alertTitle}</AlertTitle>
|
})}
|
||||||
<FormattedMessage
|
>
|
||||||
id="action.delete-description"
|
<Alert severity="warning">
|
||||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
<AlertTitle>{alertTitle}</AlertTitle>
|
||||||
/>
|
<FormattedMessage
|
||||||
</Alert>
|
id="action.delete-description"
|
||||||
</BaseDialog>
|
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||||
</div>
|
/>
|
||||||
);
|
</Alert>
|
||||||
|
</BaseDialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeleteDialog;
|
export default DeleteDialog;
|
||||||
|
@ -9,55 +9,52 @@ import BaseDialog from '../base-dialog';
|
|||||||
import Alert from '@mui/material/Alert';
|
import Alert from '@mui/material/Alert';
|
||||||
import AlertTitle from '@mui/material/AlertTitle';
|
import AlertTitle from '@mui/material/AlertTitle';
|
||||||
|
|
||||||
const DeleteMultiselectDialog = ({
|
const DeleteMultiselectDialog = ({ onClose, mapsId }: MultiDialogProps): React.ReactElement => {
|
||||||
onClose,
|
const intl = useIntl();
|
||||||
mapsId,
|
const client: Client = useSelector(activeInstance);
|
||||||
}: MultiDialogProps): React.ReactElement => {
|
const queryClient = useQueryClient();
|
||||||
const intl = useIntl();
|
|
||||||
const client: Client = useSelector(activeInstance);
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids), {
|
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids), {
|
||||||
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
onSuccess: () => handleOnMutationSuccess(() => onClose(true), queryClient),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(`Unexpected error ${error}`);
|
console.error(`Unexpected error ${error}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnSubmit = (): void => {
|
||||||
mutation.mutate(mapsId);
|
mutation.mutate(mapsId);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
title={intl.formatMessage({ id: 'action.delete-title', defaultMessage: 'Delete' })}
|
||||||
submitButton={intl.formatMessage({
|
submitButton={intl.formatMessage({
|
||||||
id: 'action.delete-title',
|
id: 'action.delete-title',
|
||||||
defaultMessage: 'Delete',
|
defaultMessage: 'Delete',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Alert severity="warning">
|
<Alert severity="warning">
|
||||||
<AlertTitle>
|
<AlertTitle>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="deletem.title"
|
id="deletem.title"
|
||||||
defaultMessage="All selected maps will be deleted"
|
defaultMessage="All selected maps will be deleted"
|
||||||
/>
|
/>
|
||||||
</AlertTitle>
|
</AlertTitle>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="action.delete-description"
|
id="action.delete-description"
|
||||||
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
defaultMessage="Deleted mindmap can not be recovered. Do you want to continue ?."
|
||||||
/>
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeleteMultiselectDialog;
|
export default DeleteMultiselectDialog;
|
||||||
|
@ -11,104 +11,104 @@ import { SimpleDialogProps } from '..';
|
|||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog';
|
||||||
|
|
||||||
export type DuplicateModel = {
|
export type DuplicateModel = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: DuplicateModel = { title: '', description: '', id: -1 };
|
const defaultModel: DuplicateModel = { title: '', description: '', id: -1 };
|
||||||
const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const service: Client = useSelector(activeInstance);
|
const service: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<DuplicateModel>(defaultModel);
|
const [model, setModel] = React.useState<DuplicateModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const mutation = useMutation<number, ErrorInfo, DuplicateModel>(
|
const mutation = useMutation<number, ErrorInfo, DuplicateModel>(
|
||||||
(model: DuplicateModel) => {
|
(model: DuplicateModel) => {
|
||||||
const { id, ...rest } = model;
|
const { id, ...rest } = model;
|
||||||
return service.duplicateMap(id, rest);
|
return service.duplicateMap(id, rest);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (mapId) => {
|
onSuccess: (mapId) => {
|
||||||
window.location.href = `/c/maps/${mapId}/edit`;
|
window.location.href = `/c/maps/${mapId}/edit`;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
mutation.mutate(model);
|
mutation.mutate(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name;
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
setModel(map);
|
setModel(map);
|
||||||
}
|
}
|
||||||
}, [mapId]);
|
}, [mapId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
error={error}
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}
|
title={intl.formatMessage({ id: 'duplicate.title', defaultMessage: 'Duplicate' })}
|
||||||
description={intl.formatMessage({
|
description={intl.formatMessage({
|
||||||
id: 'rename.description',
|
id: 'rename.description',
|
||||||
defaultMessage: 'Please, fill the new map name and description.',
|
defaultMessage: 'Please, fill the new map name and description.',
|
||||||
})}
|
})}
|
||||||
submitButton={intl.formatMessage({
|
submitButton={intl.formatMessage({
|
||||||
id: 'duplicate.title',
|
id: 'duplicate.title',
|
||||||
defaultMessage: 'Duplicate',
|
defaultMessage: 'Duplicate',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input
|
<Input
|
||||||
name="title"
|
name="title"
|
||||||
type="text"
|
type="text"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'action.rename-name-placeholder',
|
id: 'action.rename-name-placeholder',
|
||||||
defaultMessage: 'Name',
|
defaultMessage: 'Name',
|
||||||
})}
|
})}
|
||||||
value={model.title}
|
value={model.title}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
error={error}
|
error={error}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="description"
|
name="description"
|
||||||
type="text"
|
type="text"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'action.rename-description-placeholder',
|
id: 'action.rename-description-placeholder',
|
||||||
defaultMessage: 'Description',
|
defaultMessage: 'Description',
|
||||||
})}
|
})}
|
||||||
value={model.description}
|
value={model.description}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
required={false}
|
required={false}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DuplicateDialog;
|
export default DuplicateDialog;
|
||||||
|
@ -10,7 +10,13 @@ import FormControlLabel from '@mui/material/FormControlLabel';
|
|||||||
import Radio from '@mui/material/Radio';
|
import Radio from '@mui/material/Radio';
|
||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
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 Client from '../../../../classes/client';
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
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 ExportGroup = 'image' | 'document' | 'mindmap-tool';
|
||||||
|
|
||||||
type ExportDialogProps = {
|
type ExportDialogProps = {
|
||||||
mapId: number;
|
mapId: number;
|
||||||
enableImgExport: boolean;
|
enableImgExport: boolean;
|
||||||
svgXml?: string;
|
svgXml?: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExportDialog = ({
|
const ExportDialog = ({
|
||||||
mapId,
|
mapId,
|
||||||
onClose,
|
onClose,
|
||||||
enableImgExport
|
enableImgExport,
|
||||||
}: ExportDialogProps): React.ReactElement => {
|
}: ExportDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
const [submit, setSubmit] = React.useState<boolean>(false);
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId);
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
|
|
||||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||||
enableImgExport ? 'image' : 'document'
|
enableImgExport ? 'image' : 'document',
|
||||||
);
|
);
|
||||||
const [exportFormat, setExportFormat] = React.useState<ExportFormat>(
|
const [exportFormat, setExportFormat] = React.useState<ExportFormat>(
|
||||||
enableImgExport ? 'svg' : 'txt'
|
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) => {
|
const handleOnExportFormatChange = (event) => {
|
||||||
setExportFormat(event.target.value);
|
setExportFormat(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnGroupChange = (event) => {
|
const handleOnGroupChange = (event) => {
|
||||||
const value: ExportGroup = event.target.value;
|
const value: ExportGroup = event.target.value;
|
||||||
setExportGroup(value);
|
setExportGroup(value);
|
||||||
|
|
||||||
let defaultFormat: ExportFormat;
|
let defaultFormat: ExportFormat;
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'document':
|
case 'document':
|
||||||
defaultFormat = 'txt';
|
defaultFormat = 'txt';
|
||||||
break;
|
break;
|
||||||
case 'image':
|
case 'image':
|
||||||
defaultFormat = 'svg';
|
defaultFormat = 'svg';
|
||||||
break;
|
break;
|
||||||
case 'mindmap-tool':
|
case 'mindmap-tool':
|
||||||
defaultFormat = 'wxml';
|
defaultFormat = 'wxml';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
setExportFormat(defaultFormat);
|
setExportFormat(defaultFormat);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnSubmit = (): void => {
|
||||||
setSubmit(true);
|
setSubmit(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnZoomToFit = (): void => {
|
const handleOnZoomToFit = (): void => {
|
||||||
setZoomToFit(!zoomToFit);
|
setZoomToFit(!zoomToFit);
|
||||||
};
|
};
|
||||||
|
|
||||||
const exporter = (formatType: ExportFormat): Promise<string> => {
|
const exporter = (formatType: ExportFormat): Promise<string> => {
|
||||||
let svgElement: Element | null = null;
|
let svgElement: Element | null = null;
|
||||||
let size: SizeType;
|
let size: SizeType;
|
||||||
let mindmap: Mindmap;
|
let mindmap: Mindmap;
|
||||||
|
|
||||||
const designer: Designer = global.designer;
|
const designer: Designer = global.designer;
|
||||||
if (designer != null) {
|
if (designer != null) {
|
||||||
// Depending on the type of export. It will require differt POST.
|
// Depending on the type of export. It will require differt POST.
|
||||||
const workspace = designer.getWorkSpace();
|
const workspace = designer.getWorkSpace();
|
||||||
svgElement = workspace.getSVGElement();
|
svgElement = workspace.getSVGElement();
|
||||||
size = { width: window.innerWidth, height: window.innerHeight };
|
size = { width: window.innerWidth, height: window.innerHeight };
|
||||||
mindmap = designer.getMindmap();
|
mindmap = designer.getMindmap();
|
||||||
} else {
|
} else {
|
||||||
mindmap = client.fetchMindmap(mapId);
|
mindmap = client.fetchMindmap(mapId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let exporter: Exporter;
|
let exporter: Exporter;
|
||||||
switch (formatType) {
|
switch (formatType) {
|
||||||
case 'png':
|
case 'png':
|
||||||
case 'jpg':
|
case 'jpg':
|
||||||
case 'svg': {
|
case 'svg': {
|
||||||
exporter = ImageExporterFactory.create(formatType, svgElement, size.width, size.height, zoomToFit);
|
exporter = ImageExporterFactory.create(
|
||||||
break;
|
formatType,
|
||||||
}
|
svgElement,
|
||||||
case 'wxml':
|
size.width,
|
||||||
case 'mm':
|
size.height,
|
||||||
case 'md':
|
zoomToFit,
|
||||||
case 'txt': {
|
);
|
||||||
exporter = TextExporterFactory.create(formatType, mindmap);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case 'wxml':
|
||||||
default:
|
case 'mm':
|
||||||
throw new Error('Unsupported encoding');
|
case 'md':
|
||||||
}
|
case 'txt': {
|
||||||
|
exporter = TextExporterFactory.create(formatType, mindmap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported encoding');
|
||||||
|
}
|
||||||
|
|
||||||
return exporter.exportAndEncode();
|
return exporter.exportAndEncode();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (submit) {
|
if (submit) {
|
||||||
exporter(exportFormat)
|
exporter(exportFormat)
|
||||||
.then((url: string) => {
|
.then((url: string) => {
|
||||||
// Create hidden anchor to force download ...
|
// Create hidden anchor to force download ...
|
||||||
const anchor: HTMLAnchorElement = document.createElement('a');
|
const anchor: HTMLAnchorElement = document.createElement('a');
|
||||||
anchor.style.display = 'display: none';
|
anchor.style.display = 'display: none';
|
||||||
anchor.download = `${map?.title}.${exportFormat}`;
|
anchor.download = `${map?.title}.${exportFormat}`;
|
||||||
anchor.href = url;
|
anchor.href = url;
|
||||||
document.body.appendChild(anchor);
|
document.body.appendChild(anchor);
|
||||||
|
|
||||||
// Trigger click ...
|
// Trigger click ...
|
||||||
anchor.click();
|
anchor.click();
|
||||||
|
|
||||||
// Clean up ...
|
// Clean up ...
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
document.body.removeChild(anchor);
|
document.body.removeChild(anchor);
|
||||||
}).catch((fail) => {
|
})
|
||||||
console.error("Unexpected error during export:" + fail);
|
.catch((fail) => {
|
||||||
});
|
console.error('Unexpected error during export:' + fail);
|
||||||
|
});
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
}, [submit]);
|
}, [submit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
title={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
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' })}
|
description={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
id: 'export.desc',
|
||||||
>
|
defaultMessage:
|
||||||
{
|
'Export this map in the format that you want and start using it in your presentations or sharing by email',
|
||||||
!enableImgExport &&
|
})}
|
||||||
<Alert severity="info">
|
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||||
<FormattedMessage
|
>
|
||||||
id="export.warning"
|
{!enableImgExport && (
|
||||||
defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar."
|
<Alert severity="info">
|
||||||
/>
|
<FormattedMessage
|
||||||
</Alert>
|
id="export.warning"
|
||||||
}
|
defaultMessage="Exporting to Image (SVG,PNG,JPEG,PDF) is only available in the editor toolbar."
|
||||||
<FormControl component="fieldset">
|
/>
|
||||||
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
</Alert>
|
||||||
<FormControl>
|
)}
|
||||||
<FormControlLabel
|
<FormControl component="fieldset">
|
||||||
className={classes.label}
|
<RadioGroup name="export" value={exportGroup} onChange={handleOnGroupChange}>
|
||||||
value="image"
|
<FormControl>
|
||||||
disabled={!enableImgExport}
|
<FormControlLabel
|
||||||
control={<Radio color="primary" />}
|
className={classes.label}
|
||||||
label={intl.formatMessage({
|
value="image"
|
||||||
id: 'export.image',
|
disabled={!enableImgExport}
|
||||||
defaultMessage:
|
control={<Radio color="primary" />}
|
||||||
'Image: Get a graphic representation of your map including all colors and shapes.',
|
label={intl.formatMessage({
|
||||||
})}
|
id: 'export.image',
|
||||||
color="secondary"
|
defaultMessage:
|
||||||
style={{ fontSize: '9px' }}
|
'Image: Get a graphic representation of your map including all colors and shapes.',
|
||||||
/>
|
})}
|
||||||
{exportGroup == 'image' && (
|
color="secondary"
|
||||||
<>
|
style={{ fontSize: '9px' }}
|
||||||
<Select
|
/>
|
||||||
onChange={handleOnExportFormatChange}
|
{exportGroup == 'image' && (
|
||||||
variant="outlined"
|
<>
|
||||||
value={exportFormat}
|
<Select
|
||||||
className={classes.select}
|
onChange={handleOnExportFormatChange}
|
||||||
>
|
variant="outlined"
|
||||||
<MenuItem value="svg" className={classes.menu}>
|
value={exportFormat}
|
||||||
Scalable Vector Graphics (SVG)
|
className={classes.select}
|
||||||
</MenuItem>
|
>
|
||||||
<MenuItem value="png" className={classes.menu}>
|
<MenuItem value="svg" className={classes.menu}>
|
||||||
Portable Network Graphics (PNG)
|
Scalable Vector Graphics (SVG)
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value="jpg" className={classes.menu}>
|
<MenuItem value="png" className={classes.menu}>
|
||||||
JPEG Image (JPEG)
|
Portable Network Graphics (PNG)
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Select>
|
<MenuItem value="jpg" className={classes.menu}>
|
||||||
<FormControlLabel
|
JPEG Image (JPEG)
|
||||||
className={classes.select}
|
</MenuItem>
|
||||||
control={<Checkbox checked={zoomToFit} onChange={handleOnZoomToFit} />}
|
</Select>
|
||||||
label={intl.formatMessage({
|
<FormControlLabel
|
||||||
id: 'export.img-center',
|
className={classes.select}
|
||||||
defaultMessage:
|
control={<Checkbox checked={zoomToFit} onChange={handleOnZoomToFit} />}
|
||||||
'Center and zoom to fit',
|
label={intl.formatMessage({
|
||||||
})} />
|
id: 'export.img-center',
|
||||||
</>
|
defaultMessage: 'Center and zoom to fit',
|
||||||
)}
|
})}
|
||||||
</FormControl>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
className={classes.label}
|
className={classes.label}
|
||||||
value="document"
|
value="document"
|
||||||
control={<Radio color="primary" />}
|
control={<Radio color="primary" />}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'export.document-label',
|
id: 'export.document-label',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Document: Export your mindmap in a self-contained document ready to share',
|
'Document: Export your mindmap in a self-contained document ready to share',
|
||||||
})}
|
})}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
/>
|
/>
|
||||||
{exportGroup == 'document' && (
|
{exportGroup == 'document' && (
|
||||||
<Select
|
<Select
|
||||||
onChange={handleOnExportFormatChange}
|
onChange={handleOnExportFormatChange}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={exportFormat}
|
value={exportFormat}
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
>
|
>
|
||||||
<MenuItem className={classes.select} value="txt">
|
<MenuItem className={classes.select} value="txt">
|
||||||
Plain Text File (TXT)
|
Plain Text File (TXT)
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem className={classes.select} value="md">
|
<MenuItem className={classes.select} value="md">
|
||||||
Markdown (MD)
|
Markdown (MD)
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{/* <MenuItem className={classes.select} value="xls">
|
{/* <MenuItem className={classes.select} value="xls">
|
||||||
Microsoft Excel (XLS)
|
Microsoft Excel (XLS)
|
||||||
</MenuItem> */}
|
</MenuItem> */}
|
||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
className={classes.label}
|
className={classes.label}
|
||||||
value="mindmap-tool"
|
value="mindmap-tool"
|
||||||
control={<Radio color="primary" />}
|
control={<Radio color="primary" />}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'export.document',
|
id: 'export.document',
|
||||||
defaultMessage:
|
defaultMessage:
|
||||||
'Mindmap Tools: Export your mindmap in thirdparty mindmap tool formats',
|
'Mindmap Tools: Export your mindmap in thirdparty mindmap tool formats',
|
||||||
})}
|
})}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
/>
|
/>
|
||||||
{exportGroup == 'mindmap-tool' && (
|
{exportGroup == 'mindmap-tool' && (
|
||||||
<Select
|
<Select
|
||||||
onChange={handleOnExportFormatChange}
|
onChange={handleOnExportFormatChange}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
value={exportFormat}
|
value={exportFormat}
|
||||||
>
|
>
|
||||||
<MenuItem className={classes.select} value="wxml">
|
<MenuItem className={classes.select} value="wxml">
|
||||||
WiseMapping (WXML)
|
WiseMapping (WXML)
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem className={classes.select} value="mm">
|
<MenuItem className={classes.select} value="mm">
|
||||||
Freemind 1.0.1 (MM)
|
Freemind 1.0.1 (MM)
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{/* <MenuItem className={classes.select} value="mmap">
|
{/* <MenuItem className={classes.select} value="mmap">
|
||||||
MindManager (MMAP)
|
MindManager (MMAP)
|
||||||
</MenuItem> */}
|
</MenuItem> */}
|
||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ExportDialog;
|
export default ExportDialog;
|
||||||
|
@ -19,115 +19,94 @@ import Link from '@mui/material/Link';
|
|||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
|
|
||||||
const HistoryDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const HistoryDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const { data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>(`history-${mapId}`, () => {
|
const { data } = useQuery<unknown, ErrorInfo, ChangeHistory[]>(`history-${mapId}`, () => {
|
||||||
return client.fetchHistory(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 => {
|
return (
|
||||||
queryClient.invalidateQueries(`history-${mapId}`);
|
<div>
|
||||||
onClose();
|
<BaseDialog
|
||||||
};
|
onClose={handleOnClose}
|
||||||
|
title={intl.formatMessage({
|
||||||
const handleOnClick = (event, vid: number): void => {
|
id: 'action.history-title',
|
||||||
event.preventDefault();
|
defaultMessage: 'Version history',
|
||||||
client.revertHistory(mapId, vid).then(() => {
|
})}
|
||||||
handleOnClose();
|
description={intl.formatMessage({
|
||||||
});
|
id: 'action.history-description',
|
||||||
};
|
defaultMessage: 'List of changes introduced in the last 90 days.',
|
||||||
|
})}
|
||||||
return (
|
>
|
||||||
<div>
|
<TableContainer component={Paper} style={{ maxHeight: '200px' }} variant="outlined">
|
||||||
<BaseDialog
|
<Table size="small" stickyHeader>
|
||||||
onClose={handleOnClose}
|
<TableHead>
|
||||||
title={intl.formatMessage({
|
<TableRow>
|
||||||
id: 'action.history-title',
|
<TableCell align="left">
|
||||||
defaultMessage: 'Version history',
|
<FormattedMessage id="maps.modified-by" defaultMessage="Modified By" />
|
||||||
})}
|
</TableCell>
|
||||||
description={intl.formatMessage({
|
<TableCell align="left">
|
||||||
id: 'action.history-description',
|
<FormattedMessage id="maps.modified" defaultMessage="Modified" />
|
||||||
defaultMessage: 'List of changes introduced in the last 90 days.',
|
</TableCell>
|
||||||
})}
|
<TableCell align="left"></TableCell>
|
||||||
>
|
<TableCell align="left"></TableCell>
|
||||||
<TableContainer component={Paper} style={{ maxHeight: '200px' }} variant="outlined">
|
</TableRow>
|
||||||
<Table size="small" stickyHeader>
|
</TableHead>
|
||||||
<TableHead>
|
<TableBody>
|
||||||
<TableRow>
|
{changeHistory.length == 0 ? (
|
||||||
<TableCell align="left">
|
<TableRow>
|
||||||
<FormattedMessage
|
<TableCell colSpan={4}>
|
||||||
id="maps.modified-by"
|
<FormattedMessage
|
||||||
defaultMessage="Modified By"
|
id="history.no-changes"
|
||||||
/>
|
defaultMessage="There is no changes available"
|
||||||
</TableCell>
|
/>
|
||||||
<TableCell align="left">
|
</TableCell>
|
||||||
<FormattedMessage
|
</TableRow>
|
||||||
id="maps.modified"
|
) : (
|
||||||
defaultMessage="Modified"
|
changeHistory.map((row) => (
|
||||||
/>
|
<TableRow key={row.id}>
|
||||||
</TableCell>
|
<TableCell align="left">{row.lastModificationBy}</TableCell>
|
||||||
<TableCell align="left"></TableCell>
|
<TableCell align="left">
|
||||||
<TableCell align="left"></TableCell>
|
<Tooltip
|
||||||
</TableRow>
|
title={dayjs(row.lastModificationTime).format('lll')}
|
||||||
</TableHead>
|
placement="bottom-start"
|
||||||
<TableBody>
|
>
|
||||||
{changeHistory.length == 0 ? (
|
<span>{dayjs(row.lastModificationTime).fromNow()}</span>
|
||||||
<TableRow>
|
</Tooltip>
|
||||||
<TableCell colSpan={4}>
|
</TableCell>
|
||||||
<FormattedMessage
|
<TableCell align="left">
|
||||||
id="history.no-changes"
|
<Link href={`/c/maps/${mapId}/${row.id}/view`} target="history">
|
||||||
defaultMessage="There is no changes available"
|
<FormattedMessage id="maps.view" defaultMessage="View" />
|
||||||
/>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
<TableCell align="left">
|
||||||
) : (
|
<Link href="#" onClick={(e) => handleOnClick(e, row.id)}>
|
||||||
changeHistory.map((row) => (
|
<FormattedMessage id="maps.revert" defaultMessage="Revert" />
|
||||||
<TableRow key={row.id}>
|
</Link>
|
||||||
<TableCell align="left">{row.lastModificationBy}</TableCell>
|
</TableCell>
|
||||||
<TableCell align="left">
|
</TableRow>
|
||||||
<Tooltip
|
))
|
||||||
title={dayjs(row.lastModificationTime).format(
|
)}
|
||||||
'lll'
|
</TableBody>
|
||||||
)}
|
</Table>
|
||||||
placement="bottom-start"
|
</TableContainer>
|
||||||
>
|
</BaseDialog>
|
||||||
<span>
|
</div>
|
||||||
{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;
|
export default HistoryDialog;
|
||||||
|
@ -13,204 +13,206 @@ import Input from '../../../form/input';
|
|||||||
import BaseDialog from '../base-dialog';
|
import BaseDialog from '../base-dialog';
|
||||||
|
|
||||||
export type ImportModel = {
|
export type ImportModel = {
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
content?: null | string;
|
content?: null | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateProps = {
|
export type CreateProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ErrorFile = {
|
type ErrorFile = {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const defaultModel: ImportModel = { title: '' };
|
const defaultModel: ImportModel = { title: '' };
|
||||||
const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<ImportModel>(defaultModel);
|
const [model, setModel] = React.useState<ImportModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
const [errorFile, setErrorFile] = React.useState<ErrorFile>({error: false, message: ''});
|
const [errorFile, setErrorFile] = React.useState<ErrorFile>({ error: false, message: '' });
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const mutation = useMutation<number, ErrorInfo, ImportModel>(
|
const mutation = useMutation<number, ErrorInfo, ImportModel>(
|
||||||
(model: ImportModel) => {
|
(model: ImportModel) => {
|
||||||
return client.importMap(model);
|
return client.importMap(model);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (mapId: number) => {
|
onSuccess: (mapId: number) => {
|
||||||
window.location.href = `/c/maps/${mapId}/edit`;
|
window.location.href = `/c/maps/${mapId}/edit`;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(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 => {
|
const extensionFile = file.name.split('.').pop();
|
||||||
onClose();
|
const extensionAccept = ['wxml', 'mm'];
|
||||||
setModel(defaultModel);
|
|
||||||
setError(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
if (!extensionAccept.includes(extensionFile)) {
|
||||||
event.preventDefault();
|
setErrorFile({
|
||||||
mutation.mutate(model);
|
error: true,
|
||||||
};
|
message: intl.formatMessage(
|
||||||
|
{
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
id: 'import.error-file',
|
||||||
event.preventDefault();
|
defaultMessage: 'Import error {error}',
|
||||||
|
},
|
||||||
const name = event.target.name;
|
{
|
||||||
const value = event.target.value;
|
error:
|
||||||
setModel({ ...model, [name as keyof ImportModel]: value });
|
'You can import WiseMapping and Freemind maps to your list of maps. Select the file you want to import.',
|
||||||
};
|
},
|
||||||
|
),
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
model.contentType = 'application/xml';
|
||||||
<div>
|
|
||||||
<BaseDialog
|
const fileContent = event?.target?.result;
|
||||||
onClose={handleOnClose}
|
const mapConent: string =
|
||||||
onSubmit={handleOnSubmit}
|
typeof fileContent === 'string' ? fileContent : fileContent.toString();
|
||||||
error={error}
|
|
||||||
title={intl.formatMessage({
|
try {
|
||||||
id: 'import.title',
|
const importer: Importer = TextImporterFactory.create(extensionFile, mapConent);
|
||||||
defaultMessage: 'Import existing mindmap',
|
|
||||||
})}
|
importer.import(model.title, model.description).then((res) => {
|
||||||
description={intl.formatMessage({
|
model.content = res;
|
||||||
id: 'import.description',
|
setModel({ ...model });
|
||||||
defaultMessage:
|
});
|
||||||
'You can import WiseMapping and Freemind maps to your list of maps. Select the file you want to import.',
|
} catch (e) {
|
||||||
})}
|
if (e instanceof Error) {
|
||||||
submitButton={intl.formatMessage({ id: 'import.button', defaultMessage: 'Create' })}
|
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 &&
|
<FormattedMessage id="maps.choose-file" defaultMessage="Choose a file" />
|
||||||
<Alert severity='error'>
|
</Button>
|
||||||
<p>{errorFile.message}</p>
|
</label>
|
||||||
</Alert>
|
</FormControl>
|
||||||
}
|
</BaseDialog>
|
||||||
<FormControl fullWidth={true}>
|
</div>
|
||||||
<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>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ImportDialog;
|
export default ImportDialog;
|
||||||
|
@ -15,87 +15,87 @@ import ShareDialog from './share-dialog';
|
|||||||
import LabelDialog from './label-dialog';
|
import LabelDialog from './label-dialog';
|
||||||
import ReactGA from 'react-ga4';
|
import ReactGA from 'react-ga4';
|
||||||
|
|
||||||
|
|
||||||
export type BasicMapInfo = {
|
export type BasicMapInfo = {
|
||||||
name: string;
|
name: string;
|
||||||
description: string | undefined;
|
description: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ActionDialogProps = {
|
type ActionDialogProps = {
|
||||||
action?: ActionType;
|
action?: ActionType;
|
||||||
mapsId: number[];
|
mapsId: number[];
|
||||||
onClose: (success?: boolean) => void;
|
onClose: (success?: boolean) => void;
|
||||||
fromEditor: boolean;
|
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(() => {
|
const handleOnClose = (success?: boolean): void => {
|
||||||
ReactGA.event({
|
onClose(success);
|
||||||
category: 'map metadata',
|
};
|
||||||
action: action,
|
|
||||||
nonInteraction: true
|
|
||||||
|
|
||||||
});
|
switch (action) {
|
||||||
}, [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 => {
|
return (
|
||||||
onClose(success);
|
<span>
|
||||||
};
|
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||||
|
{action === 'delete' && mapsId.length == 1 && (
|
||||||
switch (action) {
|
<DeleteDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
||||||
case 'open':
|
)}
|
||||||
window.location.href = `/c/maps/${mapsId}/edit`;
|
{action === 'delete' && mapsId.length > 1 && (
|
||||||
break;
|
<DeleteMultiselectDialog onClose={handleOnClose} mapsId={mapsId} />
|
||||||
case 'print':
|
)}
|
||||||
window.open(`/c/maps/${mapsId}/print`, 'print');
|
{action === 'rename' && <RenameDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
break;
|
{action === 'duplicate' && <DuplicateDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
}
|
{action === 'history' && <HistoryDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
|
{action === 'import' && <ImportDialog onClose={handleOnClose} />}
|
||||||
return (
|
{action === 'publish' && <PublishDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
<span>
|
{action === 'info' && <InfoDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
{action === 'create' && <CreateDialog onClose={handleOnClose} />}
|
||||||
{action === 'delete' && mapsId.length == 1 && (
|
{action === 'export' && (
|
||||||
<DeleteDialog onClose={handleOnClose} mapId={mapsId[0]} />
|
<ExportDialog onClose={handleOnClose} mapId={mapsId[0]} enableImgExport={fromEditor} />
|
||||||
)}
|
)}
|
||||||
{action === 'delete' && mapsId.length > 1 && (
|
{action === 'share' && <ShareDialog onClose={handleOnClose} mapId={mapsId[0]} />}
|
||||||
<DeleteMultiselectDialog onClose={handleOnClose} mapsId={mapsId} />
|
{action === 'label' && <LabelDialog onClose={handleOnClose} mapsId={mapsId} />}
|
||||||
)}
|
</span>
|
||||||
{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 = {
|
ActionDispatcher.defaultProps = {
|
||||||
fromEditor: false,
|
fromEditor: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
export const handleOnMutationSuccess = (onClose: () => void, queryClient: QueryClient): void => {
|
||||||
queryClient.invalidateQueries('maps');
|
queryClient.invalidateQueries('maps');
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SimpleDialogProps = {
|
export type SimpleDialogProps = {
|
||||||
mapId: number;
|
mapId: number;
|
||||||
onClose: (success?: boolean) => void;
|
onClose: (success?: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MultiDialogProps = {
|
export type MultiDialogProps = {
|
||||||
mapsId: number[];
|
mapsId: number[];
|
||||||
onClose: (success?: boolean) => void;
|
onClose: (success?: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ActionDispatcher;
|
export default ActionDispatcher;
|
||||||
|
@ -14,174 +14,112 @@ import Typography from '@mui/material/Typography';
|
|||||||
import List from '@mui/material/List';
|
import List from '@mui/material/List';
|
||||||
import LocalizedFormat from 'dayjs/plugin/localizedFormat';
|
import LocalizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
|
|
||||||
|
|
||||||
// Load fromNow pluggin
|
// Load fromNow pluggin
|
||||||
dayjs.extend(LocalizedFormat)
|
dayjs.extend(LocalizedFormat);
|
||||||
|
|
||||||
|
|
||||||
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
error={error}
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'info.title', defaultMessage: 'Info' })}
|
title={intl.formatMessage({ id: 'info.title', defaultMessage: 'Info' })}
|
||||||
description={intl.formatMessage({
|
description={intl.formatMessage({
|
||||||
id: 'info.description-msg',
|
id: 'info.description-msg',
|
||||||
defaultMessage:
|
defaultMessage: 'By publishing the map you make it visible to everyone on the Internet.',
|
||||||
'By publishing the map you make it visible to everyone on the Internet.',
|
})}
|
||||||
})}
|
submitButton={intl.formatMessage({ id: 'info.button', defaultMessage: 'Accept' })}
|
||||||
submitButton={intl.formatMessage({ id: 'info.button', defaultMessage: 'Accept' })}
|
>
|
||||||
>
|
<Paper style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined" elevation={0}>
|
||||||
<Paper style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined" elevation={0}>
|
<Card variant="outlined">
|
||||||
<Card variant="outlined">
|
<List dense={true}>
|
||||||
<List dense={true}>
|
<ListItem>
|
||||||
<ListItem>
|
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
<FormattedMessage id="info.basic-info" defaultMessage="Basic Info" />
|
||||||
<FormattedMessage
|
</Typography>
|
||||||
id="info.basic-info"
|
</ListItem>
|
||||||
defaultMessage="Basic Info"
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.name" defaultMessage="Name" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{map?.title}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage id="info.name" defaultMessage="Name" />:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">{map?.title}</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.description" defaultMessage="Description" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{map?.description}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage
|
|
||||||
id="info.description"
|
|
||||||
defaultMessage="Description"
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">{map?.description}</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.creator" defaultMessage="Creator" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{map?.createdBy}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage id="info.creator" defaultMessage="Creator" />:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">{map?.createdBy}</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.creation-time" defaultMessage="Creation Date" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{dayjs(map?.creationTime).format('LLL')}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage
|
|
||||||
id="info.creation-time"
|
|
||||||
defaultMessage="Creation Date"
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">
|
|
||||||
{dayjs(map?.creationTime).format('LLL')}
|
|
||||||
</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.modified-tny" defaultMessage="Last Modified By" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{map?.lastModificationBy}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage
|
|
||||||
id="info.modified-tny"
|
|
||||||
defaultMessage="Last Modified By"
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">{map?.lastModificationBy}</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.modified-time" defaultMessage="Last Modified Date" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">
|
||||||
>
|
{dayjs(map?.lastModificationTime).format('LLL')}
|
||||||
<FormattedMessage
|
</Typography>
|
||||||
id="info.modified-time"
|
</ListItem>
|
||||||
defaultMessage="Last Modified Date"
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">
|
|
||||||
{dayjs(map?.lastModificationTime).format('LLL')}
|
|
||||||
</Typography>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.starred" defaultMessage="Starred" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{Boolean(map?.starred).toString()}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage id="info.starred" defaultMessage="Starred" />:
|
</List>
|
||||||
</Typography>
|
</Card>
|
||||||
<Typography variant="body2">
|
|
||||||
{Boolean(map?.starred).toString()}
|
|
||||||
</Typography>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card variant="outlined" style={{ marginTop: '10px' }}>
|
<Card variant="outlined" style={{ marginTop: '10px' }}>
|
||||||
<List dense={true}>
|
<List dense={true}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
<Typography variant="body1" style={{ fontWeight: 'bold' }}>
|
||||||
<FormattedMessage id="info.sharing" defaultMessage="Sharing" />
|
<FormattedMessage id="info.sharing" defaultMessage="Sharing" />
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<Typography
|
<Typography variant="caption" color="textPrimary" className={classes.textDesc}>
|
||||||
variant="caption"
|
<FormattedMessage id="info.public-visibility" defaultMessage="Publicly Visible" />:
|
||||||
color="textPrimary"
|
</Typography>
|
||||||
className={classes.textDesc}
|
<Typography variant="body2">{Boolean(map?.isPublic).toString()}</Typography>
|
||||||
>
|
</ListItem>
|
||||||
<FormattedMessage
|
</Card>
|
||||||
id="info.public-visibility"
|
</Paper>
|
||||||
defaultMessage="Publicly Visible"
|
</BaseDialog>
|
||||||
/>
|
);
|
||||||
:
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2">{Boolean(map?.isPublic).toString()}</Typography>
|
|
||||||
</ListItem>
|
|
||||||
</Card>
|
|
||||||
</Paper>
|
|
||||||
</BaseDialog>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InfoDialog;
|
export default InfoDialog;
|
||||||
|
@ -12,75 +12,77 @@ import { LabelSelector } from '../../maps-list/label-selector';
|
|||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice';
|
||||||
import { ChangeLabelMutationFunctionParam, getChangeLabelMutationFunction } from '../../maps-list';
|
import { ChangeLabelMutationFunctionParam, getChangeLabelMutationFunction } from '../../maps-list';
|
||||||
|
|
||||||
|
|
||||||
const LabelDialog = ({ mapsId, onClose }: MultiDialogProps): React.ReactElement => {
|
const LabelDialog = ({ mapsId, onClose }: MultiDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
// TODO: pass down map data instead of using query?
|
// TODO: pass down map data instead of using query?
|
||||||
const { data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
const { data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||||
return client.fetchAllMaps();
|
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));
|
return (
|
||||||
|
<div>
|
||||||
const changeLabelMutation = useMutation<void, ErrorInfo, ChangeLabelMutationFunctionParam, number>(
|
<BaseDialog
|
||||||
getChangeLabelMutationFunction(client),
|
onClose={onClose}
|
||||||
{
|
title={intl.formatMessage({
|
||||||
onSuccess: () => {
|
id: 'label.title',
|
||||||
queryClient.invalidateQueries('maps');
|
defaultMessage: 'Add a label',
|
||||||
queryClient.invalidateQueries('labels');
|
})}
|
||||||
},
|
description={intl.formatMessage({
|
||||||
onError: (error) => {
|
id: 'label.description',
|
||||||
setError(error);
|
defaultMessage: 'Use labels to organize your maps.',
|
||||||
}
|
})}
|
||||||
}
|
PaperProps={{ classes: { root: classes.paper } }}
|
||||||
);
|
error={error}
|
||||||
|
>
|
||||||
const handleChangesInLabels = (label: Label, checked: boolean) => {
|
<>
|
||||||
setError(undefined);
|
<Typography variant="body2" marginTop="10px">
|
||||||
changeLabelMutation.mutate({
|
<FormattedMessage id="label.add-for" defaultMessage="Editing labels for " />
|
||||||
maps,
|
{maps.length > 1 ? (
|
||||||
label,
|
<FormattedMessage
|
||||||
checked
|
id="label.maps-count"
|
||||||
});
|
defaultMessage="{count} maps"
|
||||||
};
|
values={{ count: maps.length }}
|
||||||
|
/>
|
||||||
return (
|
) : (
|
||||||
<div>
|
maps.map((m) => m.title).join(', ')
|
||||||
<BaseDialog
|
)}
|
||||||
onClose={onClose}
|
</Typography>
|
||||||
title={intl.formatMessage({
|
<LabelSelector onChange={handleChangesInLabels} maps={maps} />
|
||||||
id: 'label.title',
|
</>
|
||||||
defaultMessage: 'Add a label',
|
</BaseDialog>
|
||||||
})}
|
</div>
|
||||||
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;
|
export default LabelDialog;
|
||||||
|
@ -21,139 +21,133 @@ import Box from '@mui/system/Box';
|
|||||||
import AppConfig from '../../../../classes/app-config';
|
import AppConfig from '../../../../classes/app-config';
|
||||||
|
|
||||||
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId);
|
||||||
|
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
const [activeTab, setActiveTab] = React.useState('1');
|
const [activeTab, setActiveTab] = React.useState('1');
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const mutation = useMutation<void, ErrorInfo, boolean>(
|
const mutation = useMutation<void, ErrorInfo, boolean>(
|
||||||
(model: boolean) => {
|
(model: boolean) => {
|
||||||
return client.updateMapToPublic(mapId, model);
|
return client.updateMapToPublic(mapId, model);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setModel(model);
|
setModel(model);
|
||||||
handleOnMutationSuccess(onClose, queryClient);
|
handleOnMutationSuccess(onClose, queryClient);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
mutation.mutate(model);
|
mutation.mutate(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setModel(checked);
|
setModel(checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabChange = (event: React.ChangeEvent<HTMLInputElement>, newValue: string) => {
|
const handleTabChange = (event: React.ChangeEvent<HTMLInputElement>, newValue: string) => {
|
||||||
setActiveTab(newValue);
|
setActiveTab(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseUrl = AppConfig.getBaseUrl();
|
const baseUrl = AppConfig.getBaseUrl();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
error={error}
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'publish.title', defaultMessage: 'Publish' })}
|
title={intl.formatMessage({ id: 'publish.title', defaultMessage: 'Publish' })}
|
||||||
description={intl.formatMessage({
|
description={intl.formatMessage({
|
||||||
id: 'publish.description',
|
id: 'publish.description',
|
||||||
defaultMessage:
|
defaultMessage: 'By publishing the map you make it visible to everyone on the Internet.',
|
||||||
'By publishing the map you make it visible to everyone on the Internet.',
|
})}
|
||||||
})}
|
submitButton={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({
|
id: 'publish.button',
|
||||||
id: 'publish.button',
|
defaultMessage: 'Accept',
|
||||||
defaultMessage: 'Accept',
|
})}
|
||||||
})}
|
>
|
||||||
>
|
<FormControl fullWidth={true}>
|
||||||
<FormControl fullWidth={true}>
|
<FormControlLabel
|
||||||
<FormControlLabel
|
control={
|
||||||
control={
|
<Checkbox checked={model} onChange={handleOnChange} name="public" color="primary" />
|
||||||
<Checkbox
|
}
|
||||||
checked={model}
|
label={intl.formatMessage({
|
||||||
onChange={handleOnChange}
|
id: 'publish.checkbox',
|
||||||
name="public"
|
defaultMessage: 'Enable public sharing',
|
||||||
color="primary"
|
})}
|
||||||
/>
|
/>
|
||||||
}
|
</FormControl>
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'publish.checkbox',
|
|
||||||
defaultMessage: 'Enable public sharing',
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<div style={!model ? { visibility: 'hidden' } : {}}>
|
<div style={!model ? { visibility: 'hidden' } : {}}>
|
||||||
<TabContext value={activeTab}>
|
<TabContext value={activeTab}>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
<TabList onChange={handleTabChange}>
|
<TabList onChange={handleTabChange}>
|
||||||
<Tab
|
<Tab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'publish.public-url',
|
id: 'publish.public-url',
|
||||||
defaultMessage: 'Public URL',
|
defaultMessage: 'Public URL',
|
||||||
})}
|
})}
|
||||||
value="1"
|
value="1"
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'publish.embedded',
|
id: 'publish.embedded',
|
||||||
defaultMessage: 'Embedded',
|
defaultMessage: 'Embedded',
|
||||||
})}
|
})}
|
||||||
value="2"
|
value="2"
|
||||||
/>
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
</Box>
|
</Box>
|
||||||
<TabPanel value="2">
|
<TabPanel value="2">
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="publish.embedded-msg"
|
id="publish.embedded-msg"
|
||||||
defaultMessage="Copy this snippet of code to embed in your blog or page:"
|
defaultMessage="Copy this snippet of code to embed in your blog or page:"
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextareaAutosize
|
<TextareaAutosize
|
||||||
className={classes.textarea}
|
className={classes.textarea}
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
maxRows={6}
|
maxRows={6}
|
||||||
defaultValue={`<iframe style="width:600px;height:400px;border:1px solid black" src="${baseUrl}/c/maps/${mapId}/embed?zoom=1.0"></iframe>`}
|
defaultValue={`<iframe style="width:600px;height:400px;border:1px solid black" src="${baseUrl}/c/maps/${mapId}/embed?zoom=1.0"></iframe>`}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="1">
|
<TabPanel value="1">
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="publish.public-url-msg"
|
id="publish.public-url-msg"
|
||||||
defaultMessage="Copy and paste the link below to share your map with colleagues:"
|
defaultMessage="Copy and paste the link below to share your map with colleagues:"
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextareaAutosize
|
<TextareaAutosize
|
||||||
className={classes.textarea}
|
className={classes.textarea}
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
maxRows={1}
|
maxRows={1}
|
||||||
defaultValue={`${baseUrl}/c/maps/${mapId}/public`}
|
defaultValue={`${baseUrl}/c/maps/${mapId}/public`}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabContext>
|
</TabContext>
|
||||||
</div>
|
|
||||||
</BaseDialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</BaseDialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PublishDialog;
|
export default PublishDialog;
|
||||||
|
@ -10,104 +10,104 @@ import BaseDialog from '../base-dialog';
|
|||||||
import FormControl from '@mui/material/FormControl';
|
import FormControl from '@mui/material/FormControl';
|
||||||
|
|
||||||
export type RenameModel = {
|
export type RenameModel = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: RenameModel = { title: '', description: '', id: -1 };
|
const defaultModel: RenameModel = { title: '', description: '', id: -1 };
|
||||||
const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const service: Client = useSelector(activeInstance);
|
const service: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<RenameModel>(defaultModel);
|
const [model, setModel] = React.useState<RenameModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>(
|
const mutation = useMutation<RenameModel, ErrorInfo, RenameModel>(
|
||||||
(model: RenameModel) => {
|
(model: RenameModel) => {
|
||||||
const { id, ...rest } = model;
|
const { id, ...rest } = model;
|
||||||
return service.renameMap(id, rest).then(() => model);
|
return service.renameMap(id, rest).then(() => model);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
handleOnMutationSuccess(onClose, queryClient);
|
handleOnMutationSuccess(onClose, queryClient);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnClose = (): void => {
|
const handleOnClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
setModel(defaultModel);
|
setModel(defaultModel);
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
mutation.mutate(model);
|
mutation.mutate(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name;
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { map } = fetchMapById(mapId);
|
const { map } = fetchMapById(mapId);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
setModel(map);
|
setModel(map);
|
||||||
}
|
}
|
||||||
}, [mapId]);
|
}, [mapId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose}
|
onClose={handleOnClose}
|
||||||
onSubmit={handleOnSubmit}
|
onSubmit={handleOnSubmit}
|
||||||
error={error}
|
error={error}
|
||||||
title={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
title={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||||
description={intl.formatMessage({
|
description={intl.formatMessage({
|
||||||
id: 'rename.description',
|
id: 'rename.description',
|
||||||
defaultMessage: 'Please, fill the new map name and description.',
|
defaultMessage: 'Please, fill the new map name and description.',
|
||||||
})}
|
})}
|
||||||
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
submitButton={intl.formatMessage({ id: 'rename.title', defaultMessage: 'Rename' })}
|
||||||
>
|
>
|
||||||
<FormControl fullWidth={true}>
|
<FormControl fullWidth={true}>
|
||||||
<Input
|
<Input
|
||||||
name="title"
|
name="title"
|
||||||
type="text"
|
type="text"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'action.rename-name-placeholder',
|
id: 'action.rename-name-placeholder',
|
||||||
defaultMessage: 'Name',
|
defaultMessage: 'Name',
|
||||||
})}
|
})}
|
||||||
value={model.title}
|
value={model.title}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
error={error}
|
error={error}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="description"
|
name="description"
|
||||||
type="text"
|
type="text"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'action.rename-description-placeholder',
|
id: 'action.rename-description-placeholder',
|
||||||
defaultMessage: 'Description',
|
defaultMessage: 'Description',
|
||||||
})}
|
})}
|
||||||
value={model.description}
|
value={model.description}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
required={false}
|
required={false}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RenameDialog;
|
export default RenameDialog;
|
||||||
|
@ -26,247 +26,233 @@ import RoleIcon from '../../role-icon';
|
|||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
|
||||||
type ShareModel = {
|
type ShareModel = {
|
||||||
emails: string;
|
emails: string;
|
||||||
role: 'editor' | 'viewer';
|
role: 'editor' | 'viewer';
|
||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: ShareModel = { emails: '', role: 'editor', message: '' };
|
const defaultModel: ShareModel = { emails: '', role: 'editor', message: '' };
|
||||||
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [showMessage, setShowMessage] = React.useState<boolean>(false);
|
const [showMessage, setShowMessage] = React.useState<boolean>(false);
|
||||||
const [model, setModel] = React.useState<ShareModel>(defaultModel);
|
const [model, setModel] = React.useState<ShareModel>(defaultModel);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const deleteMutation = useMutation(
|
const deleteMutation = useMutation(
|
||||||
(email: string) => {
|
(email: string) => {
|
||||||
return client.deleteMapPermission(mapId, email);
|
return client.deleteMapPermission(mapId, email);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
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 ...
|
|
||||||
queryClient.invalidateQueries(`perm-${mapId}`);
|
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 => {
|
const addMutation = useMutation(
|
||||||
event.preventDefault();
|
(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 handleOnClose = (): void => {
|
||||||
const value = event.target.value;
|
// Invalidate cache ...
|
||||||
setModel({ ...model, [name as keyof ShareModel]: value });
|
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||||
event.stopPropagation();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnAddClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.stopPropagation();
|
event.preventDefault();
|
||||||
addMutation.mutate(model);
|
|
||||||
event.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnDeleteClick = (
|
const name = event.target.name;
|
||||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
const value = event.target.value;
|
||||||
email: string
|
setModel({ ...model, [name as keyof ShareModel]: value });
|
||||||
): void => {
|
event.stopPropagation();
|
||||||
event.stopPropagation();
|
};
|
||||||
deleteMutation.mutate(email);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(`perm-${mapId}`,
|
const handleOnAddClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||||
() => {
|
event.stopPropagation();
|
||||||
return client.fetchMapPermissions(mapId);
|
addMutation.mutate(model);
|
||||||
}
|
event.stopPropagation();
|
||||||
);
|
};
|
||||||
|
|
||||||
const formatName = (perm: Permission): string => {
|
const handleOnDeleteClick = (
|
||||||
return perm.name ? `${perm.name}<${perm.email}>` : perm.email;
|
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 { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(
|
||||||
const isValid = splitEmail(model.emails)
|
`perm-${mapId}`,
|
||||||
.every(str => /\S+@\S+\.\S+/.test((str || '')
|
() => {
|
||||||
.trim()));
|
return client.fetchMapPermissions(mapId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
const formatName = (perm: Permission): string => {
|
||||||
<div>
|
return perm.name ? `${perm.name}<${perm.email}>` : perm.email;
|
||||||
<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}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Select
|
// very basic email validation, just make sure the basic syntax is fine
|
||||||
variant="outlined"
|
const isValid = splitEmail(model.emails).every((str) => /\S+@\S+\.\S+/.test((str || '').trim()));
|
||||||
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>
|
|
||||||
|
|
||||||
<FormControlLabel
|
return (
|
||||||
value="start"
|
<div>
|
||||||
onChange={(event, value) => {
|
<BaseDialog
|
||||||
setShowMessage(value);
|
onClose={handleOnClose}
|
||||||
}}
|
title={intl.formatMessage({
|
||||||
style={{ fontSize: '5px' }}
|
id: 'share.delete-title',
|
||||||
control={<Checkbox color="primary" />}
|
defaultMessage: 'Share with people',
|
||||||
label={
|
})}
|
||||||
<Typography variant="subtitle2">
|
description={intl.formatMessage({
|
||||||
<FormattedMessage
|
id: 'share.delete-description',
|
||||||
id="share.add-message"
|
defaultMessage:
|
||||||
defaultMessage="Add message"
|
'Invite people to collaborate with you in the creation of your mindmap. They will be notified by email. ',
|
||||||
/>
|
})}
|
||||||
</Typography>
|
PaperProps={{ classes: { root: classes.paper } }}
|
||||||
}
|
error={error}
|
||||||
labelPlacement="end"
|
>
|
||||||
/>
|
<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
|
<Select
|
||||||
color="primary"
|
variant="outlined"
|
||||||
type="button"
|
onChange={handleOnChange}
|
||||||
variant="contained"
|
value={model.role}
|
||||||
disableElevation={true}
|
name="role"
|
||||||
onClick={handleOnAddClick}
|
style={{ margin: '0px 10px' }}
|
||||||
disabled={!isValid}
|
>
|
||||||
>
|
<MenuItem value="editor">
|
||||||
<FormattedMessage id="share.add-button" defaultMessage="Add" />
|
<FormattedMessage id="share.can-edit" defaultMessage="Can edit" />
|
||||||
</Button>
|
</MenuItem>
|
||||||
|
<MenuItem value="viewer">
|
||||||
|
<FormattedMessage id="share.can-view" defaultMessage="Can view" />
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
|
||||||
{showMessage && (
|
<FormControlLabel
|
||||||
<TextField
|
value="start"
|
||||||
multiline
|
onChange={(event, value) => {
|
||||||
rows={3}
|
setShowMessage(value);
|
||||||
maxRows={3}
|
}}
|
||||||
className={classes.textArea}
|
style={{ fontSize: '5px' }}
|
||||||
variant="filled"
|
control={<Checkbox color="primary" />}
|
||||||
name="message"
|
label={
|
||||||
onChange={handleOnChange}
|
<Typography variant="subtitle2">
|
||||||
value={model.message}
|
<FormattedMessage id="share.add-message" defaultMessage="Add message" />
|
||||||
label={intl.formatMessage({
|
</Typography>
|
||||||
id: 'share.message',
|
}
|
||||||
defaultMessage: 'Message',
|
labelPlacement="end"
|
||||||
})}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{!isLoading && (
|
<Button
|
||||||
<Paper elevation={1} className={classes.listPaper} variant="outlined">
|
color="primary"
|
||||||
<List>
|
type="button"
|
||||||
{permissions &&
|
variant="contained"
|
||||||
permissions.map((permission) => {
|
disableElevation={true}
|
||||||
return (
|
onClick={handleOnAddClick}
|
||||||
<ListItem
|
disabled={!isValid}
|
||||||
key={permission.email}
|
>
|
||||||
role={undefined}
|
<FormattedMessage id="share.add-button" defaultMessage="Add" />
|
||||||
dense
|
</Button>
|
||||||
button
|
|
||||||
>
|
|
||||||
<ListItemText
|
|
||||||
id={permission.email}
|
|
||||||
primary={formatName(permission)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<RoleIcon role={permission.role} />
|
{showMessage && (
|
||||||
<ListItemSecondaryAction>
|
<TextField
|
||||||
<Tooltip
|
multiline
|
||||||
title={
|
rows={3}
|
||||||
<FormattedMessage
|
maxRows={3}
|
||||||
id="share.delete"
|
className={classes.textArea}
|
||||||
defaultMessage="Delete collaborator"
|
variant="filled"
|
||||||
/>
|
name="message"
|
||||||
}
|
onChange={handleOnChange}
|
||||||
>
|
value={model.message}
|
||||||
<IconButton
|
label={intl.formatMessage({
|
||||||
edge="end"
|
id: 'share.message',
|
||||||
disabled={permission.role == 'owner'}
|
defaultMessage: 'Message',
|
||||||
onClick={(e) =>
|
})}
|
||||||
handleOnDeleteClick(e, permission.email)
|
/>
|
||||||
}
|
)}
|
||||||
size="large">
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemSecondaryAction>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</List>
|
|
||||||
</Paper>
|
|
||||||
)}
|
|
||||||
</BaseDialog>
|
|
||||||
</div>
|
</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;
|
export default ShareDialog;
|
||||||
|
@ -14,92 +14,85 @@ import ListItemIcon from '@mui/material/ListItemIcon';
|
|||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
|
||||||
const HelpMenu = (): React.ReactElement => {
|
const HelpMenu = (): React.ReactElement => {
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow={true}
|
arrow={true}
|
||||||
title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}
|
title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}
|
||||||
>
|
>
|
||||||
<IconButton aria-haspopup="true" onClick={handleMenu} size="large">
|
<IconButton aria-haspopup="true" onClick={handleMenu} size="large">
|
||||||
<Help />
|
<Help />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu
|
<Menu
|
||||||
id="appbar-profile"
|
id="appbar-profile"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'bottom',
|
vertical: 'bottom',
|
||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<Link
|
<Link
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
href="https://www.wisemapping.com/termsofuse.html"
|
href="https://www.wisemapping.com/termsofuse.html"
|
||||||
target="help"
|
target="help"
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<PolicyOutlined fontSize="small" />
|
<PolicyOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage
|
<FormattedMessage id="footer.termsandconditions" defaultMessage="Term And Conditions" />
|
||||||
id="footer.termsandconditions"
|
</Link>
|
||||||
defaultMessage="Term And Conditions"
|
</MenuItem>
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<Link color="textSecondary" href="mailto:team@wisemapping.com">
|
<Link color="textSecondary" href="mailto:team@wisemapping.com">
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<EmailOutlined fontSize="small" />
|
<EmailOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
<FormattedMessage id="footer.contactus" defaultMessage="Contact Us" />
|
||||||
</Link>
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<Link color="textSecondary" href="mailto:feedback@wisemapping.com">
|
<Link color="textSecondary" href="mailto:feedback@wisemapping.com">
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<FeedbackOutlined fontSize="small" />
|
<FeedbackOutlined fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
<FormattedMessage id="footer.feedback" defaultMessage="Feedback" />
|
||||||
</Link>
|
</Link>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem onClick={handleClose}>
|
<MenuItem onClick={handleClose}>
|
||||||
<Link
|
<Link color="textSecondary" href="https://www.wisemapping.com/aboutus.html" target="help">
|
||||||
color="textSecondary"
|
<ListItemIcon>
|
||||||
href="https://www.wisemapping.com/aboutus.html"
|
<EmojiPeopleOutlined fontSize="small" />
|
||||||
target="help"
|
</ListItemIcon>
|
||||||
>
|
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
||||||
<ListItemIcon>
|
</Link>
|
||||||
<EmojiPeopleOutlined fontSize="small" />
|
</MenuItem>
|
||||||
</ListItemIcon>
|
</Menu>
|
||||||
<FormattedMessage id="footer.aboutus" defaultMessage="About Us" />
|
</span>
|
||||||
</Link>
|
);
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default HelpMenu;
|
export default HelpMenu;
|
||||||
|
@ -18,134 +18,133 @@ import DialogActions from '@mui/material/DialogActions';
|
|||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
|
|
||||||
const LanguageMenu = (): React.ReactElement => {
|
const LanguageMenu = (): React.ReactElement => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
const [openHelpDialog, setHelpDialogOpen] = React.useState<boolean>(false);
|
const [openHelpDialog, setHelpDialogOpen] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const mutation = useMutation((locale: LocaleCode) => client.updateAccountLanguage(locale), {
|
const mutation = useMutation((locale: LocaleCode) => client.updateAccountLanguage(locale), {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries('account');
|
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();
|
handleClose();
|
||||||
},
|
setHelpDialogOpen(true);
|
||||||
onError: (error) => {
|
}}
|
||||||
console.error(`Unexpected error ${error}`);
|
>
|
||||||
},
|
<FormattedMessage id="language.help" defaultMessage="Help to Translate" />
|
||||||
});
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
{openHelpDialog && <HelpUsToTranslateDialog onClose={() => setHelpDialogOpen(false)} />}
|
||||||
setAnchorEl(event.currentTarget);
|
</span>
|
||||||
};
|
);
|
||||||
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type HelpUsToTranslateDialogProp = {
|
type HelpUsToTranslateDialogProp = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
const HelpUsToTranslateDialog = ({ onClose }: HelpUsToTranslateDialogProp) => {
|
const HelpUsToTranslateDialog = ({ onClose }: HelpUsToTranslateDialogProp) => {
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onClose={onClose}>
|
<Dialog open={true} onClose={onClose}>
|
||||||
<DialogTitle>Help us to support more languages !</DialogTitle>
|
<DialogTitle>Help us to support more languages !</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
We need your help !. If you are interested, send us an email to
|
We need your help !. If you are interested, send us an email to team@wisemapping.com.
|
||||||
team@wisemapping.com.
|
</DialogContentText>
|
||||||
</DialogContentText>
|
</DialogContent>
|
||||||
</DialogContent>
|
<DialogActions>
|
||||||
<DialogActions>
|
<Button autoFocus onClick={onClose}>
|
||||||
<Button autoFocus onClick={onClose}>
|
Close
|
||||||
Close
|
</Button>
|
||||||
</Button>
|
</DialogActions>
|
||||||
</DialogActions>
|
</Dialog>
|
||||||
</Dialog>
|
);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LanguageMenu;
|
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';
|
import { Label } from '../../../../classes/client';
|
||||||
|
|
||||||
export type LabelDeleteConfirmType = {
|
export type LabelDeleteConfirmType = {
|
||||||
label: Label;
|
label: Label;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LabelDeleteConfirm = ({ label, onClose, onConfirm }: LabelDeleteConfirmType): React.ReactElement => {
|
const LabelDeleteConfirm = ({
|
||||||
const intl = useIntl();
|
label,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
}: LabelDeleteConfirmType): React.ReactElement => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onSubmit={onConfirm}
|
onSubmit={onConfirm}
|
||||||
title={intl.formatMessage({ id: 'label.delete-title', defaultMessage: 'Confirm label deletion' })}
|
title={intl.formatMessage({
|
||||||
submitButton={intl.formatMessage({
|
id: 'label.delete-title',
|
||||||
id: 'action.delete-title',
|
defaultMessage: 'Confirm label deletion',
|
||||||
defaultMessage: 'Delete',
|
})}
|
||||||
})}
|
submitButton={intl.formatMessage({
|
||||||
>
|
id: 'action.delete-title',
|
||||||
<Alert severity="warning">
|
defaultMessage: 'Delete',
|
||||||
<AlertTitle>{intl.formatMessage({ id: 'label.delete-title', defaultMessage: 'Confirm label deletion' })}</AlertTitle>
|
})}
|
||||||
<span>
|
>
|
||||||
<Typography fontWeight="bold" component="span">{label.title} </Typography>
|
<Alert severity="warning">
|
||||||
<FormattedMessage
|
<AlertTitle>
|
||||||
id="label.delete-description"
|
{intl.formatMessage({
|
||||||
defaultMessage="will be deleted, including its associations to all existing maps. Do you want to continue?"
|
id: 'label.delete-title',
|
||||||
/>
|
defaultMessage: 'Confirm label deletion',
|
||||||
</span>
|
})}
|
||||||
</Alert>
|
</AlertTitle>
|
||||||
</BaseDialog>
|
<span>
|
||||||
</div>
|
<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;
|
export default LabelDeleteConfirm;
|
||||||
|
@ -12,44 +12,44 @@ import AddLabelDialog from '../../action-dispatcher/add-label-dialog';
|
|||||||
import { LabelListContainer } from './styled';
|
import { LabelListContainer } from './styled';
|
||||||
|
|
||||||
export type LabelSelectorProps = {
|
export type LabelSelectorProps = {
|
||||||
maps: MapInfo[];
|
maps: MapInfo[];
|
||||||
onChange: (label: Label, checked: boolean) => void;
|
onChange: (label: Label, checked: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LabelSelector({ onChange, maps }: LabelSelectorProps): React.ReactElement {
|
export function LabelSelector({ onChange, maps }: LabelSelectorProps): React.ReactElement {
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const { data: labels = [] } = useQuery<unknown, ErrorInfo, Label[]>('labels', async () =>
|
const { data: labels = [] } = useQuery<unknown, ErrorInfo, Label[]>('labels', async () =>
|
||||||
client.fetchLabels()
|
client.fetchLabels(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const checkedLabelIds = labels
|
const checkedLabelIds = labels
|
||||||
.map((l) => l.id)
|
.map((l) => l.id)
|
||||||
.filter((labelId) => maps.every((m) => m.labels.find((l) => l.id === labelId)));
|
.filter((labelId) => maps.every((m) => m.labels.find((l) => l.id === labelId)));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<AddLabelDialog onAdd={(label) => onChange(label, true)} />
|
<AddLabelDialog onAdd={(label) => onChange(label, true)} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<LabelListContainer>
|
<LabelListContainer>
|
||||||
{labels.map(({ id, title, color }) => (
|
{labels.map(({ id, title, color }) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={id}
|
key={id}
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${id}`}
|
id={`${id}`}
|
||||||
checked={checkedLabelIds.includes(id)}
|
checked={checkedLabelIds.includes(id)}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange({ id, title, color }, e.target.checked);
|
onChange({ id, title, color }, e.target.checked);
|
||||||
}}
|
}}
|
||||||
name={title}
|
name={title}
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={<LabelComponent label={{ id, title, color }} size="big" />}
|
label={<LabelComponent label={{ id, title, color }} size="big" />}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</LabelListContainer>
|
</LabelListContainer>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,33 +6,40 @@ import LabelTwoTone from '@mui/icons-material/LabelTwoTone';
|
|||||||
import DeleteIcon from '@mui/icons-material/Clear';
|
import DeleteIcon from '@mui/icons-material/Clear';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
|
||||||
|
|
||||||
type LabelSize = 'small' | 'big';
|
type LabelSize = 'small' | 'big';
|
||||||
type LabelComponentProps = { label: Label; onDelete?: (label: Label) => void; size?: LabelSize };
|
type LabelComponentProps = { label: Label; onDelete?: (label: Label) => void; size?: LabelSize };
|
||||||
|
|
||||||
export default function LabelComponent({ label, onDelete, size = 'small' }: LabelComponentProps): React.ReactElement<LabelComponentProps> {
|
export default function LabelComponent({
|
||||||
const iconSize = size === 'small' ? {
|
label,
|
||||||
height: '0.6em', width: '0.6em'
|
onDelete,
|
||||||
} : { height: '0.9em', width: '0.9em' };
|
size = 'small',
|
||||||
|
}: LabelComponentProps): React.ReactElement<LabelComponentProps> {
|
||||||
|
const iconSize =
|
||||||
|
size === 'small'
|
||||||
|
? {
|
||||||
|
height: '0.6em',
|
||||||
|
width: '0.6em',
|
||||||
|
}
|
||||||
|
: { height: '0.9em', width: '0.9em' };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LabelContainer color={label.color}>
|
<LabelContainer color={label.color}>
|
||||||
<LabelTwoTone htmlColor={label.color} style={iconSize} />
|
<LabelTwoTone htmlColor={label.color} style={iconSize} />
|
||||||
<LabelText>{label.title}</LabelText>
|
<LabelText>{label.title}</LabelText>
|
||||||
{onDelete && (
|
{onDelete && (
|
||||||
<IconButton
|
<IconButton
|
||||||
color="default"
|
color="default"
|
||||||
size="small"
|
size="small"
|
||||||
aria-label="delete tag"
|
aria-label="delete tag"
|
||||||
component="span"
|
component="span"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onDelete(label);
|
onDelete(label);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteIcon style={iconSize} />
|
<DeleteIcon style={iconSize} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</LabelContainer>
|
</LabelContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,22 @@ import DeleteIcon from '@mui/icons-material/Clear';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
labels: Label[],
|
labels: Label[];
|
||||||
onDelete: (label: Label) => void,
|
onDelete: (label: Label) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LabelsCell({ labels, onDelete }: Props): React.ReactElement<Props> {
|
export function LabelsCell({ labels, onDelete }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{labels.map(label => (
|
{labels.map((label) => (
|
||||||
<LabelContainer
|
<LabelContainer key={label.id} color={label.color}>
|
||||||
key={label.id}
|
|
||||||
color={label.color}
|
|
||||||
>
|
|
||||||
<LabelTwoTone htmlColor={label.color} style={{ height: '0.6em', width: '0.6em' }} />
|
<LabelTwoTone htmlColor={label.color} style={{ height: '0.6em', width: '0.6em' }} />
|
||||||
<LabelText>{ label.title }</LabelText>
|
<LabelText>{label.title}</LabelText>
|
||||||
<IconButton color="default" size='small' aria-label="delete tag" component="span"
|
<IconButton
|
||||||
|
color="default"
|
||||||
|
size="small"
|
||||||
|
aria-label="delete tag"
|
||||||
|
component="span"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onDelete(label);
|
onDelete(label);
|
||||||
@ -29,7 +30,7 @@ export function LabelsCell({ labels, onDelete }: Props): React.ReactElement<Prop
|
|||||||
>
|
>
|
||||||
<DeleteIcon style={{ height: '0.6em', width: '0.6em' }} />
|
<DeleteIcon style={{ height: '0.6em', width: '0.6em' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</LabelContainer>
|
</LabelContainer>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -9,40 +9,31 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import { Role } from '../../../classes/client';
|
import { Role } from '../../../classes/client';
|
||||||
|
|
||||||
type RoleIconProps = {
|
type RoleIconProps = {
|
||||||
role: Role;
|
role: Role;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoleIcon = ({ role }: RoleIconProps): React.ReactElement => {
|
const RoleIcon = ({ role }: RoleIconProps): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{role == 'owner' && (
|
{role == 'owner' && (
|
||||||
<Tooltip
|
<Tooltip title={<FormattedMessage id="role.owner" defaultMessage="Owner" />} arrow={true}>
|
||||||
title={<FormattedMessage id="role.owner" defaultMessage="Owner" />}
|
<PersonSharpIcon />
|
||||||
arrow={true}
|
</Tooltip>
|
||||||
>
|
)}
|
||||||
<PersonSharpIcon />
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{role == 'editor' && (
|
{role == 'editor' && (
|
||||||
<Tooltip
|
<Tooltip title={<FormattedMessage id="role.editor" defaultMessage="Editor" />} arrow={true}>
|
||||||
title={<FormattedMessage id="role.editor" defaultMessage="Editor" />}
|
<EditSharpIcon />
|
||||||
arrow={true}
|
</Tooltip>
|
||||||
>
|
)}
|
||||||
<EditSharpIcon />
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{role == 'viewer' && (
|
{role == 'viewer' && (
|
||||||
<Tooltip
|
<Tooltip title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />} arrow={true}>
|
||||||
title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />}
|
<VisibilitySharpIcon />
|
||||||
arrow={true}
|
</Tooltip>
|
||||||
>
|
)}
|
||||||
<VisibilitySharpIcon />
|
</span>
|
||||||
</Tooltip>
|
);
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RoleIcon;
|
export default RoleIcon;
|
||||||
|
@ -20,161 +20,165 @@ import AppConfig from '../../classes/app-config';
|
|||||||
import ReactGA from 'react-ga4';
|
import ReactGA from 'react-ga4';
|
||||||
|
|
||||||
export type Model = {
|
export type Model = {
|
||||||
email: string;
|
email: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
password: string;
|
password: string;
|
||||||
recaptcha: string;
|
recaptcha: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultModel: Model = { email: '', lastname: '', firstname: '', password: '', recaptcha: '' };
|
const defaultModel: Model = { email: '', lastname: '', firstname: '', password: '', recaptcha: '' };
|
||||||
const RegistrationForm = () => {
|
const RegistrationForm = () => {
|
||||||
const [model, setModel] = useState<Model>(defaultModel);
|
const [model, setModel] = useState<Model>(defaultModel);
|
||||||
const [error, setError] = useState<ErrorInfo>();
|
const [error, setError] = useState<ErrorInfo>();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const [captcha, setCaptcha] = useState<any>();
|
const [captcha, setCaptcha] = useState<any>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const Client: Client = useSelector(activeInstance);
|
const Client: Client = useSelector(activeInstance);
|
||||||
const mutation = useMutation<void, ErrorInfo, Model>(
|
const mutation = useMutation<void, ErrorInfo, Model>(
|
||||||
(model: Model) => Client.registerNewUser({ ...model }),
|
(model: Model) => Client.registerNewUser({ ...model }),
|
||||||
{
|
{
|
||||||
onSuccess: () => history.push('/c/registration-success'),
|
onSuccess: () => history.push('/c/registration-success'),
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setError(error);
|
setError(error);
|
||||||
captcha.reset();
|
captcha.reset();
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
mutation.mutate(model);
|
mutation.mutate(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const name = event.target.name;
|
const name = event.target.name;
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
setModel({ ...model, [name as keyof Model]: value });
|
setModel({ ...model, [name as keyof Model]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage id="registration.title" defaultMessage="Become a member" />
|
<FormattedMessage id="registration.title" defaultMessage="Become a member" />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="registration.desc"
|
id="registration.desc"
|
||||||
defaultMessage="Signing up is free and just take a moment "
|
defaultMessage="Signing up is free and just take a moment "
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<form onSubmit={handleOnSubmit}>
|
<form onSubmit={handleOnSubmit}>
|
||||||
<GlobalError error={error} />
|
<GlobalError error={error} />
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'registration.email',
|
id: 'registration.email',
|
||||||
defaultMessage: 'Email',
|
defaultMessage: 'Email',
|
||||||
})}
|
})}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="firstname"
|
name="firstname"
|
||||||
type="text"
|
type="text"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'registration.firstname',
|
id: 'registration.firstname',
|
||||||
defaultMessage: 'First Name',
|
defaultMessage: 'First Name',
|
||||||
})}
|
})}
|
||||||
autoComplete="given-name"
|
autoComplete="given-name"
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="lastname"
|
name="lastname"
|
||||||
type="text"
|
type="text"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'registration.lastname',
|
id: 'registration.lastname',
|
||||||
defaultMessage: 'Last Name',
|
defaultMessage: 'Last Name',
|
||||||
})}
|
})}
|
||||||
autoComplete="family-name"
|
autoComplete="family-name"
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'registration.password',
|
id: 'registration.password',
|
||||||
defaultMessage: 'Password',
|
defaultMessage: 'Password',
|
||||||
})}
|
})}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{AppConfig.isRecaptcha2Enabled() &&
|
{AppConfig.isRecaptcha2Enabled() && (
|
||||||
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>
|
<div style={{ width: '330px', padding: '5px 0px 5px 20px' }}>
|
||||||
<ReCAPTCHA
|
<ReCAPTCHA
|
||||||
ref={el => setCaptcha(el)}
|
ref={(el) => setCaptcha(el)}
|
||||||
sitekey={AppConfig.getRecaptcha2SiteKey()}
|
sitekey={AppConfig.getRecaptcha2SiteKey()}
|
||||||
onChange={(value: string) => {
|
onChange={(value: string) => {
|
||||||
model.recaptcha = value;
|
model.recaptcha = value;
|
||||||
setModel(model);
|
setModel(model);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
<div style={{ fontSize: '12px', padding: '10px 0px' }}>
|
<div style={{ fontSize: '12px', padding: '10px 0px' }}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="registration.termandconditions"
|
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"
|
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>
|
</div>
|
||||||
|
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
value={intl.formatMessage({
|
value={intl.formatMessage({
|
||||||
id: 'registration.register',
|
id: 'registration.register',
|
||||||
defaultMessage: 'Register',
|
defaultMessage: 'Register',
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RegistationPage = (): React.ReactElement => {
|
const RegistationPage = (): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = intl.formatMessage({
|
document.title = intl.formatMessage({
|
||||||
id: 'registration.page-title',
|
id: 'registration.page-title',
|
||||||
defaultMessage: 'Registration | WiseMapping',
|
defaultMessage: 'Registration | WiseMapping',
|
||||||
});
|
});
|
||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Registration:Init' });
|
ReactGA.send({
|
||||||
}, []);
|
hitType: 'pageview',
|
||||||
|
page: window.location.pathname,
|
||||||
|
title: 'Registration:Init',
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type="only-signin" />
|
<Header type="only-signin" />
|
||||||
<RegistrationForm />
|
<RegistrationForm />
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RegistationPage;
|
export default RegistationPage;
|
||||||
|
@ -8,47 +8,53 @@ import Typography from '@mui/material/Typography';
|
|||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import ReactGA from 'react-ga4';
|
import ReactGA from 'react-ga4';
|
||||||
|
|
||||||
|
|
||||||
const RegistrationSuccessPage = (): React.ReactElement => {
|
const RegistrationSuccessPage = (): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = intl.formatMessage({ id: 'registation.success-title', defaultMessage: 'Registation Success | WiseMapping' });
|
document.title = intl.formatMessage({
|
||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Registration:Success' });
|
id: 'registation.success-title',
|
||||||
|
defaultMessage: 'Registation Success | WiseMapping',
|
||||||
});
|
});
|
||||||
|
ReactGA.send({
|
||||||
|
hitType: 'pageview',
|
||||||
|
page: window.location.pathname,
|
||||||
|
title: 'Registration:Success',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header type="none" />
|
<Header type="none" />
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<Typography variant="h4" component="h1">
|
<Typography variant="h4" component="h1">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="resetpassword.success.title"
|
id="resetpassword.success.title"
|
||||||
defaultMessage="Your account has been created successfully"
|
defaultMessage="Your account has been created successfully"
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography paragraph>
|
<Typography paragraph>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="registration.success.desc"
|
id="registration.success.desc"
|
||||||
defaultMessage="Click 'Sign In' button below and start creating mind maps."
|
defaultMessage="Click 'Sign In' button below and start creating mind maps."
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to="/c/login"
|
to="/c/login"
|
||||||
disableElevation={true}
|
disableElevation={true}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
<FormattedMessage id="login.signin" defaultMessage="Sign In" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RegistrationSuccessPage;
|
export default RegistrationSuccessPage;
|
||||||
|
@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
|
|||||||
import App from './app';
|
import App from './app';
|
||||||
|
|
||||||
async function bootstrapApplication() {
|
async function bootstrapApplication() {
|
||||||
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
|
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrapApplication();
|
bootstrapApplication();
|
||||||
|
Loading…
Reference in New Issue
Block a user