diff --git a/README.md b/README.md index 1d7ca0ebb8a506cc7d6c82fff6323c74067da506..8b8325669da89b027e511f5cff1070bc5ae27c79 100644 --- a/README.md +++ b/README.md @@ -28,67 +28,18 @@ from 8080, then run: Where the `pem` and `key` files are a pem encoded certificate and key. -## How to generate server.pem and server.key pair - -In order to generate a pair of pem and key use the following command using openssl. - -`openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout server.key -out server.pem` - -Note that a self signed certificate will be rejected from opendht, -QNetworkAccessManager and other curl-like program by default. The following -changes should disable those checks. - -```diff -src/http.cpp | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/http.cpp b/src/http.cpp -index 6b7d4aad..f10a4138 100644 ---- a/src/http.cpp -+++ b/src/http.cpp -@@ -111,7 +111,7 @@ std::shared_ptr<asio::ssl::context> - newTlsClientContext(const std::shared_ptr<dht::Logger>& logger) - { - auto ctx = std::make_shared<asio::ssl::context>(asio::ssl::context::tls_client); -- ctx->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert); -+ ctx->set_verify_mode(asio::ssl::verify_peer); - - if (char* path = getenv("CA_ROOT_FILE")) { - if (logger) -@@ -160,7 +160,7 @@ Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certi - asio::error_code ec; - if (server_ca) { - ssl_ctx_ = std::make_shared<asio::ssl::context>(asio::ssl::context::tls_client); -- ssl_ctx_->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert); -+ ssl_ctx_->set_verify_mode(asio::ssl::verify_peer); - auto ca = server_ca->toString(false/*chain*/); - ssl_ctx_->add_certificate_authority(asio::const_buffer{ca.data(), ca.size()}, ec); - if (ec) -@@ -488,6 +488,7 @@ Connection::set_ssl_verification(const std::string& hostname, const asio::ssl::v - ssl_socket_->asio_ssl_stream().set_verify_callback([ - id = id_, logger = logger_, hostname, checkOcsp = checkOcsp_ - ] (bool preverified, asio::ssl::verify_context& ctx) -> bool { -+ return true; - X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); - if (logger) { - char subject_name[1024]; -@@ -1218,7 +1219,7 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb) - conn_ = std::make_shared<Connection>(ctx_, server_ca_, client_identity_, logger_); - else - conn_ = std::make_shared<Connection>(ctx_, true/*ssl*/, logger_); -- conn_->set_ssl_verification(get_url().host, asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert); -+ conn_->set_ssl_verification(get_url().host, asio::ssl::verify_peer); - } - else - conn_ = std::make_shared<Connection>(ctx_, false/*ssl*/, logger_); +## How to generate a trusted server.pem and server.key pair -``` +In order to test JAMS with the Jami client, you need to generate a locally-trusted development certificate. Here is how to do it using `mkcert`: + +First follow the mkcert [installation guide](https://github.com/FiloSottile/mkcert?tab=readme-ov-file#installation). -```cpp -// jami-client-qt/src/app/networkmanager.cpp NetworkManager::NetworkManager -QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); -sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone); -QSslConfiguration::setDefaultConfiguration(sslConfig); +Then run the following commands: + +``` +mkcert -install # Creates a local certificate authority (CA). This step is required only once. +mkcert -key-file server.key -cert-file server.pem localhost 127.0.0.1 ::1 +cp server.key server.pem /path/to/jami-jams/jams/ ``` ## Run with the debugger enabled diff --git a/jams-react-client/documentation/assets/css/material-dashboard.css b/jams-react-client/documentation/assets/css/material-dashboard.css index 1e305499b45bf33a237fba45199414b2d26c3a85..3123b5cea73b7bba2774cd7233d7f28c29f84625 100644 --- a/jams-react-client/documentation/assets/css/material-dashboard.css +++ b/jams-react-client/documentation/assets/css/material-dashboard.css @@ -2502,7 +2502,7 @@ h6, .h2, .h3, .h4 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-family: "Ubuntu", sans-serif; font-weight: 300; line-height: 1.5em; } diff --git a/jams-react-client/package-lock.json b/jams-react-client/package-lock.json index 487758509f8d0198655d2be747259ea8a9cd15c8..9ff5641f6365e2872b5ca53bd1948c2815a906e0 100644 --- a/jams-react-client/package-lock.json +++ b/jams-react-client/package-lock.json @@ -38,6 +38,7 @@ "react-i18next": "^11.7.3", "react-image-file-resizer": "^0.3.8", "react-router-dom": "5.2.0", + "ubuntu-fontface": "^0.1.13", "yup": "^1.2.0" }, "devDependencies": { @@ -26886,6 +26887,11 @@ "node": ">=4.2.0" } }, + "node_modules/ubuntu-fontface": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ubuntu-fontface/-/ubuntu-fontface-0.1.13.tgz", + "integrity": "sha512-4gJ4uL5mzpWJ1F0xO+FWsB0r7FvDSxdo2771xrXT1s+gKN3d/EPzb2Bybu3shNPF3/Bd6QPX0z9szZlq6p2+EA==" + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/jams-react-client/package.json b/jams-react-client/package.json index dc9e5ab09747228317664102a03fa5b3f49f087f..9c5f9750e800c92f329f357661bd343a6edd8100 100644 --- a/jams-react-client/package.json +++ b/jams-react-client/package.json @@ -34,6 +34,7 @@ "react-i18next": "^11.7.3", "react-image-file-resizer": "^0.3.8", "react-router-dom": "5.2.0", + "ubuntu-fontface": "^0.1.13", "yup": "^1.2.0" }, "scripts": { diff --git a/jams-react-client/public/index.html b/jams-react-client/public/index.html index 8ff5b3236aea32de1eb9cd3e8bb81ad7ba59e643..025cfe1e6c38fad0b364e9b527c63969e41cdf26 100644 --- a/jams-react-client/public/index.html +++ b/jams-react-client/public/index.html @@ -20,44 +20,26 @@ --> <!DOCTYPE html> <html lang="en"> - <head> - <meta charset="utf-8" /> - <meta - name="viewport" - content="width=device-width, initial-scale=1, shrink-to-fit=no" - /> - <meta name="theme-color" content="#000000" /> - <!-- + +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> + <meta name="theme-color" content="#000000" /> + <!-- manifest.json provides metadata used when your web app is added to the homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ --> - <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> - <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> - <link - rel="apple-touch-icon" - sizes="76x76" - href="%PUBLIC_URL%/apple-icon.png" - /> - <link - rel="stylesheet" - href="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.css" - /> + <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> + <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> + <link rel="apple-touch-icon" sizes="76x76" href="%PUBLIC_URL%/apple-icon.png" /> + <link rel="stylesheet" href="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.css" /> - <script src="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script> - <link - rel="stylesheet" - href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" - /> - <link - href="https://fonts.googleapis.com/icon?family=Material+Icons" - rel="stylesheet" - /> - <link - rel="stylesheet" - href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" - /> + <script src="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script> + <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" /> + <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> + <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" /> - <!-- + <!-- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. @@ -66,12 +48,13 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - <title>JAMS - Jami Account Management Server</title> - </head> - <body> - <noscript> You need to enable JavaScript to run this app. </noscript> - <div id="root"></div> - <!-- + <title>JAMS - Jami Account Management Server</title> +</head> + +<body> + <noscript> You need to enable JavaScript to run this app. </noscript> + <div id="root"></div> + <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. @@ -81,5 +64,6 @@ To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> - </body> -</html> +</body> + +</html> \ No newline at end of file diff --git a/jams-react-client/src/assets/css/material-dashboard-react.css b/jams-react-client/src/assets/css/material-dashboard-react.css index 3a7ad4bb4d044fd8fe84765648018a5e1174fb6c..42ef9b3fd3051b75b37ea43b04db3ef99890fd21 100644 --- a/jams-react-client/src/assets/css/material-dashboard-react.css +++ b/jams-react-client/src/assets/css/material-dashboard-react.css @@ -65,7 +65,7 @@ body { background-color: #eeeeee; color: #3c4858; margin: 0; - font-family: Roboto, Helvetica, Arial, sans-serif; + font-family: "Ubuntu", sans-serif; font-weight: 300; line-height: 1.5em; } @@ -127,9 +127,9 @@ h3, h4, h5, h6 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-family: "Ubuntu", sans-serif; font-weight: 300; - line-height: 1.5em; + line-height: 1em; } a { @@ -246,7 +246,7 @@ footer ul li a:hover { } } .fixed-plugin { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-family: "Ubuntu", sans-serif; font-weight: 300; line-height: 1.5em; position: fixed; diff --git a/jams-react-client/src/assets/jss/material-dashboard-react.js b/jams-react-client/src/assets/jss/material-dashboard-react.js index e4262636d706067ef44aa7724a3a76b60dbc5fff..534f582daba53c163d985ac09c955229c87b71a1 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react.js @@ -65,7 +65,7 @@ const container = { }; const defaultFont = { - fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', + fontFamily: '"Ubuntu"', fontWeight: "300", lineHeight: "1.5em", }; @@ -74,7 +74,7 @@ const primaryColor = ["#ffffff", "#ab47bc", "#8e24aa", "#af2cc5"]; const warningColor = ["#ff9800", "#ffa726", "#fb8c00", "#ffa21a"]; const dangerColor = ["#f44336", "#ef5350", "#e53935", "#f55a4e"]; const successColor = ["#4caf50", "#66bb6a", "#43a047", "#5cb860"]; -const infoColor = ["#005699", "#005699", "#005699", "#00d3ee"]; +const infoColor = ["#005699", "#005699", "#005699", "#003c6b"]; const roseColor = ["#e91e63", "#ec407a", "#d81b60", "#eb3573"]; const grayColor = [ "#999", @@ -231,7 +231,7 @@ const title = { marginTop: "30px", marginBottom: "25px", minHeight: "32px", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", "& small": { color: grayColor[1], fontWeight: "400", diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/buttonStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/buttonStyle.js index 2737b6367a29f4c7b41864f84709a33ca219a8d5..b0aa08a9bfc1e511695354e7bd66606eb9057c8a 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/buttonStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/buttonStyle.js @@ -140,6 +140,7 @@ const buttonStyle = { }, info: { backgroundColor: infoColor[0], + color: whiteColor, boxShadow: "0 2px 2px 0 rgba(" + hexToRgb(infoColor[0]) + @@ -149,7 +150,8 @@ const buttonStyle = { hexToRgb(infoColor[0]) + ", 0.12)", "&:hover,&:focus": { - backgroundColor: infoColor[0], + backgroundColor: infoColor[3], + color: whiteColor, boxShadow: "0 14px 26px -12px rgba(" + hexToRgb(infoColor[0]) + diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardBodyStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardBodyStyle.js index 38f4856c2601b8bd366dcfd751ab7de793311545..cea3ad5d27150cb7bdd97c21c613cf3a52ea93e9 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardBodyStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardBodyStyle.js @@ -1,6 +1,6 @@ const cardBodyStyle = { cardBody: { - padding: "0.9375rem 20px", + padding: "10px", flex: "1 1 auto", WebkitBoxFlex: "1", position: "relative", diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardFooterStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardFooterStyle.js index b3554ac2eb83642f90566223e3796e926fa27f6b..9b3facb5c8f00a840ad2961fdb301d80ac068d92 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardFooterStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardFooterStyle.js @@ -3,10 +3,9 @@ import { grayColor } from "assets/jss/material-dashboard-react.js"; const cardFooterStyle = (theme) => ({ cardFooter: { padding: "0", - paddingTop: "10px", - margin: "0 15px 10px", + margin: "0 10px 10px 10px", borderRadius: "0", - justifyContent: "space-between", + justifyContent: "end", // alignItems: "center", display: "flex", backgroundColor: "transparent", diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js index 833147824c0af31dc1768152611631ad5e2ab811..31ffd64772abc4110f1e76485532b93026781faf 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js @@ -20,6 +20,7 @@ const cardStyle = { minWidth: "0", wordWrap: "break-word", fontSize: ".875rem", + justifyContent: "space-between", "& ul": { "list-style": "none", padding: 0, diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js index e3ef2f6f73bc7f9be411bafdebb62934c53d68a1..424d3acee2dd0bdfe47447211156bb8f395d7c12 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/devicesStyle.js @@ -18,10 +18,7 @@ const tasksStyle = { borderBottom: "1px solid " + grayColor[5], }, tableActions: { - display: "flex", - border: "none", padding: "12px 8px !important", - verticalAlign: "middle", }, tableCell: { ...defaultFont, @@ -30,6 +27,7 @@ const tasksStyle = { lineHeight: "1.42857143", fontSize: "14px", textAlign: "left", + wordBreak: "break-all", }, tableCellRTL: { textAlign: "right", diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/footerStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/footerStyle.js index b6663987bf711418a6abaf68553fe87e6e12d756..b4730f494734d2d90b0fb7b68516b913d0f3d00e 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/footerStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/footerStyle.js @@ -23,15 +23,15 @@ const footerStyle = { display: "block", }, right: { - paddingTop: "15px", + paddingTop: "10px", margin: "0", - fontSize: "14px", + fontSize: "12px", float: "right!important", }, footer: { bottom: "0", borderTop: "1px solid " + grayColor[11], - padding: "15px 0", + padding: "10px 0", ...defaultFont, }, container, diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js index 8b241a8483cebeb0c1ea97e30aab59a647beac9c..63ae9eeff2401153bcc88e60bcf5d712408069ab 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js @@ -20,7 +20,7 @@ const headerStyle = (theme) => ({ position: "absolute", width: "50%", [theme.breakpoints.down("lg")]: { - width: "130px", + width: "auto", }, [theme.breakpoints.up("md")]: { display: "none", diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/sidebarStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/sidebarStyle.js index c2202b68dbb20fb10facdea3e08f82a6252c1e0c..a7466c7ff206aa3bc9977aee1e5d95bb8cca620a 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/sidebarStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/sidebarStyle.js @@ -137,8 +137,10 @@ const sidebarStyle = (theme) => ({ listStyle: "none", position: "fixed", bottom: "0", - left: "0", width: drawerWidth, + [theme.breakpoints.up("md")]: { + left: "0", + }, }, item: { position: "relative", diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/views/dashboardStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/views/dashboardStyle.js index 42eb7a013a2bdf566d0235720402e79851176401..9b31e733dd93ba823827c8bee508ea576e708b30 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/views/dashboardStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/views/dashboardStyle.js @@ -54,7 +54,7 @@ const dashboardStyle = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", "& small": { @@ -68,7 +68,7 @@ const dashboardStyle = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", "& small": { diff --git a/jams-react-client/src/components/CustomPopupState/CustomPopupState.tsx b/jams-react-client/src/components/CustomPopupState/CustomPopupState.tsx index 6098d2d268d7cefab5809206bbc92bee0eb21335..c0e723b6e6e4aadb0cb9c3c16bc36f30e34ac183 100644 --- a/jams-react-client/src/components/CustomPopupState/CustomPopupState.tsx +++ b/jams-react-client/src/components/CustomPopupState/CustomPopupState.tsx @@ -15,12 +15,12 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import InfoIcon from "@mui/icons-material/Info"; import Popover from "@mui/material/Popover"; import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state"; import IconButton from "@mui/material/IconButton"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; +import { InfoOutlined } from "@mui/icons-material"; interface CustomPopupStateProps { message: string; @@ -32,7 +32,7 @@ export default function CustomPopupState(props: CustomPopupStateProps) { <PopupState variant="popover" popupId="demo-popup-popover"> {(popupState) => ( <div> - <InfoIcon + <InfoOutlined color="disabled" fontSize="small" {...bindTrigger(popupState)} diff --git a/jams-react-client/src/components/FormikField/FormikField.tsx b/jams-react-client/src/components/FormikField/FormikField.tsx index e1e77281380e18253543841cf4dfec39cae62155..901f447546f251b90e30273d972befee70f3c246 100644 --- a/jams-react-client/src/components/FormikField/FormikField.tsx +++ b/jams-react-client/src/components/FormikField/FormikField.tsx @@ -41,7 +41,7 @@ const FormikField = (props: FormikFieldProps) => { return ( <div className="FormikField"> <FormControl size="medium" error={props.onKeyUpError} fullWidth> - <InputLabel htmlFor={props.name}> + <InputLabel htmlFor={props.name} style={{ marginTop: "10px" }}> {props.onKeyUpError ? ( props.onKeyUpErrorMessage ) : ( diff --git a/jams-react-client/src/components/Navbars/Navbar.tsx b/jams-react-client/src/components/Navbars/Navbar.tsx index 5fc8d15968e960608dcaa67eb35d231c3a8a4f92..aac1b46e90fb975899bf0c637f54c0604b982519 100644 --- a/jams-react-client/src/components/Navbars/Navbar.tsx +++ b/jams-react-client/src/components/Navbars/Navbar.tsx @@ -45,9 +45,6 @@ export default function Header(props: HeaderProps) { return ( <AppBar className={classes.appBar}> <Toolbar className={classes.container}> - <div className={classes.flex}> - <Button color="info" className={classes.title}></Button> - </div> <Hidden mdUp implementation="css"> <IconButton color="default" diff --git a/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx b/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx index e3a3659fc4c572215cdec3510d18a7e1c80d6fc9..74cb4e410b39c4d82ca76df7633a87691fff135b 100644 --- a/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx +++ b/jams-react-client/src/components/PasswordDialog/PasswordDialog.tsx @@ -62,9 +62,6 @@ const styles = (theme: Theme) => ({ display: "flex", justifyContent: "space-between", }, - whiteButtonText: { - color: "white", - }, }); const useStyles = makeStyles(styles as any); @@ -291,7 +288,6 @@ const PasswordDialog: FC<PasswordDialogProps> = ({ type="submit" disabled={!isValid && !dirty} color="info" - className={classes.whiteButtonText} autoFocus > Update password diff --git a/jams-react-client/src/components/Sidebar/Sidebar.tsx b/jams-react-client/src/components/Sidebar/Sidebar.tsx index b744a114a8069f63b382fb210e9ba55d56f33fad..c18b3343592188dae2b6f76a5726a2645d235ac2 100644 --- a/jams-react-client/src/components/Sidebar/Sidebar.tsx +++ b/jams-react-client/src/components/Sidebar/Sidebar.tsx @@ -29,7 +29,7 @@ import ListItemText from "@mui/material/ListItemText"; import Icon from "@mui/material/Icon"; import Snackbar from "@mui/material/Snackbar/Snackbar"; -import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import ExitToAppOutlinedIcon from "@mui/icons-material/ExitToAppOutlined"; import UpdateIcon from "@mui/icons-material/Update"; import styles from "assets/jss/material-dashboard-react/components/sidebarStyle"; @@ -182,7 +182,7 @@ const Sidebar: FC<SidebarProps> = (props) => { [classes.itemIconRTL]: false, })} > - <ExitToAppIcon /> + <ExitToAppOutlinedIcon /> </Icon> <ListItemText primary={i18next.t("Logout", "Logout") as string} diff --git a/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx b/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx index dd228dea22adbc6927897f2a1890e7f1a76ecf9b..44d89fefd7a86bac22e908878aad80b7c58c9be0 100644 --- a/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx +++ b/jams-react-client/src/components/Snackbar/BlueprintSnackbar.tsx @@ -59,6 +59,7 @@ export const BlueprintSnackbar: FC<BlueprintSnackbarProps> = ({ TransitionComponent={SlideTransition} anchorOrigin={{ vertical: "bottom", horizontal: "right" }} open={snackbar.open} + autoHideDuration={5000} onClose={handleClose} message={snackbar.message} key={"bottomright"} diff --git a/jams-react-client/src/index.tsx b/jams-react-client/src/index.tsx index 0ffb974e1da426b1257921f6d963fa04cd3c91e4..7723e08c2326d108cce88002db1066cd798f0434 100644 --- a/jams-react-client/src/index.tsx +++ b/jams-react-client/src/index.tsx @@ -59,6 +59,7 @@ import SettingsRoute from "routes/SettingsRoute"; import SignIn from "layouts/SignIn"; import "assets/css/material-dashboard-react.css?v=1.9.0"; +import "../node_modules/ubuntu-fontface/ubuntu.min.css"; import "./i18n"; import { grayColor } from "assets/jss/material-dashboard-react"; @@ -78,6 +79,7 @@ const theme = createTheme({ allVariants: { color: grayColor[2], }, + fontFamily: ["Ubuntu", "sans-serif"].join(","), }, }); diff --git a/jams-react-client/src/layouts/BaseLayout.tsx b/jams-react-client/src/layouts/BaseLayout.tsx index 06ea9bb4b91b0caeb4814704f43862aadd9cf945..5f12945e62cb8265107df740a12876f6051dd204 100644 --- a/jams-react-client/src/layouts/BaseLayout.tsx +++ b/jams-react-client/src/layouts/BaseLayout.tsx @@ -27,10 +27,13 @@ import Footer from "components/Footer/Footer"; import Sidebar from "components/Sidebar/Sidebar"; // @mui/icons-material -import Person from "@mui/icons-material/Person"; -import Group from "@mui/icons-material/Group"; -import AllInbox from "@mui/icons-material/AllInbox"; -import SettingsIcon from "@mui/icons-material/Settings"; +import { + AccountCircleOutlined, + AllInboxOutlined, + GroupOutlined, + PersonOutlineOutlined, + SettingsOutlined, +} from "@mui/icons-material"; // core components/views for Admin layout import Users from "views/Users/Users"; import Groups from "views/Groups/Groups"; @@ -57,7 +60,6 @@ import DialogContentText from "@mui/material/DialogContentText/DialogContentText import Button from "@mui/material/Button"; import i18next from "i18next"; -import { AccountCircle as AccountCircleIcon } from "@mui/icons-material"; let ps: PerfectScrollbar; @@ -97,21 +99,21 @@ export default function Admin(props: AdminProps) { { path: `/user/${auth.getUsername()}`, name: i18next.t("myprofile", "My profile") as string, - icon: AccountCircleIcon, + icon: AccountCircleOutlined, component: Users, layout: "/admin", }, { path: "/users", name: i18next.t("users", "Users") as string, - icon: Person, + icon: PersonOutlineOutlined, component: Users, layout: "/admin", }, { path: "/groups", name: i18next.t("groups", "Groups") as string, - icon: Group, + icon: GroupOutlined, component: Groups, layout: "/admin", admin: true, @@ -119,7 +121,7 @@ export default function Admin(props: AdminProps) { { path: "/blueprints", name: i18next.t("blueprints", "Blueprints") as string, - icon: AllInbox, + icon: AllInboxOutlined, component: Blueprints, layout: "/admin", admin: true, @@ -127,7 +129,7 @@ export default function Admin(props: AdminProps) { { path: "/settings", name: i18next.t("settings", "Settings") as string, - icon: SettingsIcon, + icon: SettingsOutlined, component: Settings, layout: "/admin", admin: true, diff --git a/jams-react-client/src/layouts/ListLayout.tsx b/jams-react-client/src/layouts/ListLayout.tsx index 113f80df1e153dd445d7cf1fef09274a37de765f..1ffc6b9ac5005917c40e2f33de27b255ed17d45e 100644 --- a/jams-react-client/src/layouts/ListLayout.tsx +++ b/jams-react-client/src/layouts/ListLayout.tsx @@ -26,12 +26,6 @@ import Navbar from "components/Navbars/Navbar"; import Footer from "components/Footer/Footer"; import Sidebar from "components/Sidebar/Sidebar"; -// @mui/icons-material -import AccountCircleIcon from "@mui/icons-material/AccountCircle"; -import Person from "@mui/icons-material/Person"; -import Group from "@mui/icons-material/Group"; -import AllInbox from "@mui/icons-material/AllInbox"; -import SettingsIcon from "@mui/icons-material/Settings"; // core components/views for Admin layout import Users from "views/Users/Users"; import Groups from "views/Groups/Groups"; @@ -59,6 +53,13 @@ import Button from "@mui/material/Button"; import i18next from "i18next"; import { Route } from "./BaseLayout"; +import { + AccountCircleOutlined, + AllInboxOutlined, + GroupOutlined, + PersonOutline, + SettingsOutlined, +} from "@mui/icons-material"; let ps: PerfectScrollbar; @@ -89,7 +90,7 @@ export default function Admin(props: AdminProps) { { path: `/user/${auth.getUsername()}`, name: i18next.t("myprofile", "My profile") as string, - icon: AccountCircleIcon, + icon: AccountCircleOutlined, component: Users, layout: "/admin", admin: false, @@ -97,7 +98,7 @@ export default function Admin(props: AdminProps) { { path: "/users", name: i18next.t("users", "Users") as string, - icon: Person, + icon: PersonOutline, component: Users, layout: "/admin", admin: false, @@ -105,7 +106,7 @@ export default function Admin(props: AdminProps) { { path: "/groups", name: i18next.t("groups", "Groups") as string, - icon: Group, + icon: GroupOutlined, component: Groups, layout: "/admin", admin: true, @@ -113,7 +114,7 @@ export default function Admin(props: AdminProps) { { path: "/blueprints", name: i18next.t("blueprints", "Blueprints") as string, - icon: AllInbox, + icon: AllInboxOutlined, component: Blueprints, layout: "/admin", admin: true, @@ -121,7 +122,7 @@ export default function Admin(props: AdminProps) { { path: "/settings", name: i18next.t("settings", "Settings") as string, - icon: SettingsIcon, + icon: SettingsOutlined, component: Settings, layout: "/admin", admin: true, diff --git a/jams-react-client/src/layouts/SignIn.tsx b/jams-react-client/src/layouts/SignIn.tsx index 8358b2283fa24b8e4ca8e3554d2993ff0e1cd6e5..17db2a31828a135c0a1a34492b3ceebc164d915d 100644 --- a/jams-react-client/src/layouts/SignIn.tsx +++ b/jams-react-client/src/layouts/SignIn.tsx @@ -144,7 +144,7 @@ export default function SignIn() { <div className={classes.paper}> <img src={logo} - style={{ width: "25em", paddingBottom: "20px" }} + style={{ maxWidth: "25em", paddingBottom: "20px" }} alt="logo" /> <Typography component="h1" variant="h5"> diff --git a/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx b/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx index e953b3bb188f33e8495d25bf28cddf390d34b981..8f2b58cb276129704846751c162e9fc5f9ebd605 100644 --- a/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx +++ b/jams-react-client/src/views/Blueprint/ColorPickerPopup.tsx @@ -22,7 +22,6 @@ import { HexAlphaColorPicker, HexColorPicker } from "react-colorful"; const styles = { root: { position: "relative", - padding: "0 20px", }, colorButtonStyle: { borderRadius: "50%", diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx index d8951af40f02bb5efdebc200d84ac2b9106c2b81..bc4fb4e0667aa3ccffb42750f5b7fc0aa62daded 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.tsx @@ -118,7 +118,7 @@ const styles = () => ({ marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -154,6 +154,9 @@ const styles = () => ({ minWidth: "80vh", maxWidth: "80vh", }, + marginTop: { + marginTop: "15px", + }, }); const useStyles = makeStyles(styles as any); @@ -308,7 +311,7 @@ export default function EditBlueprintConfiguration( : "none", }} > - <FormControl className={classes.margin}> + <FormControl className={classes.marginTop}> <InputLabel htmlFor="turnServer"> { i18next.t( @@ -340,7 +343,7 @@ export default function EditBlueprintConfiguration( : "none", }} > - <FormControl className={classes.margin}> + <FormControl className={classes.marginTop}> <InputLabel htmlFor="turnServerUserName"> { i18next.t( @@ -375,7 +378,7 @@ export default function EditBlueprintConfiguration( : "none", }} > - <FormControl className={classes.margin}> + <FormControl className={classes.marginTop}> <InputLabel htmlFor="turnServerPassword"> { i18next.t( @@ -469,7 +472,7 @@ export default function EditBlueprintConfiguration( /> <FormGroup row> <FormControl - className={classes.margin} + className={classes.marginTop} style={{ display: selectedDHTProxyOption === "customDHTProxy" @@ -501,7 +504,7 @@ export default function EditBlueprintConfiguration( </FormGroup> <FormGroup row> <FormControl - className={classes.margin} + className={classes.marginTop} style={{ display: selectedDHTProxyOption === "customDHTProxy" diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx index 4b6a3e8a6e3c7004028c803d9437034ed2ff7544..eea2bc97fb825ff45b756bfd6588a46614c1d939 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.tsx @@ -78,7 +78,7 @@ const styles = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -465,9 +465,6 @@ export default function EditBlueprintPermissions( <TableCell align="left"> {i18next.t("last_name", "Last name") as string} </TableCell> - <TableCell align="right"> - {i18next.t("action", "Action") as string} - </TableCell> </TableRow> </TableHead> <TableBody> diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx b/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx index 54c9c12de3f070e091631acae7b14b03f8cddce8..807ac46ffa35a7a953f94eb4933192a502f328b4 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx +++ b/jams-react-client/src/views/Blueprint/EditBlueprintUi.tsx @@ -47,9 +47,6 @@ import { PolicyDataContext } from "./PolicyDataContext"; const styles = { ...dashboardStyle, - card: { - minWidth: "600px", - }, cardBody: { flexGrow: 1, }, @@ -65,7 +62,7 @@ const styles = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -202,7 +199,7 @@ export default function EditBlueprintUi({ <BlueprintSnackbar snackbar={snackbar} setSnackbar={setSnackbar} /> <GridContainer> <GridItem xs={12} sm={12} md={6}> - <Card profile className={classes.card}> + <Card profile> <CardHeader color="info" stats icon> <CardIcon color="info"> <SettingsIcon /> diff --git a/jams-react-client/src/views/Blueprints/Blueprints.tsx b/jams-react-client/src/views/Blueprints/Blueprints.tsx index 1db75936e5bb036484b95ebd53e533e9d84b5332..9b1e803d234948760bdc2ffc8927178f28b4dc00 100644 --- a/jams-react-client/src/views/Blueprints/Blueprints.tsx +++ b/jams-react-client/src/views/Blueprints/Blueprints.tsx @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { Link, useHistory } from "react-router-dom"; // @mui/material components import { makeStyles } from "@mui/styles"; @@ -29,11 +29,8 @@ import Card from "components/Card/Card"; import CardBody from "components/Card/CardBody"; import CardFooter from "components/Card/CardFooter"; -import GroupIcon from "@mui/icons-material/Group"; -import PersonIcon from "@mui/icons-material/Person"; import Search from "@mui/icons-material/Search"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; -import InfoIcon from "@mui/icons-material/Info"; import axios from "axios"; import configApiCall from "api"; @@ -54,6 +51,11 @@ import DialogTitle from "@mui/material/DialogTitle"; import i18next from "i18next"; import CreateBlueprintDialog from "./CreateBlueprintDialog"; +import { + GroupOutlined, + InfoOutlined, + PersonOutline, +} from "@mui/icons-material"; export interface Blueprint { name: string; @@ -74,15 +76,20 @@ const styles = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, + cardTitle: { + marginTop: "0px", + height: "2em", + }, deleteIcon: { float: "right", }, search: { width: "90%", + maxWidth: "500px", }, loading: { width: "100%", @@ -100,9 +107,8 @@ export default function Blueprints() { const classes = useStyles(); const history = useHistory(); const [blueprints, setBlueprints] = useState<Blueprint[]>([]); - const [loading, setLoading] = useState(false); const [zeroBlueprint, setZeroBlueprint] = useState(false); - const [progress, setProgress] = useState(0); + const [searchValue, setSearchValue] = useState<string>(""); const [open, setOpen] = useState(false); @@ -110,23 +116,12 @@ export default function Blueprints() { const [openRemoveDialog, setOpenRemoveDialog] = useState(false); useEffect(() => { - setLoading(true); - const timer = setInterval(() => { - setProgress((oldProgress) => { - if (oldProgress === 100) { - return 0; - } - const diff = Math.random() * 10; - return Math.min(oldProgress + diff, 100); - }); - }, 500); axios(configApiCall(api_path_blueprints + "?name=*", "GET", null, null)) .then((response) => { const allBluePrints = response.data; if (allBluePrints.length === 0) setZeroBlueprint(true); else setZeroBlueprint(false); setBlueprints(allBluePrints); - setLoading(false); }) .catch((error) => { setBlueprints([]); @@ -137,9 +132,7 @@ export default function Blueprints() { console.log("Error getting blueprints: " + error); } }); - return () => { - clearInterval(timer); - }; + return; }, [history, open, openRemoveDialog]); const handleRemoveBlueprint = (blueprintRemovedName: string) => { @@ -187,7 +180,7 @@ export default function Blueprints() { "Are you sure you want to delete" ) as string } - <strong>{removedBlueprint}</strong> ? + <strong> {removedBlueprint}</strong> ? </DialogContentText> </DialogContent> <DialogActions> @@ -218,8 +211,8 @@ export default function Blueprints() { }} inputProps={{ placeholder: i18next.t( - "search_blueprints_placeholder", - "Search blueprints…" + "search_blueprints", + "Search blueprints" ), inputProps: { "aria-label": i18next.t( @@ -227,20 +220,17 @@ export default function Blueprints() { "Search blueprints" ), }, + onKeyUp: (e: React.KeyboardEvent<HTMLInputElement>) => + setSearchValue((e.target as HTMLInputElement).value), }} /> )} {!zeroBlueprint && <Search />} - <div className={classes.loading}> - {loading && ( - <LinearProgress variant="determinate" value={progress} /> - )} - </div> </div> </GridItem> {zeroBlueprint ? ( <div className={classes.blueprintsNotFound}> - <InfoIcon /> + <InfoOutlined /> <p style={{ marginLeft: "10px" }}> { i18next.t( @@ -251,57 +241,68 @@ export default function Blueprints() { </p> </div> ) : ( - blueprints.map((blueprint: Blueprint) => ( - <GridItem xs={12} sm={6} md={3} lg={2} xl={2} key={blueprint.name}> - <Card profile> - <Link to={`/blueprint/${blueprint.name}`}> - <CardBody profile> - <h3 className={classes.cardTitle}> - {blueprint.name - ? blueprint.name - : (i18next.t( - "no_blueprint_name", - "No blueprint name" - ) as string)} - </h3> - <ul> - <li> - {JSON.parse(blueprint.policyData).rendezVous === - true ? ( - <p> - <GroupIcon - fontSize="small" - style={{ marginRight: "10px" }} - /> - {i18next.t("rendezvous", "Rendezvous") as string} - </p> - ) : ( - <p> - <PersonIcon - fontSize="small" - style={{ marginRight: "10px" }} - /> - {i18next.t("standalone", "Standalone") as string} - </p> - )} - </li> - </ul> - </CardBody> - </Link> - <CardFooter> - <IconButton - color="primary" - onClick={() => { - handleRemoveBlueprint(blueprint.name); - }} - size="large" - > - <DeleteOutlineIcon /> - </IconButton> - </CardFooter> - </Card> - </GridItem> - )) + blueprints + .filter((blueprint: Blueprint) => { + if (searchValue === "") { + return blueprint; + } else { + return blueprint.name + .toLowerCase() + .includes(searchValue.toLowerCase()); + } + }) + .map((blueprint: Blueprint) => ( + <GridItem + xs={12} + sm={6} + md={3} + lg={2} + xl={2} + key={blueprint.name} + > + <Card profile> + <Link to={`/blueprint/${blueprint.name}`}> + <CardBody profile> + <h3 className={classes.cardTitle}> + {blueprint.name + ? blueprint.name + : (i18next.t( + "no_blueprint_name", + "No blueprint name" + ) as string)} + </h3> + <ul style={{ fontSize: "12px" }}> + <li> + {JSON.parse(blueprint.policyData).rendezVous === + true ? ( + <React.Fragment> + <GroupOutlined style={{ marginRight: "10px" }} /> + {i18next.t("rendezvous", "Rendezvous") as string} + </React.Fragment> + ) : ( + <React.Fragment> + <PersonOutline style={{ marginRight: "10px" }} /> + {i18next.t("standalone", "Standalone") as string} + </React.Fragment> + )} + </li> + </ul> + </CardBody> + </Link> + <CardFooter> + <IconButton + color="primary" + onClick={() => { + handleRemoveBlueprint(blueprint.name); + }} + size="large" + > + <DeleteOutlineIcon /> + </IconButton> + </CardFooter> + </Card> + </GridItem> + )) )} </GridContainer> </div> diff --git a/jams-react-client/src/views/Blueprints/CreateBlueprintDialog.tsx b/jams-react-client/src/views/Blueprints/CreateBlueprintDialog.tsx index ba206a76a2304a63793363c21128b3753d91ae2c..261d70051a5cd8586fee798ee3ff5679142213ee 100644 --- a/jams-react-client/src/views/Blueprints/CreateBlueprintDialog.tsx +++ b/jams-react-client/src/views/Blueprints/CreateBlueprintDialog.tsx @@ -85,6 +85,11 @@ export default function CreateBlueprintDialog({ const handleCheckBlueprintNameExists = (searchBlueprintNameValue: string) => { setDisableCreate(true); + if (searchBlueprintNameValue.length === 0) { + setDisableCreate(true); + setBlueprintNameExists(false); + return; + } axios( configApiCall( api_path_blueprints + "?name=" + searchBlueprintNameValue, @@ -163,7 +168,7 @@ export default function CreateBlueprintDialog({ <DialogContent> <TextField autoFocus - error={blueprintNameExists} + error={blueprintNameExists || disableCreate} margin="dense" id="blueprintName" label={i18next.t("blueprint_name", "Blueprint name") as string} diff --git a/jams-react-client/src/views/Contacts/Contacts.tsx b/jams-react-client/src/views/Contacts/Contacts.tsx index a28bc0c6f39c648784c1c84a986594fb6243d452..0e7bcd7f7be941f38cf624995401c847cba9075e 100644 --- a/jams-react-client/src/views/Contacts/Contacts.tsx +++ b/jams-react-client/src/views/Contacts/Contacts.tsx @@ -54,7 +54,6 @@ import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import jami from "assets/img/faces/jami.png"; import noProfilePicture from "assets/img/faces/no-profile-picture.png"; -import LinearProgress from "@mui/material/LinearProgress"; import headerLinksStyle from "assets/jss/material-dashboard-react/components/headerLinksStyle"; import TemporaryDrawer from "components/Drawer/Drawer"; @@ -67,6 +66,7 @@ import DialogTitle from "@mui/material/DialogTitle"; import i18next from "i18next"; import { Contact, ServerContact } from "../../types/Contact"; import { UserProfile } from "views/UserProfile/DisplayUserProfile"; +import { InfoOutlined } from "@mui/icons-material"; const styles = { ...headerLinksStyle, @@ -77,12 +77,15 @@ const styles = { marginTop: "0", marginBottom: "0", }, + cardBody: { + marginTop: "0", + }, cardTitleWhite: { color: "#FFFFFF", marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -91,6 +94,7 @@ const styles = { }, search: { width: "90%", + maxWidth: "500px", }, loading: { width: "100%", @@ -98,6 +102,20 @@ const styles = { actionButtons: { height: "3em", }, + cardBodyContent: { + wordWrap: "break-word", + fontSize: "12px", + }, + link: { + position: "absolute", + top: "10px", + right: "10px", + }, + contactsNotFound: { + marginLeft: "10px", + display: "flex", + alignItems: "center", + }, }; const useStyles = makeStyles(styles as any); @@ -113,7 +131,6 @@ export default function Users(props: UsersProps) { const [contacts, setContacts] = useState<Contact[]>([]); const [users, setUsers] = useState<UserProfile[]>([]); const [searchValue, setSearchValue] = useState<string>(""); - const [loading, setLoading] = useState(false); const [progress, setProgress] = useState(0); const [openDrawer, setOpenDrawer] = useState(false); const [removedContact, setRemovedContact] = useState(""); @@ -222,7 +239,6 @@ export default function Users(props: UsersProps) { }); } setContacts(originalContacts); - setLoading(false); }) .catch((error) => { if (error.response && error.response.status === 401) { @@ -276,8 +292,7 @@ export default function Users(props: UsersProps) { }; useEffect(() => { - setLoading(true); - const timer = setInterval(() => { + setInterval(() => { setProgress((oldProgress) => { if (oldProgress === 100) { return 0; @@ -288,9 +303,7 @@ export default function Users(props: UsersProps) { }, 500); getAllContacts(); searchContacts(); - return () => { - clearInterval(timer); - }; + return; }, [openDrawer]); const removeContact = () => { @@ -409,10 +422,7 @@ export default function Users(props: UsersProps) { className: classes.margin + " " + classes.search, }} inputProps={{ - placeholder: i18next.t( - "search_contacts_using", - "Search contact fields (URI, firstname, lastname)" - ), + placeholder: i18next.t("search_contacts", "Search contacts"), inputProps: { "aria-label": i18next.t( "search_contacts", @@ -424,17 +434,13 @@ export default function Users(props: UsersProps) { }} /> <Search /> - <div className={classes.loading}> - {loading && ( - <LinearProgress variant="determinate" value={progress} /> - )} - </div> </div> </GridItem> {contacts .filter((data: Contact) => { - if (searchValue.length === 0) return data; - else { + if (searchValue.length === 0) { + return true; + } else { if ( typeof data.username !== "undefined" && typeof data.firstName !== "undefined" && @@ -454,21 +460,23 @@ export default function Users(props: UsersProps) { data.uri.toLowerCase() === searchValue.toLowerCase() ); } else { - return data; + return false; } } }) .map((contact) => ( <GridItem xs={12} - sm={12} - md={2} + sm={6} + md={3} + lg={2} + xl={2} key={contact.uri} style={{ display: contact.display }} > {contact.username && ( <Card profile> - <CardBody profile> + <CardBody profile className={classes.cardBody}> <CardAvatar profile> <img src={ @@ -480,22 +488,23 @@ export default function Users(props: UsersProps) { /> </CardAvatar> <a - style={{ float: "right", top: 0 }} + className={classes.link} href={`/user/${contact.username}`} > - <LaunchIcon - fontSize="small" - style={{ - marginRight: "10px", - color: "black", - }} - ></LaunchIcon> + <IconButton color="primary" size="medium"> + <LaunchIcon + fontSize="small" + style={{ + color: "black", + }} + ></LaunchIcon> + </IconButton> </a> <h4 className={classes.cardTitle}> {(contact.firstName || contact.lastName) && `${contact.firstName} ${contact.lastName}`} </h4> - <ul> + <ul className={classes.cardBodyContent}> <li style={{ display: "block" }}> {contact.username && ( <img @@ -540,7 +549,7 @@ export default function Users(props: UsersProps) { onClick={() => { handleRemoveContact(contact.uri, contact.username); }} - size="large" + size="medium" > <DeleteOutlineIcon /> </IconButton> @@ -549,9 +558,18 @@ export default function Users(props: UsersProps) { )} </GridItem> ))} - {contacts.length === 0 && - ((props.username + - i18next.t("has_no_contacts", " has no contacts")) as string)} + {contacts.length === 0 && ( + <div className={classes.contactsNotFound}> + <InfoOutlined /> + + <p style={{ marginLeft: "10px" }}> + { + (props.username + + i18next.t("has_no_contacts", " has no contacts")) as string + } + </p> + </div> + )} </GridContainer> </div> ); diff --git a/jams-react-client/src/views/Groups/CreateGroupDialog.tsx b/jams-react-client/src/views/Groups/CreateGroupDialog.tsx index e2529bccfe8e0bd024f27354bee50db444d0b8c4..9910f974514c10c827b2e5dbcc2a00963ff13ea1 100644 --- a/jams-react-client/src/views/Groups/CreateGroupDialog.tsx +++ b/jams-react-client/src/views/Groups/CreateGroupDialog.tsx @@ -239,7 +239,6 @@ export default function CreateGroupDialog({ <Button onClick={handleCreateGroup} color="info" - className={classes.whiteButtonText} disabled={disableCreate} autoFocus > diff --git a/jams-react-client/src/views/Groups/EditGroup.tsx b/jams-react-client/src/views/Groups/EditGroup.tsx index 96d656b2e92724bbafee7672fcc0072ffc3a7a53..8ce674b6edaddf3742373c0b35c3d50e501eb755 100644 --- a/jams-react-client/src/views/Groups/EditGroup.tsx +++ b/jams-react-client/src/views/Groups/EditGroup.tsx @@ -104,7 +104,7 @@ const useStyles = makeStyles(() => ({ marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -514,9 +514,6 @@ export default function EditGroup(props: EditGroupProps) { <TableCell align="left"> {i18next.t("last_name", "Last name") as string} </TableCell> - <TableCell align="right"> - {i18next.t("action", "Action") as string} - </TableCell> </TableRow> </TableHead> <TableBody> diff --git a/jams-react-client/src/views/Groups/Groups.tsx b/jams-react-client/src/views/Groups/Groups.tsx index dcfc2eeb33627eba16d940c0395cda665000d00d..11a6c5e282f1596436cea111db6ece9c0cab892e 100644 --- a/jams-react-client/src/views/Groups/Groups.tsx +++ b/jams-react-client/src/views/Groups/Groups.tsx @@ -31,9 +31,6 @@ import CustomInput from "components/CustomInput/CustomInput"; import IconButton from "@mui/material/IconButton"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import Search from "@mui/icons-material/Search"; -import MailOutlineIcon from "@mui/icons-material/MailOutline"; -import PersonIcon from "@mui/icons-material/Person"; -import InfoIcon from "@mui/icons-material/Info"; import axios from "axios"; import configApiCall from "api"; @@ -52,11 +49,14 @@ import DialogContent from "@mui/material/DialogContent"; import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; -import LinearProgress from "@mui/material/LinearProgress"; - import i18next from "i18next"; import CreateGroupDialog from "./CreateGroupDialog"; +import { + AllInboxOutlined, + InfoOutlined, + PersonOutline, +} from "@mui/icons-material"; const styles = { cardCategoryWhite: { @@ -71,15 +71,20 @@ const styles = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, + cardTitle: { + marginTop: "0px", + height: "2em", + }, deleteIcon: { float: "right", }, search: { width: "90%", + maxWidth: "500px", }, loading: { width: "100%", @@ -89,9 +94,6 @@ const styles = { display: "flex", alignItems: "center", }, - whiteButtonText: { - color: "white", - }, inputBottomMargin: { marginBottom: "1rem", }, @@ -110,9 +112,7 @@ export default function Groups() { const classes = useStyles(); const history = useHistory(); const [groups, setGroups] = useState([]); - const [loading, setLoading] = useState(false); const [zeroGroup, setZeroGroup] = useState(false); - const [progress, setProgress] = useState(0); const [searchValue, setSearchValue] = useState<string>(""); const [openCreate, setOpenCreate] = useState(false); @@ -150,17 +150,6 @@ export default function Groups() { }; useEffect(() => { - setLoading(true); - const timer = setInterval(() => { - setProgress((oldProgress) => { - if (oldProgress === 100) { - return 0; - } - const diff = Math.random() * 10; - return Math.min(oldProgress + diff, 100); - }); - }, 500); - axios(configApiCall(api_path_get_list_group, "GET", null, null)) .then((response) => { const allGroups = response.data; @@ -187,7 +176,6 @@ export default function Groups() { }); }); setGroups(allGroups); - setLoading(false); }) .catch((error) => { if (error.response && error.response.status === 401) { @@ -199,9 +187,7 @@ export default function Groups() { console.error("Error getting groups: " + error); } }); - return () => { - clearInterval(timer); - }; + return; }, [openCreate, openRemoveDialog, history]); if (!auth.hasAdminScope()) { @@ -242,7 +228,7 @@ export default function Groups() { "Are you sure you want to delete" ) as string }{" "} - <strong>{removedGroup.name}</strong> ? + <strong> {removedGroup.name}</strong> ? </DialogContentText> </DialogContent> <DialogActions> @@ -274,10 +260,7 @@ export default function Groups() { className: classes.margin + " " + classes.search, }} inputProps={{ - placeholder: i18next.t( - "search_groups_placeholder", - "Search groups…" - ), + placeholder: i18next.t("search_groups", "Search groups"), inputProps: { "aria-label": i18next.t( "search_groups", @@ -290,27 +273,22 @@ export default function Groups() { /> )} {!zeroGroup && <Search />} - <div className={classes.loading}> - {!zeroGroup && loading && ( - <LinearProgress variant="determinate" value={progress} /> - )} - </div> </div> </GridItem> {zeroGroup ? ( <div className={classes.groupsNotFound}> - <InfoIcon />{" "} + <InfoOutlined />{" "} <p style={{ marginLeft: "10px" }}> {i18next.t("no_groups_found", "No groups Found") as string} </p> </div> ) : ( groups - .filter((data: Group) => { + .filter((group: Group) => { if (searchValue === "") { - return data; + return group; } else { - return data.name + return group.name .toLowerCase() .includes(searchValue.toLowerCase()); } @@ -321,26 +299,27 @@ export default function Groups() { <Link to={`/group/${group.id}`}> <CardBody profile> <h3 className={classes.cardTitle}>{group.name}</h3> - <ul> + <ul style={{ fontSize: "12px" }}> <li> - <PersonIcon - fontSize="small" - style={{ marginRight: "10px" }} - /> + <PersonOutline style={{ marginRight: "10px" }} /> <strong style={{ marginRight: "5px" }}> {group.groupMembersLength} </strong> {i18next.t("users", "Users") as string} </li> <li> - <MailOutlineIcon - fontSize="small" - style={{ marginRight: "10px" }} - /> - <strong style={{ marginRight: "5px" }}> - {i18next.t("blueprint", "Blueprint") as string} - </strong> - {group.blueprint} + <AllInboxOutlined style={{ marginRight: "10px" }} /> + <div + style={{ + display: "flex", + flexDirection: "column", + }} + > + <strong style={{ marginRight: "5px" }}> + {i18next.t("blueprint", "Blueprint") as string} + </strong> + {group.blueprint} + </div> </li> </ul> </CardBody> diff --git a/jams-react-client/src/views/Settings/General.tsx b/jams-react-client/src/views/Settings/General.tsx index 811571fb58182bf87cfc0818738ca159b5440a61..9351ecd0f4d18b18a5303c46aa203827c1a74abf 100644 --- a/jams-react-client/src/views/Settings/General.tsx +++ b/jams-react-client/src/views/Settings/General.tsx @@ -25,11 +25,7 @@ import Typography from "@mui/material/Typography"; import { makeStyles } from "@mui/styles"; import RefreshIcon from "@mui/icons-material/Refresh"; -import FileCopyIcon from "@mui/icons-material/FileCopy"; -import VisibilityIcon from "@mui/icons-material/Visibility"; -import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import IconButton from "@mui/material/IconButton"; -import VpnKeyIcon from "@mui/icons-material/VpnKey"; import InputAdornment from "@mui/material/InputAdornment"; import GridContainer from "components/Grid/GridContainer"; @@ -50,6 +46,12 @@ import LanguagePicker from "../../components/LanguagePicker/LanguagePicker"; import pjson from "../../../package.json"; import generator from "generate-password-browser"; import { Theme } from "@mui/material"; +import { + FileCopyOutlined, + VisibilityOffOutlined, + VisibilityOutlined, + VpnKeyOutlined, +} from "@mui/icons-material"; const useStyles = makeStyles((theme: Theme) => ({ paper: { @@ -209,7 +211,7 @@ export default function General(props: GeneralProps) { type={passwordVisible ? "text" : "password"} startAdornment={ <InputAdornment position="start"> - <VpnKeyIcon /> + <VpnKeyOutlined /> </InputAdornment> } endAdornment={ @@ -220,9 +222,9 @@ export default function General(props: GeneralProps) { size="large" > {passwordVisible ? ( - <VisibilityIcon /> + <VisibilityOutlined /> ) : ( - <VisibilityOffIcon /> + <VisibilityOffOutlined /> )} </IconButton> } @@ -230,9 +232,6 @@ export default function General(props: GeneralProps) { onKeyUpError={false} onKeyUpErrorMessage="" /> - {touched.password && errors.password ? ( - <span>{errors.password.toString()}</span> - ) : null} <FormikField name="confirmPassword" @@ -247,7 +246,7 @@ export default function General(props: GeneralProps) { type={passwordVisible ? "text" : "password"} startAdornment={ <InputAdornment position="start"> - <VpnKeyIcon /> + <VpnKeyOutlined /> </InputAdornment> } endAdornment={ @@ -258,9 +257,9 @@ export default function General(props: GeneralProps) { size="large" > {passwordVisible ? ( - <VisibilityIcon /> + <VisibilityOutlined /> ) : ( - <VisibilityOffIcon /> + <VisibilityOffOutlined /> )} </IconButton> } @@ -268,9 +267,6 @@ export default function General(props: GeneralProps) { onKeyUpError={false} onKeyUpErrorMessage="" /> - {touched.confirmPassword && errors.confirmPassword ? ( - <span>{errors.confirmPassword.toString()}</span> - ) : null} <Button variant="contained" @@ -306,7 +302,7 @@ export default function General(props: GeneralProps) { variant="contained" size="large" className={classes.button} - startIcon={<FileCopyIcon />} + startIcon={<FileCopyOutlined />} > { i18next.t( diff --git a/jams-react-client/src/views/UserProfile/AdminAddUserToGroup.tsx b/jams-react-client/src/views/UserProfile/AdminAddUserToGroup.tsx index 83c419ee89531a67523664d62edb2d258044bcd5..c9c3d0161aad73772c2a066485da4a2ef63e624f 100644 --- a/jams-react-client/src/views/UserProfile/AdminAddUserToGroup.tsx +++ b/jams-react-client/src/views/UserProfile/AdminAddUserToGroup.tsx @@ -26,6 +26,7 @@ import { TableRow, TableCell, TableBody, + ClassNameMap, } from "@mui/material"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; @@ -48,7 +49,7 @@ interface AdminAddUserToGroupProps { username: string; openDrawer: boolean; setOpenDrawer: (open: boolean) => void; - classes: any; + classes: ClassNameMap<string>; groupMemberships: GroupMembership[]; setGroupMemberships: (groupMemberships: GroupMembership[]) => void; removeUserFromGroup: (group: GroupMembership) => void; @@ -152,9 +153,6 @@ const AdminAddUserToGroup: FC<AdminAddUserToGroupProps> = ({ <TableCell align="left"> {i18next.t("group_name", "Group name") as string} </TableCell> - <TableCell align="right"> - {i18next.t("action", "Action") as string} - </TableCell> </TableRow> </TableHead> <TableBody> diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx b/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx index 125ffcae3fd0481cc57cb034763dea6e446f00a3..64c57cb3edf4ba3ef4117c824d43e28733f334f4 100644 --- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx +++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.tsx @@ -30,15 +30,9 @@ import { DialogTitle, Grid, Chip, - LinearProgress, Theme, } from "@mui/material"; -import EditIcon from "@mui/icons-material/Edit"; -import DeleteIcon from "@mui/icons-material/Delete"; - -import VpnKeyIcon from "@mui/icons-material/VpnKey"; - import auth from "auth"; import configApiCall from "api"; import { @@ -64,6 +58,11 @@ import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardS import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react"; import UserProfileFieldsList from "./UserProfileFieldsList"; import AdminAddUserToGroup from "./AdminAddUserToGroup"; +import { + DeleteOutlineOutlined, + EditOutlined, + VpnKeyOutlined, +} from "@mui/icons-material"; const styles = (theme: Theme) => ({ ...dashboardStyle, @@ -82,7 +81,7 @@ const styles = (theme: Theme) => ({ marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -179,9 +178,6 @@ const styles = (theme: Theme) => ({ loading: { width: "100%", }, - whiteButtonText: { - color: "white", - }, }); const useStyles = makeStyles(styles as any); @@ -248,7 +244,6 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ const [revokedUser, setRevokedUser] = useState(""); const [changePasswordOpen, setChangePasswordOpen] = useState(false); const [loading, setLoading] = useState(false); - const [progress, setProgress] = useState(0); const [openDrawer, setOpenDrawer] = useState(false); const removeUserFromGroup = (group: GroupMembership) => { @@ -272,19 +267,6 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ useEffect(() => { setLoading(true); - const timer = setInterval(() => { - setProgress((oldProgress) => { - if (oldProgress === 100) { - return 0; - } - const diff = Math.random() * 10; - return Math.min(oldProgress + diff, 100); - }); - }, 500); - - return () => { - clearInterval(timer); - }; }, [history, username]); const getAdminUserGroups = () => { @@ -435,12 +417,7 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ <Button onClick={handleClose} color="primary"> {i18next.t("cancel", "Cancel") as string} </Button> - <Button - onClick={revokeUser} - color="info" - className={classes.whiteButtonText} - autoFocus - > + <Button onClick={revokeUser} color="info" autoFocus> {i18next.t("revoke", "Revoke") as string} </Button> </DialogActions> @@ -450,9 +427,6 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ open={changePasswordOpen} onClose={() => setChangePasswordOpen(false)} /> - <div className={classes.loading}> - {loading && <LinearProgress variant="determinate" value={progress} />} - </div> {!loading && ( <GridContainer> <Grid item xs={12} sm={12} md={6}> @@ -484,7 +458,9 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ </div> </div> </Grid> - <UserProfileFieldsList user={user} /> + <Grid item xs={12}> + <UserProfileFieldsList user={user} /> + </Grid> </Grid> </div> </CardBody> @@ -494,10 +470,9 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ {canEdit() && ( <Button color="info" - className={classes.whiteButtonText} onClick={() => setDisplayUser(false)} > - <EditIcon />{" "} + <EditOutlined />{" "} {i18next.t("edit_profile", "Edit profile") as string} </Button> )} @@ -506,12 +481,11 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ {auth.isLocalDirectory() && auth.hasAdminScope() && ( <Button color="info" - className={classes.whiteButtonText} onClick={() => { setChangePasswordOpen(true); }} > - <VpnKeyIcon />{" "} + <VpnKeyOutlined />{" "} { i18next.t( "change_password", @@ -528,10 +502,9 @@ const DisplayUserProfile: FC<DisplayUserProfileProps> = ({ {auth.hasAdminScope() && revoked === false && ( <Button color="info" - className={classes.whiteButtonText} onClick={() => handleClickOpen(user.username)} > - <DeleteIcon fontSize="small" />{" "} + <DeleteOutlineOutlined fontSize="small" />{" "} {i18next.t("revoke_user", "Revoke user") as string} </Button> )} diff --git a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx index d25fcb5b256e7a864c59745b0a7dc929e159685c..b10f517a90d0207a1d855e2fb044a5c3b86b770c 100644 --- a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx +++ b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.tsx @@ -112,7 +112,7 @@ const styles = (theme: Theme) => ({ marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -231,9 +231,6 @@ const styles = (theme: Theme) => ({ alignItems: "center", }, }, - whiteButtonText: { - color: "white", - }, }); const useStyles = makeStyles(styles as any); @@ -648,12 +645,7 @@ export default function EditCreateUserProfile( </div> </DialogContent> <DialogActions> - <Button - onClick={cropProfilePicture} - color="info" - className={classes.whiteButtonText} - autoFocus - > + <Button onClick={cropProfilePicture} color="info" autoFocus> {i18next.t("validate", "Validate") as string} </Button> </DialogActions> @@ -1108,11 +1100,7 @@ export default function EditCreateUserProfile( </CardBody> <CardFooter className={classes.alignRight}> {!props.createUser && ( - <Button - color="info" - className={classes.whiteButtonText} - onClick={handleCancelUpdate} - > + <Button color="info" onClick={handleCancelUpdate}> {i18next.t("cancel", "Cancel") as string} </Button> )} @@ -1121,7 +1109,6 @@ export default function EditCreateUserProfile( type="submit" disabled={!isValid || !dirty || userExists} color="info" - className={classes.whiteButtonText} > { i18next.t( @@ -1131,12 +1118,7 @@ export default function EditCreateUserProfile( } </Button> ) : ( - <Button - type="submit" - disabled={!isValid} - color="info" - className={classes.whiteButtonText} - > + <Button type="submit" disabled={!isValid} color="info"> {i18next.t("save_profile", "Save Profile") as string} </Button> )} diff --git a/jams-react-client/src/views/UserProfile/UserProfileFieldsList.tsx b/jams-react-client/src/views/UserProfile/UserProfileFieldsList.tsx index a316095a9170ecae4738fd58861e898ff6c7a11a..0df0f7c626ef4077db99e62d7e1823af969bee32 100644 --- a/jams-react-client/src/views/UserProfile/UserProfileFieldsList.tsx +++ b/jams-react-client/src/views/UserProfile/UserProfileFieldsList.tsx @@ -32,8 +32,8 @@ import SmartphoneOutlinedIcon from "@mui/icons-material/SmartphoneOutlined"; import LocalPrintshopOutlinedIcon from "@mui/icons-material/LocalPrintshopOutlined"; import PhoneForwardedOutlinedIcon from "@mui/icons-material/PhoneForwardedOutlined"; -import PersonIcon from "@mui/icons-material/Person"; import { UserProfile } from "./DisplayUserProfile"; +import { PersonOutlineOutlined } from "@mui/icons-material"; interface UserProfileFieldsListProps { user: UserProfile; @@ -47,7 +47,7 @@ const UserProfileFieldsList: FC<UserProfileFieldsListProps> = ({ user }) => { <ListItem> <ListItemAvatar> <Avatar> - <PersonIcon /> + <PersonOutlineOutlined /> </Avatar> </ListItemAvatar> <ListItemText primary={`${user.firstName} ${user.lastName}`} /> diff --git a/jams-react-client/src/views/Users/Users.tsx b/jams-react-client/src/views/Users/Users.tsx index 633aaafc6d78c00ea4ddf6fd93368e483956c981..a6bcee1bcc7e066604909a2bb2618eef5142743e 100644 --- a/jams-react-client/src/views/Users/Users.tsx +++ b/jams-react-client/src/views/Users/Users.tsx @@ -37,8 +37,6 @@ import Card from "components/Card/Card"; import CardAvatar from "components/Card/CardAvatar"; import CardBody from "components/Card/CardBody"; -import InfoIcon from "@mui/icons-material/Info"; -import BusinessOutlinedIcon from "@mui/icons-material/BusinessOutlined"; import Search from "@mui/icons-material/Search"; import Checkbox from "@mui/material/Checkbox"; import FormControlLabel from "@mui/material/FormControlLabel"; @@ -49,12 +47,12 @@ import { api_path_get_user_directory_search } from "globalUrls"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import jami from "assets/img/faces/jami.png"; import noProfilePicture from "assets/img/faces/no-profile-picture.png"; -import LinearProgress from "@mui/material/LinearProgress"; import headerLinksStyle from "assets/jss/material-dashboard-react/components/headerLinksStyle"; import { debounce } from "lodash"; import i18next from "i18next"; import { UserProfile } from "views/UserProfile/DisplayUserProfile"; +import { BusinessCenterOutlined, InfoOutlined } from "@mui/icons-material"; const styles = { ...headerLinksStyle, @@ -70,7 +68,7 @@ const styles = { marginTop: "0px", minHeight: "auto", fontWeight: "300", - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + fontFamily: "'Ubuntu'", marginBottom: "3px", textDecoration: "none", }, @@ -79,6 +77,7 @@ const styles = { }, search: { width: "90%", + maxWidth: "500px", }, loading: { width: "100%", @@ -90,6 +89,12 @@ const styles = { }, cardBodyContent: { wordWrap: "break-word", + fontSize: "12px", + }, + link: { + "&:hover": { + color: "unset", + }, }, }; const useStyles = makeStyles(styles as any); @@ -97,10 +102,8 @@ export default function Users() { const classes = useStyles(); const history = useHistory(); const [users, setUsers] = useState<UserProfile[]>([]); - const noUsersFound = users.length === 0; + const [noUsersFound, setNoUsersFound] = useState(false); const [noMatchFound, setNoMatchFound] = useState(false); - const [loading, setLoading] = useState(false); - const [progress, setProgress] = useState(0); const [showRevokedUsers, setShowRevokedUsers] = useState(false); const [searchValue, setSearchValue] = useState(""); @@ -109,16 +112,6 @@ export default function Users() { const [numberPages, setNumberPages] = useState(1); useEffect(() => { - setLoading(true); - const timer = setInterval(() => { - setProgress((oldProgress) => { - if (oldProgress === 100) { - return 0; - } - const diff = Math.random() * 10; - return Math.min(oldProgress + diff, 100); - }); - }, 500); axios( configApiCall( api_path_get_user_directory_search, @@ -130,10 +123,13 @@ export default function Users() { .then((response) => { setUsers(response.data.profiles); setNumberPages(response.data.numPages); - setLoading(false); + if (response.data.profiles.length === 0) { + setNoUsersFound(true); + } else { + setNoUsersFound(false); + } }) .catch((error) => { - setLoading(false); if (error.response && error.response.status === 401) { auth.authenticated = false; history.push("/signin"); @@ -141,16 +137,10 @@ export default function Users() { console.error("Error getting users: " + error); } }); - return () => { - clearInterval(timer); - }; }, [history]); const searchUsers = (value: string, page = 1) => { setSelectedPage(page); - setLoading(true); - setNoMatchFound(false); - setUsers([]); axios( configApiCall( api_path_get_user_directory_search, @@ -160,14 +150,20 @@ export default function Users() { ) ) .then((response) => { - setLoading(false); setUsers(response.data.profiles); setNumberPages(response.data.numPages); setNoMatchFound(response.data.profiles.length === 0); + setNoUsersFound(false); + if (value.length === 0) { + setNoMatchFound(false); + // This is the case were the search value is empty but there is still no users found + if (response.data.profiles.length === 0) { + setNoUsersFound(true); + } + } }) .catch((error) => { setUsers([]); - setLoading(false); if (error.response && error.response.status === 401) { auth.authenticated = false; history.push("/signin"); @@ -194,7 +190,7 @@ export default function Users() { <GridContainer> <GridItem xs={12} sm={12} md={12}> {auth.isLocalDirectory() && auth.hasAdminScope() && ( - <Link to={"/createuser"}> + <Link to={"/createuser"} className={classes.link}> <Button variant="contained" color="primary"> <AddCircleOutlineIcon />{" "} {i18next.t("create_user", "Create user") as string} @@ -210,7 +206,11 @@ export default function Users() { color="primary" /> } - style={{ marginLeft: "1rem" }} + style={ + auth.isLocalDirectory() && auth.hasAdminScope() + ? { marginLeft: "1rem" } + : { marginLeft: "-8px" } + } label="Display revoked users" /> @@ -221,10 +221,7 @@ export default function Users() { className: classes.margin + " " + classes.search, }} inputProps={{ - placeholder: i18next.t( - "search_users_using", - "Search users using (username, first name, last name)" - ), + placeholder: i18next.t("search_users", "Search users"), inputProps: { "aria-label": i18next.t( "search_users", @@ -237,7 +234,7 @@ export default function Users() { <Search /> </GridItem> <GridItem xs={12} sm={12} md={6}> - {!noUsersFound && ( + {numberPages > 1 && !noMatchFound && ( <Pagination count={numberPages} page={selectedPage} @@ -247,15 +244,9 @@ export default function Users() { </GridItem> </GridContainer> - <div className={classes.loading}> - {loading && ( - <LinearProgress variant="determinate" value={progress} /> - )} - </div> - {noUsersFound && ( <div className={classes.usersNotFound}> - <InfoIcon /> + <InfoOutlined /> <p style={{ marginLeft: "10px" }}> {i18next.t("no_users_found", "No users found") as string} @@ -263,7 +254,7 @@ export default function Users() { </div> )} </GridItem> - {(!noUsersFound || !noMatchFound) && + {!noMatchFound && users .sort(function (a, b) { if (a.username < b.username) { @@ -277,7 +268,10 @@ export default function Users() { .map((user) => ( <GridItem xs={12} sm={6} md={3} lg={2} xl={2} key={user.username}> <Card profile> - <Link to={`/user/${user.username}`}> + <Link + to={`/user/${user.username}`} + style={{ height: "100%" }} + > <CardBody profile> <CardAvatar profile> <img @@ -317,7 +311,7 @@ export default function Users() { </li> <li> {user.organization && ( - <BusinessOutlinedIcon + <BusinessCenterOutlined fontSize="small" style={{ marginRight: "10px" }} /> @@ -334,7 +328,7 @@ export default function Users() { ))} {noMatchFound && ( <div className={classes.usersNotFound}> - <InfoIcon /> + <InfoOutlined /> <p style={{ marginLeft: "10px" }}> {