From c7fe1f2874b9048fd0352ec957f6db433c57f078 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 24 Aug 2023 10:51:38 +0200 Subject: [PATCH 001/115] DISPLAY-1028: remove unused react logos --- public/logo192.png | Bin 5347 -> 0 bytes public/logo512.png | Bin 9664 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 public/logo192.png delete mode 100644 public/logo512.png diff --git a/public/logo192.png b/public/logo192.png deleted file mode 100644 index fc44b0a3796c0e0a64c3d858ca038bd4570465d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN From 7308e0098a9a89f33dbb808849891bfe0849d834 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 24 Aug 2023 10:51:55 +0200 Subject: [PATCH 002/115] DISPLAY-1028: add svg + logo component --- src/components/navigation/logo.jsx | 8 +++++++ src/components/navigation/logo.svg | 13 ++++++++++ src/components/user/aarhus-logo.svg | 37 +++++++++++++++++++++++++++++ src/components/user/mitid-logo.svg | 8 +++++++ 4 files changed, 66 insertions(+) create mode 100644 src/components/navigation/logo.jsx create mode 100644 src/components/navigation/logo.svg create mode 100644 src/components/user/aarhus-logo.svg create mode 100644 src/components/user/mitid-logo.svg diff --git a/src/components/navigation/logo.jsx b/src/components/navigation/logo.jsx new file mode 100644 index 00000000..d5b58111 --- /dev/null +++ b/src/components/navigation/logo.jsx @@ -0,0 +1,8 @@ +import { React } from "react"; +import logo from "./logo.svg"; + +const Logo = () => { + return ; +}; + +export default Logo; diff --git a/src/components/navigation/logo.svg b/src/components/navigation/logo.svg new file mode 100644 index 00000000..5c9817c0 --- /dev/null +++ b/src/components/navigation/logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/components/user/aarhus-logo.svg b/src/components/user/aarhus-logo.svg new file mode 100644 index 00000000..659cc6fb --- /dev/null +++ b/src/components/user/aarhus-logo.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/user/mitid-logo.svg b/src/components/user/mitid-logo.svg new file mode 100644 index 00000000..1cfab8e6 --- /dev/null +++ b/src/components/user/mitid-logo.svg @@ -0,0 +1,8 @@ + \ No newline at end of file From 8a2973fc3fedda3cf6230282ae9b53330c2d5201 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 24 Aug 2023 10:52:23 +0200 Subject: [PATCH 003/115] DISPLAY-1028: updates to login page --- .../login-sidebar/login-sidebar.jsx | 24 ++++++ src/components/user/login.jsx | 75 +++++++++++++------ 2 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 src/components/navigation/login-sidebar/login-sidebar.jsx diff --git a/src/components/navigation/login-sidebar/login-sidebar.jsx b/src/components/navigation/login-sidebar/login-sidebar.jsx new file mode 100644 index 00000000..92b33625 --- /dev/null +++ b/src/components/navigation/login-sidebar/login-sidebar.jsx @@ -0,0 +1,24 @@ +import { React } from "react"; +import { useTranslation, Trans } from "react-i18next"; +import Logo from "../logo"; + +const LoginSidebar = () => { + const { t } = useTranslation("common", { keyPrefix: "login-sidebar" }); + return ( + <> + +
+
+

+ {t("AD-info-text")} +

+

+ {t("MitID-info-text")} +

+
+
+ + ); +}; + +export default LoginSidebar; diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 41a1aeed..d57c0ab3 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -1,8 +1,8 @@ import { React, useEffect, useState, useContext } from "react"; -import { Alert, Button, Card, Form, Row } from "react-bootstrap"; +import { Alert, Button, Form, Row } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; -import { useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import queryString from "query-string"; import Col from "react-bootstrap/Col"; import { MultiSelect } from "react-multi-select-component"; @@ -13,7 +13,9 @@ import { api } from "../../redux/api/api.generated"; import ConfigLoader from "../../config-loader"; import { displayError } from "../util/list/toast-component/display-toast"; import localStorageKeys from "../util/local-storage-keys"; - +import LoginSidebar from "../navigation/login-sidebar/login-sidebar"; +import AarhusLogo from "./aarhus-logo.svg"; +import MitIdLogo from "./mitid-logo.svg"; /** * Login component * @@ -32,7 +34,7 @@ function Login() { const [error, setError] = useState(false); const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); - const [oidcAuthUrls, setOidcAuthUrls] = useState(""); + const [oidcAuthUrls, setOidcAuthUrls] = useState({ authorizationUrl: "sf" }); const [oidcAuthLoadingError, setOidcAuthLoadingError] = useState(""); const [ready, setReady] = useState(false); @@ -199,29 +201,54 @@ function Login() { return ( <> {ready && ( - -
- - -

{t("login-with-oidc")}

- {oidcAuthUrls && ( + <> + + + + + + +

{t("login-header")}

+

+ {t("oidc-mit-id-header")} +

+
- )} - {oidcAuthLoadingError && ( - {oidcAuthLoadingError} - )} - - + + {oidcAuthLoadingError && ( + {oidcAuthLoadingError} + )} + + + + {t("login-with-oidc")} + + {oidcAuthLoadingError && ( + {oidcAuthLoadingError} + )} +
+

+ {t("os2-display-user-header")} +

<> {!context.tenants.get && ( <> -

{t("login-with-username-password")}

)} - -
- -
+ + + + )} {!ready && ( From ff516ba01d6d97369af5d527d6c695213158e412 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 24 Aug 2023 10:52:34 +0200 Subject: [PATCH 004/115] DISPLAY-1028: use logo in sidebar --- src/components/navigation/sidebar/sidebar.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/navigation/sidebar/sidebar.jsx b/src/components/navigation/sidebar/sidebar.jsx index e3a1b410..dfd372f8 100644 --- a/src/components/navigation/sidebar/sidebar.jsx +++ b/src/components/navigation/sidebar/sidebar.jsx @@ -5,6 +5,7 @@ import Col from "react-bootstrap/Col"; import { useTranslation } from "react-i18next"; import NavItems from "../nav-items/nav-items"; import "./sidebar.scss"; +import Logo from "../logo"; /** * The sidebar component. @@ -25,10 +26,10 @@ function SideBar() { onSelect={(selectedKey) => setActive(selectedKey)} > - {t("sidebar.brand")} + From 2285a25137c85eeb12b6d0eb158575de7818caae Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 24 Aug 2023 10:52:45 +0200 Subject: [PATCH 005/115] DISPLAY-1028: fix minor accessibility issues --- src/components/media/image-list.jsx | 24 +++------ src/components/media/media-list.scss | 23 ++++---- .../navigation/nav-items/nav-items.jsx | 38 +++++++++----- src/components/slide/content/file-preview.jsx | 8 ++- .../slide/content/media-selector-list.jsx | 24 ++++----- .../slide/content/media-selector-modal.jsx | 1 + src/components/util/column-hoc.jsx | 1 + .../util/list/checkbox-for-list.jsx | 38 ++++++-------- src/components/util/modal/modal-dialog.jsx | 4 +- src/components/util/search-box/search-box.jsx | 11 ++-- src/translations/da/common.json | 52 ++++++++----------- 11 files changed, 102 insertions(+), 122 deletions(-) diff --git a/src/components/media/image-list.jsx b/src/components/media/image-list.jsx index 543f2cb7..96393523 100644 --- a/src/components/media/image-list.jsx +++ b/src/components/media/image-list.jsx @@ -32,7 +32,7 @@ function ImageList({ media, multiple }) { } // Translations - const { t } = useTranslation("common"); + const { t } = useTranslation("common", { keyPrefix: "media-list" }); return (
@@ -46,25 +46,17 @@ function ImageList({ media, multiple }) { : "" }`} > - + {data.description} selectImage(data)} checked={selected.find((item) => item.id === data["@id"])} - tabIndex={-1} - aria-label={t("media-list.checkbox-form-aria-label")} - readOnly + aria-label={t("checkbox-form-aria-label", { this: data.title })} /> -
diff --git a/src/components/media/media-list.scss b/src/components/media/media-list.scss index e9aa3019..fd0d960e 100644 --- a/src/components/media/media-list.scss +++ b/src/components/media/media-list.scss @@ -1,12 +1,6 @@ @import "~bootstrap/scss/bootstrap/"; .media-item { - &-button { - padding: 0; - border: none; - background-color: transparent; - display: flex; - img { height: 150px; @include media-breakpoint-up(md) { @@ -24,13 +18,7 @@ transition: opacity 0.1s ease-out; } - &:hover { - img { - opacity: 0.6; - transition: opacity 0.2s ease-in; - } - } - } + &.selected { border-color: $primary; @@ -44,7 +32,14 @@ position: absolute; top: $spacer * 0.5; right: $spacer * 0.5; - pointer-events: none; + + input { + &:focus { + // override bootstrap to make focus more visible on images. + border: 3px solid #86b7fe; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.65); + } + } } } diff --git a/src/components/navigation/nav-items/nav-items.jsx b/src/components/navigation/nav-items/nav-items.jsx index 4e27124a..a90a757d 100644 --- a/src/components/navigation/nav-items/nav-items.jsx +++ b/src/components/navigation/nav-items/nav-items.jsx @@ -22,7 +22,7 @@ import "./nav-items.scss"; * @returns {object} Nav items */ function NavItems() { - const { t } = useTranslation("common"); + const { t } = useTranslation("common", { keyPrefix: "nav-items" }); const { setSelected } = useModal(); const context = useContext(UserContext); const { page, createdBy, isPublished } = useContext(ListContext); @@ -55,9 +55,13 @@ function NavItems() { to="/slide/list" > - {t("nav-items.content-slides")} + {t("content-slides")} - + @@ -69,7 +73,7 @@ function NavItems() { } to="/media/list" > - {t("nav-items.content-media")} + {t("content-media")} @@ -81,9 +85,13 @@ function NavItems() { to="/playlist/list" > - {t("nav-items.playlists-playlists")} + {t("playlists-playlists")} - + @@ -96,7 +104,7 @@ function NavItems() { } to="/campaign/list" > - {t("nav-items.playlists-campaigns")} + {t("playlists-campaigns")} @@ -109,7 +117,7 @@ function NavItems() { } to="/shared/list" > - {t("nav-items.shared-playlists")} + {t("shared-playlists")} @@ -123,9 +131,13 @@ function NavItems() { to="/screen/list" > - {t("nav-items.screens-screens")} + {t("screens-screens")} - + @@ -139,7 +151,7 @@ function NavItems() { } to="/group/list" > - {t("nav-items.screens-groups")} + {t("screens-groups")} @@ -153,7 +165,7 @@ function NavItems() { to="/themes/list" > - {t("nav-items.configuration")} + {t("configuration")} @@ -166,7 +178,7 @@ function NavItems() { } to="/themes/list" > - {t("nav-items.configuration-themes")} + {t("configuration-themes")} diff --git a/src/components/slide/content/file-preview.jsx b/src/components/slide/content/file-preview.jsx index 74b2078f..6ee8d5d3 100644 --- a/src/components/slide/content/file-preview.jsx +++ b/src/components/slide/content/file-preview.jsx @@ -22,13 +22,12 @@ function FilePreview({ fileEntry, enableVideoControls = false }) { const renderPreview = (fileEntryToRender) => { /* eslint-disable jsx-a11y/media-has-caption */ if (fileEntryToRender?.assets) { - const { assets } = fileEntryToRender; + const { assets, title } = fileEntryToRender; if (assets.type?.indexOf("image/") === 0) { - return ( - {t("file.image-preview")} - ); + return {title}; } + if (assets.type?.indexOf("video/") === 0) { return (
{oidcAuthLoadingError && ( {oidcAuthLoadingError} From 572373de2107164b1b2d49abb76bf3913743d592 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Fri, 1 Sep 2023 08:45:58 +0200 Subject: [PATCH 021/115] DISPLAY-1028: replace dacity with facity --- src/components/user/login.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 2c764886..62302e4d 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -7,7 +7,7 @@ import queryString from "query-string"; import Col from "react-bootstrap/Col"; import { MultiSelect } from "react-multi-select-component"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { daCity } from "@fortawesome/free-solid-svg-icons"; +import { faCity } from "@fortawesome/free-solid-svg-icons"; import LoadingComponent from "../util/loading-component/loading-component"; import UserContext from "../../context/user-context"; import FormInput from "../util/forms/form-input"; @@ -235,7 +235,7 @@ function Login() { aria-label={t("login-with-oidc-aria-label")} to={oidcAuthUrls.authorizationUrl} > - + {t("login-with-oidc")} )} From 88d7b15387758d20cc3c3595a2aabafc9892d63b Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Fri, 1 Sep 2023 11:33:08 +0200 Subject: [PATCH 022/115] DISPLAY-1028: style login page better --- .../login-sidebar/login-sidebar.jsx | 11 +- src/components/navigation/logo.jsx | 6 +- src/components/user/bg-icons.svg | 118 ++++++++++++++++++ src/components/user/login.jsx | 16 ++- src/components/user/login.scss | 28 +++++ 5 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 src/components/user/bg-icons.svg create mode 100644 src/components/user/login.scss diff --git a/src/components/navigation/login-sidebar/login-sidebar.jsx b/src/components/navigation/login-sidebar/login-sidebar.jsx index 92b33625..3db5af91 100644 --- a/src/components/navigation/login-sidebar/login-sidebar.jsx +++ b/src/components/navigation/login-sidebar/login-sidebar.jsx @@ -4,20 +4,21 @@ import Logo from "../logo"; const LoginSidebar = () => { const { t } = useTranslation("common", { keyPrefix: "login-sidebar" }); + return ( - <> +
-
+
-

+

{t("AD-info-text")}

-

+

{t("MitID-info-text")}

- +
); }; diff --git a/src/components/navigation/logo.jsx b/src/components/navigation/logo.jsx index d5b58111..8840254f 100644 --- a/src/components/navigation/logo.jsx +++ b/src/components/navigation/logo.jsx @@ -2,7 +2,11 @@ import { React } from "react"; import logo from "./logo.svg"; const Logo = () => { - return ; + return ( +
+ +
+ ); }; export default Logo; diff --git a/src/components/user/bg-icons.svg b/src/components/user/bg-icons.svg new file mode 100644 index 00000000..e4bb067f --- /dev/null +++ b/src/components/user/bg-icons.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 62302e4d..1c04e794 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -17,6 +17,7 @@ import { displayError } from "../util/list/toast-component/display-toast"; import localStorageKeys from "../util/local-storage-keys"; import LoginSidebar from "../navigation/login-sidebar/login-sidebar"; import MitIdLogo from "./mitid-logo.svg"; +import "./login.scss"; /** * Login component @@ -203,16 +204,19 @@ function Login() { return ( <> {ready && ( - <> - +
+ - +

{t("login-header")}

{t("oidc-mit-id-header")} @@ -225,6 +229,7 @@ function Login() { onClick={() => {}} className="margin-right-button" size="lg" + aria-describedby="mitid-explanation" aria-label={t("login-with-mitid-aria-label")} > @@ -234,6 +239,7 @@ function Login() { className="margin-right-button btn btn-primary btn-lg margin-right-button d-flex align-items-center" aria-label={t("login-with-oidc-aria-label")} to={oidcAuthUrls.authorizationUrl} + aria-describedby="ad-explanation" > {t("login-with-oidc")} @@ -306,7 +312,7 @@ function Login() { - +

)} {!ready && ( diff --git a/src/components/user/login.scss b/src/components/user/login.scss new file mode 100644 index 00000000..c38820d0 --- /dev/null +++ b/src/components/user/login.scss @@ -0,0 +1,28 @@ +.login-container { + padding: 7em; + + @media (max-width: 800px) { + padding: 0em; + } + + .login-box-shadow { + @media (max-width: 480px) { + box-shadow: white 0px 2px 8px 0px; + } + box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px; + } + + .background-image-screens { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + background-image: url("./bg-icons.svg"); + } + + .border-color-white { + // to override bootstraps self-important !important. + border-color: rgba(248, 249, 250, 0.4) !important; + } +} From 34f9671648405a56967166028527cc0b31a88e32 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Fri, 1 Sep 2023 12:50:27 +0200 Subject: [PATCH 023/115] DISPLAY-1028: color of background --- src/components/user/login.jsx | 2 +- src/components/user/login.scss | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 1c04e794..e6157155 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -212,7 +212,7 @@ function Login() { > - +
Date: Fri, 1 Sep 2023 12:58:43 +0200 Subject: [PATCH 024/115] DISPLAY-1028: changelog + coding standards --- CHANGELOG.md | 2 ++ src/components/user/login.scss | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc592b19..5fe83ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- [#212](https://github.com/os2display/display-admin-client/pull/212) + update color of login background - [#211](https://github.com/os2display/display-admin-client/pull/211) update login page add todo for button with mitid diff --git a/src/components/user/login.scss b/src/components/user/login.scss index 82d0fb9e..76adcca7 100644 --- a/src/components/user/login.scss +++ b/src/components/user/login.scss @@ -1,7 +1,7 @@ .login-container { padding: 7em; height: 100vh; - background: #F8F9FA; + background: #f8f9fa; @media (max-width: 800px) { padding: 0em; From 7d7775aeda3011314447a3cd51df3c0665e9a0cb Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Mon, 28 Aug 2023 10:28:27 +0200 Subject: [PATCH 025/115] DISPLAY-1009: add focus trap to modals (accessibility) --- package.json | 1 + src/components/util/modal/modal-dialog.jsx | 37 ++++++++++++---------- yarn.lock | 20 ++++++++++++ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 499ae60e..6931f5d9 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dayjs": "^1.10.7", "dompurify": "^2.3.3", "eslint-plugin-jsdoc": "^35.4.3", + "focus-trap-react": "^10.2.1", "i18next": "^21.6.14", "lodash.find": "^4.2.0", "lodash.get": "^4.4.2", diff --git a/src/components/util/modal/modal-dialog.jsx b/src/components/util/modal/modal-dialog.jsx index 70540507..7ebc292e 100644 --- a/src/components/util/modal/modal-dialog.jsx +++ b/src/components/util/modal/modal-dialog.jsx @@ -2,6 +2,7 @@ import { React, useEffect } from "react"; import { Button, Modal } from "react-bootstrap"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; +import FocusTrap from "focus-trap-react"; /** * @param {object} props The props. @@ -49,24 +50,26 @@ function ModalDialog({ }, []); return ( - <> - - -

{title}

-
-
- {children} - - - {showAcceptButton && ( - - )} - - + {showAcceptButton && ( + + )} + +
+ ); } diff --git a/yarn.lock b/yarn.lock index 6c0b41d2..0792a1cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6007,6 +6007,21 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +focus-trap-react@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/focus-trap-react/-/focus-trap-react-10.2.1.tgz#3f72c9c018885e089806346966c59303eb6e32ef" + integrity sha512-UrAKOn52lvfHF6lkUMfFhlQxFgahyNW5i6FpHWkDxAeD4FSk3iwx9n4UEA4Sims0G5WiGIi0fAyoq3/UVeNCYA== + dependencies: + focus-trap "^7.5.2" + tabbable "^6.2.0" + +focus-trap@^7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.5.2.tgz#e5ee678d10a18651f2591ffb66c949fb098d57cf" + integrity sha512-p6vGNNWLDGwJCiEjkSK6oERj/hEyI9ITsSwIUICBoKLlWiTWXJRfQibCwcoi50rTZdbi87qDtUlMCmQwsGSgPw== + dependencies: + tabbable "^6.2.0" + follow-redirects@^1.0.0: version "1.15.0" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz" @@ -12604,6 +12619,11 @@ symbol-tree@^3.2.4: resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tabbable@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" + integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== + table@^6.0.9, table@^6.6.0: version "6.8.0" resolved "https://registry.npmjs.org/table/-/table-6.8.0.tgz" From df0d0fcf66649965449bc07919110eb587e5d75f Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Mon, 28 Aug 2023 10:37:07 +0200 Subject: [PATCH 026/115] DISPLAY-1009: add nav item --- src/app.jsx | 8 +++++++ .../navigation/nav-items/nav-items.jsx | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/app.jsx b/src/app.jsx index b117fb8f..00c44459 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -35,6 +35,9 @@ import Logout from "./components/user/logout"; import AuthHandler from "./auth-handler"; import LoadingComponent from "./components/util/loading-component/loading-component"; import ModalProvider from "./context/modal-context/modal-provider"; +import UsersList from "./components/users/users-list"; +import UserEdit from "./components/users/user-edit"; +import UserCreate from "./components/users/user-create"; import "react-toastify/dist/ReactToastify.css"; import "./app.scss"; @@ -331,6 +334,11 @@ function App() { element={} /> + + } /> + } /> + } /> + + + + + `nav-link ${isActive ? "disabled" : ""}` + } + to="/users/list" + > + + {t("external-users")} + + + + + + Date: Mon, 28 Aug 2023 10:37:49 +0200 Subject: [PATCH 027/115] DISPLAY-1009: add user files --- src/components/users/user-columns.jsx | 33 ++++++ src/components/users/user-create.jsx | 85 +++++++++++++++ src/components/users/user-edit.jsx | 123 +++++++++++++++++++++ src/components/users/user-form.jsx | 96 +++++++++++++++++ src/components/users/users-list.jsx | 148 ++++++++++++++++++++++++++ src/translations/da/common.json | 42 ++++++++ 6 files changed, 527 insertions(+) create mode 100644 src/components/users/user-columns.jsx create mode 100644 src/components/users/user-create.jsx create mode 100644 src/components/users/user-edit.jsx create mode 100644 src/components/users/user-form.jsx create mode 100644 src/components/users/users-list.jsx diff --git a/src/components/users/user-columns.jsx b/src/components/users/user-columns.jsx new file mode 100644 index 00000000..b1bfbf67 --- /dev/null +++ b/src/components/users/user-columns.jsx @@ -0,0 +1,33 @@ +import { React } from "react"; +import { useTranslation } from "react-i18next"; +import SelectColumnHoc from "../util/select-column-hoc"; +import ColumnHoc from "../util/column-hoc"; + +/** + * Columns for Users lists. + * + * @param {object} props - The props. + * @param {Function} props.apiCall - The api to call + * @param {string} props.infoModalRedirect - The url for redirecting in the info modal. + * @param {string} props.infoModalTitle - The info modal title. + * @param {string} props.dataKey The data key for mapping the data. + * @param {Function} props.handleDelete + * @returns {object} The columns for the users lists. + */ +function getUserColumns() { + const { t } = useTranslation("common", { keyPrefix: "users-list" }); + + const columns = [ + { + path: "activated", + label: t("columns.activated"), + }, + ]; + + return columns; +} + +const UserColumns = ColumnHoc(getUserColumns); +const SelectUserColumns = SelectColumnHoc(getUserColumns); + +export { SelectUserColumns, UserColumns }; diff --git a/src/components/users/user-create.jsx b/src/components/users/user-create.jsx new file mode 100644 index 00000000..b15a851f --- /dev/null +++ b/src/components/users/user-create.jsx @@ -0,0 +1,85 @@ +import { React, useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { usePostV1ScreenGroupsMutation } from "../../redux/api/api.generated"; +import UserForm from "./user-form"; +import { + displaySuccess, + displayError, +} from "../util/list/toast-component/display-toast"; + +/** + * The group edit component. + * + * @returns {object} The group edit page. + */ +function UserCreate() { + const { t } = useTranslation("common", { keyPrefix: "user-create" }); + const navigate = useNavigate(); + const headerText = t("create-new-user-header"); + const [formStateObject, setFormStateObject] = useState({ + title: "", + description: "", + createdBy: "", + modifiedBy: "", + }); + + const [ + PostV1ScreenGroups, + { error: saveError, isLoading: isSaving, isSuccess: isSaveSuccess }, + ] = usePostV1ScreenGroupsMutation(); + + /** Handle submitting is done. */ + useEffect(() => { + if (isSaveSuccess) { + displaySuccess(t("success-messages.saved-user")); + navigate("/users/list"); + } + }, [isSaveSuccess]); + + /** If the user is saved with error, display the error message */ + useEffect(() => { + if (saveError) { + displayError(t("error-messages.save-user-error"), saveError); + } + }, [saveError]); + + /** + * Set state on change in input field + * + * @param {object} props The props. + * @param {object} props.target Event target. + */ + const handleInput = ({ target }) => { + const localFormStateObject = { ...formStateObject }; + localFormStateObject[target.id] = target.value; + setFormStateObject(localFormStateObject); + }; + + /** Handles submit. */ + const handleSubmit = () => { + const saveData = { + title: formStateObject.title, + description: formStateObject.description, + modifiedBy: formStateObject.modifiedBy, + createdBy: formStateObject.createdBy, + }; + + PostV1ScreenGroups({ + screenGroupScreenGroupInput: JSON.stringify(saveData), + }); + }; + + return ( + + ); +} + +export default UserCreate; diff --git a/src/components/users/user-edit.jsx b/src/components/users/user-edit.jsx new file mode 100644 index 00000000..52a214b7 --- /dev/null +++ b/src/components/users/user-edit.jsx @@ -0,0 +1,123 @@ +import { React, useEffect, useState } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; + +import { + useGetV1ScreenGroupsByIdQuery, + usePutV1ScreenGroupsByIdMutation, +} from "../../redux/api/api.generated"; +import { + displaySuccess, + displayError, +} from "../util/list/toast-component/display-toast"; +import UserForm from "./user-form"; + +/** + * The user edit component. + * + * @returns {object} The user edit page. + */ +function UserEdit() { + const { t } = useTranslation("common", { keyPrefix: "user-edit" }); + const navigate = useNavigate(); + const headerText = t("edit-user-header"); + const [formStateObject, setFormStateObject] = useState(); + const [savingUser, setSavingUser] = useState(false); + const [loadingMessage, setLoadingMessage] = useState( + t("loading-messages.loading-user") + ); + const { id } = useParams(); + const [PutV1ScreenGroup, { error: saveError, isSuccess: isSaveSuccess }] = + usePutV1ScreenGroupsByIdMutation(); + + const { + data, + error: loadError, + isLoading, + } = useGetV1ScreenGroupsByIdQuery({ id }); + + /** Set loaded data into form state. */ + useEffect(() => { + if (data) { + setFormStateObject(data); + } + }, [data]); + + /** If the user is saved, display the success message */ + useEffect(() => { + if (isSaveSuccess) { + setSavingUser(false); + displaySuccess(t("success-messages.saved-user")); + navigate("/user/list"); + } + }, [isSaveSuccess]); + + /** If the user is saved with error, display the error message */ + useEffect(() => { + if (saveError) { + displayError(t("error-messages.save-user-error"), saveError); + setSavingUser(false); + } + }, [saveError]); + + /** If the user is not loaded, display the error message */ + useEffect(() => { + if (loadError) { + displayError( + t("error-messages.load-user-error", { + error: loadError.error + ? loadError.error + : loadError.data["hydra:description"], + id, + }) + ); + } + }, [loadError]); + + /** + * Set state on change in input field + * + * @param {object} props The props. + * @param {object} props.target Event target. + */ + const handleInput = ({ target }) => { + const localFormStateObject = { ...formStateObject }; + localFormStateObject[target.id] = target.value; + setFormStateObject(localFormStateObject); + }; + + /** Handles submit. */ + const handleSubmit = () => { + setSavingUser(true); + setLoadingMessage(t("loading-messages.saving-user")); + const saveData = { + title: formStateObject.title, + description: formStateObject.description, + modifiedBy: formStateObject.modifiedBy, + createdBy: formStateObject.createdBy, + }; + PutV1ScreenGroup({ + id, + screenGroupScreenGroupInput: JSON.stringify(saveData), + }); + }; + + return ( + <> + {formStateObject && ( + + )} + + ); +} + +export default UserEdit; diff --git a/src/components/users/user-form.jsx b/src/components/users/user-form.jsx new file mode 100644 index 00000000..848ccc8b --- /dev/null +++ b/src/components/users/user-form.jsx @@ -0,0 +1,96 @@ +import { React } from "react"; +import { useNavigate } from "react-router-dom"; +import { Button } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; +import PropTypes from "prop-types"; +import Form from "react-bootstrap/Form"; +import LoadingComponent from "../util/loading-component/loading-component"; +import ContentBody from "../util/content-body/content-body"; +import ContentFooter from "../util/content-footer/content-footer"; +import FormInput from "../util/forms/form-input"; + +/** + * The user form component. + * + * @param {object} props - The props. + * @param {object} props.user The user object to modify in the form. + * @param {Function} props.handleInput Handles form input. + * @param {Function} props.handleSubmit Handles form submit. + * @param {string} props.headerText Headline text. + * @param {boolean} props.isLoading Indicator of whether the form is loading + * @param {string} props.loadingMessage The loading message for the spinner + * @returns {object} The user form. + */ +function UserForm({ + user, + handleInput, + handleSubmit, + headerText, + isLoading, + loadingMessage, +}) { + const { t } = useTranslation("common"); + const navigate = useNavigate(); + + return ( + <> + + +

{headerText}

+ + + + + + + + + + ); +} + +UserForm.defaultProps = { + isLoading: false, + loadingMessage: "", + user: PropTypes.shape({ + title: "", + }), +}; + +UserForm.propTypes = { + user: PropTypes.shape({ + title: PropTypes.string.isRequired, + }), + handleInput: PropTypes.func.isRequired, + handleSubmit: PropTypes.func.isRequired, + headerText: PropTypes.string.isRequired, + isLoading: PropTypes.bool, + loadingMessage: PropTypes.string, +}; + +export default UserForm; diff --git a/src/components/users/users-list.jsx b/src/components/users/users-list.jsx new file mode 100644 index 00000000..470660b0 --- /dev/null +++ b/src/components/users/users-list.jsx @@ -0,0 +1,148 @@ +import { React, useEffect, useState, useContext } from "react"; +import { useTranslation } from "react-i18next"; +import List from "../util/list/list"; +import ListContext from "../../context/list-context"; +import UserContext from "../../context/user-context"; +import useModal from "../../context/modal-context/modal-context-hook"; +import { UserColumns } from "./user-columns"; +import ContentHeader from "../util/content-header/content-header"; +import ContentBody from "../util/content-body/content-body"; +import idFromUrl from "../util/helpers/id-from-url"; +import { + displaySuccess, + displayError, +} from "../util/list/toast-component/display-toast"; +import { + useGetV1ScreenGroupsQuery, + useGetV1ScreenGroupsByIdScreensQuery, + useDeleteV1ScreenGroupsByIdMutation, +} from "../../redux/api/api.generated"; + +/** + * The users list component. + * + * @returns {object} The users list. + */ +function UsersList() { + const { t } = useTranslation("common", { keyPrefix: "users-list" }); + const { selected, setSelected } = useModal(); + const { + searchText: { get: searchText }, + page: { get: page }, + createdBy: { get: createdBy }, + } = useContext(ListContext); + const context = useContext(UserContext); + + // Local state + const [isDeleting, setIsDeleting] = useState(false); + const [listData, setListData] = useState(); + const [loadingMessage, setLoadingMessage] = useState( + t("loading-messages.loading-users") + ); + + // Delete call + const [ + DeleteV1ScreenGroups, + { isSuccess: isDeleteSuccess, error: isDeleteError }, + ] = useDeleteV1ScreenGroupsByIdMutation(); + + // Get method + const { + data, + error: usersGetError, + isLoading, + refetch, + } = useGetV1ScreenGroupsQuery({ + page, + order: { createdAt: "desc" }, + title: searchText, + createdBy, + }); + + useEffect(() => { + if (data) { + setListData(data); + } + }, [data]); + + useEffect(() => { + refetch(); + }, [searchText, page, createdBy]); + + /** Deletes multiple users. */ + useEffect(() => { + if (isDeleting && selected.length > 0) { + const userToDelete = selected[0]; + setSelected(selected.slice(1)); + const userToDeleteId = idFromUrl(userToDelete.id); + DeleteV1ScreenGroups({ id: userToDeleteId }); + } + }, [isDeleting, isDeleteSuccess]); + + // Sets success messages in local storage, because the page is reloaded + useEffect(() => { + if (isDeleteSuccess && selected.length === 0) { + displaySuccess(t("success-messages.user-delete")); + refetch(); + setIsDeleting(false); + } + }, [isDeleteSuccess]); + + // If the tenant is changed, data should be refetched + useEffect(() => { + if (context.selectedTenant.get) { + refetch(); + } + }, [context.selectedTenant.get]); + + // Display error on unsuccessful deletion + useEffect(() => { + if (isDeleteError) { + setIsDeleting(false); + displayError(t("error-messages.user-delete-error"), isDeleteError); + } + }, [isDeleteError]); + + /** Starts the deletion process. */ + const handleDelete = () => { + setIsDeleting(true); + setLoadingMessage(t("loading-messages.deleting-user")); + }; + + // The columns for the table. + const columns = UserColumns({ handleDelete }); + + // Error with retrieving list of users + useEffect(() => { + if (usersGetError) { + displayError(t("error-messages.users-load-error"), usersGetError); + } + }, [usersGetError]); + + return ( + <> + + + <> + {listData && ( + + )} + + + + ); +} + +export default UsersList; diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 62697cc7..40cc3333 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -86,6 +86,26 @@ "change-view-list": "Liste", "change-view-calendar": "Kalender" }, + "users-list": { + "columns": { + "activated": "Aktiv" + }, + "loading-messages": { + "loading-users": "Henter brugere", + "deleting-user": "Sletter bruger(e)" + }, + "error-messages": { + "user-delete-error": "Der skete en fejl da brugeren skulle slettes:", + "users-load-error": "Der skete en fejl da brugerne skulle hentes:" + }, + "success-messages": { + "user-delete": "Brugeren er blevet slettet" + }, + "header": "Brugere", + "create-new-user": "Opret ny bruger", + "change-view-list": "Liste", + "change-view-calendar": "Kalender" + }, "groups-columns": { "screens": "Skærme i gruppen" }, @@ -303,6 +323,12 @@ "group-description-label": "Gruppens beskrivelse", "group-description-placeholder": "Udfyld gruppens beskrivelse" }, + "user-form": { + "save-button": "Gem brugeren", + "cancel-button": "Annuller", + "user-title-label": "Brugerens navn", + "user-title-placeholder": "Udfyld brugerens navn" + }, "group-create": { "create-new-group-header": "Opret ny gruppe", "loading-messages": { @@ -329,6 +355,20 @@ "load-group-error": "Der skete en fejl da gruppen med følgende id: {{id}} skulle hentes:" } }, + "user-edit": { + "edit-user-header": "Rediger følgende bruger", + "loading-messages": { + "saving-group": "Gemmer brugeren", + "loading-group": "Henter brugeren" + }, + "success-messages": { + "saved-group": "Brugeren er gemt" + }, + "error-messages": { + "save-group-error": "Der skete en fejl da brugeren skulle gemmes:", + "load-group-error": "Der skete en fejl da brugeren med følgende id: {{id}} skulle hentes:" + } + }, "playlists-columns": { "name": "Navn", "created-by": "Oprettet af", @@ -644,7 +684,9 @@ "add-new-slide-aria-label": "Tilføj nyt slide", "add-new-playlist-aria-label": "Tilføj ny spilleliste", "add-new-screen-aria-label": "Tilføj ny skærm", + "add-new-external-user-aria-label": "Tilføj ny bruger", "screens-screens": "Skærme", + "external-users": "Eksterne brugere", "screens-groups": "Grupper", "playlists-campaigns": "Kampagner", "playlists-playlists": "Spillelister", From 13bcb7b2c6f64c0885acedfa388be626a9149e06 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Mon, 28 Aug 2023 10:37:55 +0200 Subject: [PATCH 028/115] DISPLAY-1009: remove useless id --- src/components/util/list/checkbox-for-list.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/util/list/checkbox-for-list.jsx b/src/components/util/list/checkbox-for-list.jsx index 3da6c229..49af7cc8 100644 --- a/src/components/util/list/checkbox-for-list.jsx +++ b/src/components/util/list/checkbox-for-list.jsx @@ -19,7 +19,7 @@ function CheckboxForList({ selected, onSelected, disabled, title }) { return (
- + {} : onSelected} From 2a73cecac1fe35e08df094e16af39fe501363994 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Mon, 28 Aug 2023 10:40:16 +0200 Subject: [PATCH 029/115] DISPLAY-1009: add todos --- src/components/users/users-list.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/users/users-list.jsx b/src/components/users/users-list.jsx index 470660b0..c8972c6a 100644 --- a/src/components/users/users-list.jsx +++ b/src/components/users/users-list.jsx @@ -14,7 +14,6 @@ import { } from "../util/list/toast-component/display-toast"; import { useGetV1ScreenGroupsQuery, - useGetV1ScreenGroupsByIdScreensQuery, useDeleteV1ScreenGroupsByIdMutation, } from "../../redux/api/api.generated"; @@ -41,12 +40,14 @@ function UsersList() { ); // Delete call + // todo change delete from groups to users const [ DeleteV1ScreenGroups, { isSuccess: isDeleteSuccess, error: isDeleteError }, ] = useDeleteV1ScreenGroupsByIdMutation(); // Get method + // todo change get from groups to users const { data, error: usersGetError, From 09a3093b40d5b7106b2855604494bb934b2e0a08 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Mon, 28 Aug 2023 10:46:26 +0200 Subject: [PATCH 030/115] DISPLAY-1009: add todos --- src/components/users/user-create.jsx | 5 +++-- src/components/users/user-edit.jsx | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/users/user-create.jsx b/src/components/users/user-create.jsx index b15a851f..a0e6ec55 100644 --- a/src/components/users/user-create.jsx +++ b/src/components/users/user-create.jsx @@ -9,9 +9,9 @@ import { } from "../util/list/toast-component/display-toast"; /** - * The group edit component. + * The user create component. * - * @returns {object} The group edit page. + * @returns {object} The user create page. */ function UserCreate() { const { t } = useTranslation("common", { keyPrefix: "user-create" }); @@ -24,6 +24,7 @@ function UserCreate() { modifiedBy: "", }); + // Todo change post from group to user const [ PostV1ScreenGroups, { error: saveError, isLoading: isSaving, isSuccess: isSaveSuccess }, diff --git a/src/components/users/user-edit.jsx b/src/components/users/user-edit.jsx index 52a214b7..663a3d99 100644 --- a/src/components/users/user-edit.jsx +++ b/src/components/users/user-edit.jsx @@ -1,7 +1,6 @@ import { React, useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; - import { useGetV1ScreenGroupsByIdQuery, usePutV1ScreenGroupsByIdMutation, @@ -27,9 +26,12 @@ function UserEdit() { t("loading-messages.loading-user") ); const { id } = useParams(); + + // Todo change put from group to user const [PutV1ScreenGroup, { error: saveError, isSuccess: isSaveSuccess }] = usePutV1ScreenGroupsByIdMutation(); + // Todo change get from group to user const { data, error: loadError, From 49313096085ebcd5cf1738e86b38f288d0a836b5 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Fri, 1 Sep 2023 11:50:08 +0200 Subject: [PATCH 031/115] DISPLAY-1009: apply coding standards --- src/components/users/user-columns.jsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/users/user-columns.jsx b/src/components/users/user-columns.jsx index b1bfbf67..c4954e78 100644 --- a/src/components/users/user-columns.jsx +++ b/src/components/users/user-columns.jsx @@ -1,4 +1,3 @@ -import { React } from "react"; import { useTranslation } from "react-i18next"; import SelectColumnHoc from "../util/select-column-hoc"; import ColumnHoc from "../util/column-hoc"; @@ -6,12 +5,6 @@ import ColumnHoc from "../util/column-hoc"; /** * Columns for Users lists. * - * @param {object} props - The props. - * @param {Function} props.apiCall - The api to call - * @param {string} props.infoModalRedirect - The url for redirecting in the info modal. - * @param {string} props.infoModalTitle - The info modal title. - * @param {string} props.dataKey The data key for mapping the data. - * @param {Function} props.handleDelete * @returns {object} The columns for the users lists. */ function getUserColumns() { From 7fda6678ef7745905758af10f634d67881a459bd Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Fri, 1 Sep 2023 11:53:14 +0200 Subject: [PATCH 032/115] DISPLAY-1009: add correct link to button --- src/components/users/users-list.jsx | 2 +- src/translations/da/common.json | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/users/users-list.jsx b/src/components/users/users-list.jsx index c8972c6a..0859e286 100644 --- a/src/components/users/users-list.jsx +++ b/src/components/users/users-list.jsx @@ -125,7 +125,7 @@ function UsersList() { <> diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 40cc3333..3b935451 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -341,6 +341,18 @@ "save-group-error": "Der skete en fejl da gruppen skulle gemmes:" } }, + "user-create": { + "create-new-user-header": "Opret ny bruger", + "loading-messages": { + "saving-user": "Gemmer bruger" + }, + "success-messages": { + "saved-user": "Brugeren er gemt" + }, + "error-messages": { + "save-user-error": "Der skete en fejl da brugeren skulle gemmes:" + } + }, "group-edit": { "edit-group-header": "Rediger følgende gruppe", "loading-messages": { From 622da312b752b06544dd418161cf342e1dfbfd0d Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Fri, 1 Sep 2023 13:59:00 +0200 Subject: [PATCH 033/115] DISPLAY-1009: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe83ffc..945a93aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- [#213](https://github.com/os2display/display-admin-client/pull/213) + add focus trap, to trap the keyboard focus in our modals + add user list + add user create + add user edit + add a bunch of todos - [#212](https://github.com/os2display/display-admin-client/pull/212) update color of login background - [#211](https://github.com/os2display/display-admin-client/pull/211) From 548b36e58767cda2420d5b6e759e93ecf33a26a8 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:54:52 +0200 Subject: [PATCH 034/115] DISPLAY-993: Fixed OIDC login buttons --- CHANGELOG.md | 14 +- src/components/user/login.jsx | 242 ++++++++++++----------------- src/components/user/oidc-login.jsx | 76 +++++++++ src/translations/da/common.json | 7 +- 4 files changed, 184 insertions(+), 155 deletions(-) create mode 100644 src/components/user/oidc-login.jsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe83ffc..71b340c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,16 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- [#213](https://github.com/os2display/display-admin-client/pull/213) + Fixed OIDC login buttons. - [#212](https://github.com/os2display/display-admin-client/pull/212) - update color of login background + Updated color of login background - [#211](https://github.com/os2display/display-admin-client/pull/211) - update login page - add todo for button with mitid - clean up translations a bit - fix some WCAG2.1 issues - add os2display logo + Updated login page + Added todo for button with mitid + Cleaned up translations a bit + Fixed some WCAG2.1 issues + Added os2display logo - [#210](https://github.com/os2display/display-admin-client/pull/210) Use thumbnails for media list if they are set diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index e6157155..db8864af 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -1,14 +1,13 @@ import { React, useEffect, useState, useContext } from "react"; -import { Alert, Button, Form, Row } from "react-bootstrap"; +import { Button, Form, Row } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; -import { Link, useLocation } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import queryString from "query-string"; import Col from "react-bootstrap/Col"; import { MultiSelect } from "react-multi-select-component"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCity } from "@fortawesome/free-solid-svg-icons"; -import LoadingComponent from "../util/loading-component/loading-component"; import UserContext from "../../context/user-context"; import FormInput from "../util/forms/form-input"; import { api } from "../../redux/api/api.generated"; @@ -18,6 +17,7 @@ import localStorageKeys from "../util/local-storage-keys"; import LoginSidebar from "../navigation/login-sidebar/login-sidebar"; import MitIdLogo from "./mitid-logo.svg"; import "./login.scss"; +import OIDCLogin from "./oidc-login"; /** * Login component @@ -37,9 +37,6 @@ function Login() { const [error, setError] = useState(false); const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); - const [oidcAuthUrls, setOidcAuthUrls] = useState(""); - const [oidcAuthLoadingError, setOidcAuthLoadingError] = useState(""); - const [ready, setReady] = useState(false); /** * Login, both called from oidc login and manuel login. @@ -140,6 +137,7 @@ function Login() { if (search) { const query = queryString.parse(search); + idToken = query.id_token; state = query.state; } @@ -161,38 +159,7 @@ function Login() { } } }) - .catch(() => { - if (isMounted) { - setOidcAuthLoadingError(t("error-oidc-login")); - } - }) - .finally(() => { - if (isMounted) { - setReady(true); - } - }); - } else { - fetch(`${config.api}v1/authentication/oidc/urls?providerKey=oidc`, { - mode: "cors", - credentials: "include", - }) - .then((resp) => { - resp.json().then((data) => { - if (isMounted) { - setOidcAuthUrls(data); - } - }); - }) - .catch(() => { - if (isMounted) { - setOidcAuthLoadingError(t("error-fetching-oidc-urls")); - } - }) - .finally(() => { - if (isMounted) { - setReady(true); - } - }); + .catch(() => {}); } }); @@ -203,120 +170,103 @@ function Login() { return ( <> - {ready && ( -
- - - - - - -

{t("login-header")}

-

- {t("oidc-mit-id-header")} -

-
- - {oidcAuthUrls.authorizationUrl && ( - - - {t("login-with-oidc")} - - )} -
- {oidcAuthLoadingError && ( - {oidcAuthLoadingError} + } + /> +
+ +

+ {t("os2-display-user-header")} +

+ + + {!context.tenants.get && ( + <> + setEmail(ev.target.value)} + value={email} + name="email" + label={t("email")} + required + /> + setPassword(ev.target.value)} + value={password} + name="password" + label={t("password")} + type="password" + required + /> + + )} -

- {t("os2-display-user-header")} -

- <> - {!context.tenants.get && ( - <> - setEmail(ev.target.value)} - value={email} - name="email" - label={t("email")} - required - /> - 1 && ( +
+ + {t("select-tenant-label")} + + { + return { + label: item.title, + value: item.tenantKey, + }; + }) || [] } - onChange={(ev) => setPassword(ev.target.value)} - value={password} - name="password" - label={t("password")} - type="password" - required + hasSelectAll={false} + onChange={onSelectTenant} + className="single-select" + labelledBy="tenant" /> - - + {t("tenant-help-text")} +
)} - {!context.selectedTenant.get && - context.tenants.get?.length > 1 && ( -
- - {t("select-tenant-label")} - - { - return { - label: item.title, - value: item.tenantKey, - }; - }) || [] - } - hasSelectAll={false} - onChange={onSelectTenant} - className="single-select" - labelledBy="tenant" - /> - {t("tenant-help-text")} -
- )} - - - -
- )} - {!ready && ( - - )} +
+ + +
); } diff --git a/src/components/user/oidc-login.jsx b/src/components/user/oidc-login.jsx new file mode 100644 index 00000000..ab8b1e29 --- /dev/null +++ b/src/components/user/oidc-login.jsx @@ -0,0 +1,76 @@ +import { React, useEffect, useState } from "react"; +import { Alert, Spinner } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; +import "./login.scss"; +import PropTypes from "prop-types"; +import ConfigLoader from "../../config-loader"; + +/** + * OIDC Login component + * + * @param {object} props The props + * @param {string} props.providerKey The provider key + * @param {string} props.text Button text + * @param {object} props.icon Button icon + * @returns {object} - The component + */ +function OIDCLogin({ providerKey, text, icon }) { + // Hooks + const { t } = useTranslation("common", { keyPrefix: "oidc-login" }); + + // State + const [oidcAuthUrl, setOidcAuthUrl] = useState(""); + const [oidcAuthLoadingError, setOidcAuthLoadingError] = useState(""); + + useEffect(() => { + ConfigLoader.loadConfig().then((config) => { + fetch( + `${config.api}v1/authentication/oidc/urls?providerKey=${providerKey}`, + { + mode: "cors", + credentials: "include", + } + ) + .then((resp) => { + resp.json().then((data) => { + setOidcAuthUrl(data.authorizationUrl); + }); + }) + .catch(() => { + setOidcAuthLoadingError(t("error-fetching-oidc-urls")); + }); + }); + + return () => {}; + }, []); + + return ( + <> + {!oidcAuthUrl && ( + + )} + {oidcAuthUrl !== "" && ( + + {icon} + {text} + + )} + {oidcAuthLoadingError !== "" && ( + {oidcAuthLoadingError} + )} + + ); +} + +OIDCLogin.propTypes = { + providerKey: PropTypes.string.isRequired, + icon: PropTypes.element.isRequired, + text: PropTypes.string.isRequired, +}; + +export default OIDCLogin; diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 62697cc7..4f50e174 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -22,7 +22,7 @@ }, "login-sidebar": { "AD-info-text": "Er du medarbejder skal du benytte AD login.", - "MitID-info-text": "Er du borger skal du benytte MitID" + "MitID-info-text": "Er du borger skal du benytte MitID login." }, "slides-list": { "columns": { @@ -745,7 +745,6 @@ "submit": "Log ind", "error-oidc-login": "Log ind med AD fejlede", "error-fetching-oidc-urls": "Opsætning af AD login fejlede", - "login-with-oidc": "AD", "login-with-mitid-aria-label": "Log ind med mitid", "oidc-mit-id-header": "Adgangsservice", "os2-display-user-header": "OS2displaybruger", @@ -753,7 +752,9 @@ "login-with-oidc-aria-label": "Log ind med AD", "login-with-username-password": "Log ind med email og kodeord", "select-tenant-label": "Vælg lokation", - "tenant-help-text": "Du er tilknyttet flere lokationer, og lokationen kan blive ændret igen når du er logget ind" + "tenant-help-text": "Du er tilknyttet flere lokationer, og lokationen kan blive ændret igen når du er logget ind", + "login-with-ad": "AD login", + "login-with-external": " login" }, "file-dropzone": { "drag-and-drop-text": "Vælg filer ved at trykke på eller trække filer ind i firkanten", From 6e24f7dace9ba13906536dab98c9e3313d0c60ac Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:08:27 +0200 Subject: [PATCH 035/115] DISPLAY-993: Fixed login flow --- src/components/user/login.jsx | 238 ++++++++++++++++++-------------- src/index.js | 8 +- src/translations/da/common.json | 5 +- 3 files changed, 137 insertions(+), 114 deletions(-) diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index db8864af..1e429e7f 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -37,28 +37,31 @@ function Login() { const [error, setError] = useState(false); const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); + const [loggedIn, setLoggedIn] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); /** * Login, both called from oidc login and manuel login. * * @param {object} data - Login data */ - function login(data) { + const login = (data) => { // Set token in local storage, to persist login on refresh localStorage.setItem(localStorageKeys.API_TOKEN, data.token); context.userName.set(data.user?.fullname); context.email.set(data.user?.email); localStorage.setItem(localStorageKeys.USER_NAME, data.user?.fullname); localStorage.setItem(localStorageKeys.EMAIL, data.user?.email); + // If there are more than one tenant, the user should pick a tenant - if (data.tenants?.length > 1) { + if ((data.tenants?.length ?? 0) > 1) { // Save tenants localStorage.setItem( localStorageKeys.TENANTS, JSON.stringify(data.tenants) ); context.tenants.set(data.tenants); - } else if (data.tenants?.length > 0) { + } else if ((data.tenants?.length ?? 0) > 0) { // authenticated, and use the only received tenant. context.authenticated.set(true); localStorage.setItem( @@ -70,7 +73,9 @@ function Login() { setError(true); displayError(t("missing-tenants")); } - } + + setLoggedIn(true); + }; /** * Select tenant function @@ -132,36 +137,41 @@ function Login() { useEffect(() => { let isMounted = true; - let idToken = null; + let code = null; let state = null; if (search) { const query = queryString.parse(search); - idToken = query.id_token; + code = query.code; state = query.state; - } - ConfigLoader.loadConfig().then((config) => { - if (state && idToken) { - fetch( - `${config.api}v1/authentication/oidc/token?state=${state}&id_token=${idToken}`, - { + if (state && code) { + ConfigLoader.loadConfig().then((config) => { + const searchParams = new URLSearchParams({ + state, + code, + }); + + fetch(`${config.api}v1/authentication/oidc/token?${searchParams}`, { mode: "cors", credentials: "include", - } - ) - .then((resp) => resp.json()) - .then((data) => { - if (isMounted) { - if (data?.token) { - login(data); - } - } }) - .catch(() => {}); + .then((resp) => resp.json()) + .then((data) => { + if (data.code !== 200) { + setErrorMessage(data.message); + } + + if (isMounted) { + if (data?.token) { + login(data); + } + } + }); + }); } - }); + } return () => { isMounted = false; @@ -178,93 +188,107 @@ function Login() { > - -
-

{t("login-header")}

- -

- {t("oidc-mit-id-header")} -

- -
- } - /> - - } - /> -
-

- {t("os2-display-user-header")} -

- -
- {!context.tenants.get && ( - <> - setEmail(ev.target.value)} - value={email} - name="email" - label={t("email")} - required - /> - setPassword(ev.target.value)} - value={password} - name="password" - label={t("password")} - type="password" - required - /> - - - )} + {errorMessage !== "" &&
123123{errorMessage}
} - {!context.selectedTenant.get && - context.tenants.get?.length > 1 && ( -
- - {t("select-tenant-label")} - - { - return { - label: item.title, - value: item.tenantKey, - }; - }) || [] - } - hasSelectAll={false} - onChange={onSelectTenant} - className="single-select" - labelledBy="tenant" - /> - {t("tenant-help-text")} + {loggedIn && ( + <> + {!context.selectedTenant.get && + (context.tenants.get.length ?? 0) > 1 && ( + +
+

{t("logged-in-select-tenant")}

+ +
+ + {t("select-tenant-label")} + + + { + return { + label: item.title, + value: item.tenantKey, + }; + }) || [] + } + hasSelectAll={false} + onChange={onSelectTenant} + className="single-select" + labelledBy="tenant" + /> + {t("tenant-help-text")} +
- )} - -
- + + )} + + )} + + {!loggedIn && ( + +
+

{t("login-header")}

+ +

+ {t("oidc-mit-id-header")} +

+ +
+ } + /> + + } + /> +
+ +

+ {t("os2-display-user-header")} +

+ +
+ setEmail(ev.target.value)} + value={email} + name="email" + label={t("email")} + required + /> + + setPassword(ev.target.value)} + value={password} + name="password" + label={t("password")} + type="password" + required + /> + + + +
+ + )}
diff --git a/src/index.js b/src/index.js index 71232d41..fa4d7780 100644 --- a/src/index.js +++ b/src/index.js @@ -10,10 +10,8 @@ const root = createRoot(container); root.render( - - - - - + + + ); diff --git a/src/translations/da/common.json b/src/translations/da/common.json index 4f50e174..d4bea06a 100644 --- a/src/translations/da/common.json +++ b/src/translations/da/common.json @@ -747,14 +747,15 @@ "error-fetching-oidc-urls": "Opsætning af AD login fejlede", "login-with-mitid-aria-label": "Log ind med mitid", "oidc-mit-id-header": "Adgangsservice", - "os2-display-user-header": "OS2displaybruger", + "os2-display-user-header": "OS2display-bruger", "login-header": "Log ind", "login-with-oidc-aria-label": "Log ind med AD", "login-with-username-password": "Log ind med email og kodeord", "select-tenant-label": "Vælg lokation", "tenant-help-text": "Du er tilknyttet flere lokationer, og lokationen kan blive ændret igen når du er logget ind", "login-with-ad": "AD login", - "login-with-external": " login" + "login-with-external": " login", + "logged-in-select-tenant": "Vælg lokation." }, "file-dropzone": { "drag-and-drop-text": "Vælg filer ved at trykke på eller trække filer ind i firkanten", From ec4b905b5cb5b0900b4cb829e8dd027b192ad480 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:09:54 +0200 Subject: [PATCH 036/115] DISPLAY-993: Cleaned up manifest.json --- public/manifest.json | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/public/manifest.json b/public/manifest.json index 080d6c77..41ed6808 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,21 +1,11 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "OS2Display admin", + "name": "OS2Display admin", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" } ], "start_url": ".", From 066a62d4744b78ce21d1584f3d76be49ae81d5f0 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:21:45 +0200 Subject: [PATCH 037/115] DISPLAY-993: Cleaned up DOM --- src/components/user/login.jsx | 201 +++++++++++++++++----------------- 1 file changed, 101 insertions(+), 100 deletions(-) diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 1e429e7f..6b09447d 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -1,5 +1,5 @@ import { React, useEffect, useState, useContext } from "react"; -import { Button, Form, Row } from "react-bootstrap"; +import { Alert, Button, Form, Row } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; import { useLocation } from "react-router-dom"; @@ -111,8 +111,8 @@ function Login() { api.endpoints.postCredentialsItem.initiate({ credentials: JSON.stringify({ email, - password, - }), + password + }) }) ) .then((response) => { @@ -150,12 +150,12 @@ function Login() { ConfigLoader.loadConfig().then((config) => { const searchParams = new URLSearchParams({ state, - code, + code }); fetch(`${config.api}v1/authentication/oidc/token?${searchParams}`, { mode: "cors", - credentials: "include", + credentials: "include" }) .then((resp) => resp.json()) .then((data) => { @@ -189,106 +189,107 @@ function Login() { - {errorMessage !== "" &&
123123{errorMessage}
} + +
- {loggedIn && ( - <> - {!context.selectedTenant.get && + {errorMessage && errorMessage !== "" && ( +
+ {errorMessage} +
+ )} + + {loggedIn && + !context.selectedTenant.get && (context.tenants.get.length ?? 0) > 1 && ( - -
-

{t("logged-in-select-tenant")}

- -
- - {t("select-tenant-label")} - - - { - return { - label: item.title, - value: item.tenantKey, - }; - }) || [] - } - hasSelectAll={false} - onChange={onSelectTenant} - className="single-select" - labelledBy="tenant" - /> - {t("tenant-help-text")} -
+ <> +

{t("logged-in-select-tenant")}

+ +
+ + {t("select-tenant-label")} + + + { + return { + label: item.title, + value: item.tenantKey + }; + }) || [] + } + hasSelectAll={false} + onChange={onSelectTenant} + className="single-select" + labelledBy="tenant" + /> + {t("tenant-help-text")}
- + )} - - )} - - {!loggedIn && ( - -
-

{t("login-header")}

- -

- {t("oidc-mit-id-header")} -

- -
- } - /> - - } - /> -
-

- {t("os2-display-user-header")} -

- -
- setEmail(ev.target.value)} - value={email} - name="email" - label={t("email")} - required - /> - - setPassword(ev.target.value)} - value={password} - name="password" - label={t("password")} - type="password" - required - /> - - - -
- - )} + {!loggedIn && (<> +

{t("login-header")}

+ +

+ {t("oidc-mit-id-header")} +

+ +
+ } + /> + + } + /> +
+ +

+ {t("os2-display-user-header")} +

+ +
+ setEmail(ev.target.value)} + value={email} + name="email" + label={t("email")} + required + /> + + setPassword(ev.target.value)} + value={password} + name="password" + label={t("password")} + type="password" + required + /> + + + + + )} +
+
From 3f916ac3749b3e797760dbfe899416babd9066ff Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:12:14 +0200 Subject: [PATCH 038/115] DISPLAY-993: Set spinner when not ready --- docker-compose.yml | 2 +- src/components/user/login.jsx | 244 ++++++++++++++++------------- src/components/user/oidc-login.jsx | 2 +- 3 files changed, 135 insertions(+), 113 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d4651b0c..88ed8afb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: node: image: node:14 - command: npm start + command: yarn start networks: - app working_dir: /app diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 6b09447d..2b5bdd0c 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -1,5 +1,5 @@ import { React, useEffect, useState, useContext } from "react"; -import { Alert, Button, Form, Row } from "react-bootstrap"; +import { Button, Form, Row } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; import { useLocation } from "react-router-dom"; @@ -18,6 +18,7 @@ import LoginSidebar from "../navigation/login-sidebar/login-sidebar"; import MitIdLogo from "./mitid-logo.svg"; import "./login.scss"; import OIDCLogin from "./oidc-login"; +import LoadingComponent from "../util/loading-component/loading-component"; /** * Login component @@ -34,6 +35,7 @@ function Login() { const context = useContext(UserContext); // Local stage + const [ready, setReady] = useState(false); const [error, setError] = useState(false); const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); @@ -111,8 +113,8 @@ function Login() { api.endpoints.postCredentialsItem.initiate({ credentials: JSON.stringify({ email, - password - }) + password, + }), }) ) .then((response) => { @@ -150,12 +152,12 @@ function Login() { ConfigLoader.loadConfig().then((config) => { const searchParams = new URLSearchParams({ state, - code + code, }); fetch(`${config.api}v1/authentication/oidc/token?${searchParams}`, { mode: "cors", - credentials: "include" + credentials: "include", }) .then((resp) => resp.json()) .then((data) => { @@ -168,9 +170,16 @@ function Login() { login(data); } } + }) + .finally(() => { + setReady(true); }); }); + } else { + setReady(true); } + } else { + setReady(true); } return () => { @@ -180,118 +189,131 @@ function Login() { return ( <> -
- - - - - - -
- - {errorMessage && errorMessage !== "" && ( -
- {errorMessage} -
- )} - - {loggedIn && - !context.selectedTenant.get && - (context.tenants.get.length ?? 0) > 1 && ( + {ready && ( +
+ + + + + + +
+ {errorMessage && errorMessage !== "" && ( +
+ {errorMessage} +
+ )} + + {loggedIn && + !context.selectedTenant.get && + (context.tenants.get.length ?? 0) > 1 && ( + <> +

{t("logged-in-select-tenant")}

+ +
+ + {t("select-tenant-label")} + + + { + return { + label: item.title, + value: item.tenantKey, + }; + }) || [] + } + hasSelectAll={false} + onChange={onSelectTenant} + className="single-select" + labelledBy="tenant" + /> + {t("tenant-help-text")} +
+ + )} + + {!loggedIn && ( <> -

{t("logged-in-select-tenant")}

- -
- - {t("select-tenant-label")} - - - { - return { - label: item.title, - value: item.tenantKey - }; - }) || [] +

{t("login-header")}

+ +

+ {t("oidc-mit-id-header")} +

+ +
+ + } + /> + } - hasSelectAll={false} - onChange={onSelectTenant} - className="single-select" - labelledBy="tenant" /> - {t("tenant-help-text")}
- - )} - {!loggedIn && (<> -

{t("login-header")}

- -

- {t("oidc-mit-id-header")} -

- -
- } - /> - - } - /> -
+

+ {t("os2-display-user-header")} +

-

- {t("os2-display-user-header")} -

- -
- setEmail(ev.target.value)} - value={email} - name="email" - label={t("email")} - required - /> - - setPassword(ev.target.value)} - value={password} - name="password" - label={t("password")} - type="password" - required - /> - - - - - )} -
- - -
+
+ setEmail(ev.target.value)} + value={email} + name="email" + label={t("email")} + required + /> + + setPassword(ev.target.value)} + value={password} + name="password" + label={t("password")} + type="password" + required + /> + + + + + )} +
+ + +
+ )} + + {!ready && ( + + )} ); } diff --git a/src/components/user/oidc-login.jsx b/src/components/user/oidc-login.jsx index ab8b1e29..5796e607 100644 --- a/src/components/user/oidc-login.jsx +++ b/src/components/user/oidc-login.jsx @@ -47,7 +47,7 @@ function OIDCLogin({ providerKey, text, icon }) { return ( <> {!oidcAuthUrl && ( - + )} {oidcAuthUrl !== "" && ( Date: Wed, 6 Sep 2023 10:28:55 +0200 Subject: [PATCH 039/115] DISPLAY-1009: add externalusers to hardcoded access config --- src/app.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app.jsx b/src/app.jsx index 00c44459..6f03b69d 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -142,6 +142,9 @@ function App() { shared: { roles: ["ROLE_ADMIN"], }, + externalUsers: { + roles: ["ROLE_ADMIN"], + }, }); }); }, []); From fcc6536906bc6e31ba68eb76d60be4954c1fd3d6 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Wed, 6 Sep 2023 10:44:02 +0200 Subject: [PATCH 040/115] DISPLAY-1009: fix tests --- src/components/navigation/topbar/top-bar.spec.js | 2 +- src/components/user/login.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/navigation/topbar/top-bar.spec.js b/src/components/navigation/topbar/top-bar.spec.js index 447f2f70..56278f8a 100644 --- a/src/components/navigation/topbar/top-bar.spec.js +++ b/src/components/navigation/topbar/top-bar.spec.js @@ -87,7 +87,7 @@ describe("Nav items loads", () => { cy.get("#topbar_signout").should("not.be.visible"); cy.get("#basic-navbar-nav-burger").click(); cy.get("#basic-navbar-nav").should("exist"); - cy.get("#basic-navbar-nav").find(".nav-item").should("have.length", 11); + cy.get("#basic-navbar-nav").find(".nav-item").should("have.length", 12); cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 3); cy.get("#basic-navbar-nav").find("#topbar_signout").should("be.visible"); }); diff --git a/src/components/user/login.spec.js b/src/components/user/login.spec.js index 2952a198..1a4d7ad5 100644 --- a/src/components/user/login.spec.js +++ b/src/components/user/login.spec.js @@ -104,6 +104,6 @@ describe("Login works", () => { cy.get("#login").click(); cy.wait(["@token"]); cy.get(".name").should("have.text", "John Doe (ABC Tenant)"); - cy.get(".sidebar-nav").find(".nav-item").should("have.length", 9); + cy.get(".sidebar-nav").find(".nav-item").should("have.length", 10); }); }); From 2129e0571d116b26ce44b1adef6fd96268f8f9bc Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Wed, 6 Sep 2023 11:21:48 +0200 Subject: [PATCH 041/115] DISPLAY-1009: cypress test --- src/components/navigation/topbar/top-bar.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/navigation/topbar/top-bar.spec.js b/src/components/navigation/topbar/top-bar.spec.js index 56278f8a..e6f63252 100644 --- a/src/components/navigation/topbar/top-bar.spec.js +++ b/src/components/navigation/topbar/top-bar.spec.js @@ -88,7 +88,7 @@ describe("Nav items loads", () => { cy.get("#basic-navbar-nav-burger").click(); cy.get("#basic-navbar-nav").should("exist"); cy.get("#basic-navbar-nav").find(".nav-item").should("have.length", 12); - cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 3); + cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 4); cy.get("#basic-navbar-nav").find("#topbar_signout").should("be.visible"); }); }); From c7fd2aea603a6ed30d947419723c28799c8a0858 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 7 Sep 2023 10:34:32 +0200 Subject: [PATCH 042/115] DISPLAY-1024: remove tenantselect from topbar --- src/components/navigation/topbar/top-bar.jsx | 99 ++------------------ 1 file changed, 6 insertions(+), 93 deletions(-) diff --git a/src/components/navigation/topbar/top-bar.jsx b/src/components/navigation/topbar/top-bar.jsx index 57820ef5..6ad380e1 100644 --- a/src/components/navigation/topbar/top-bar.jsx +++ b/src/components/navigation/topbar/top-bar.jsx @@ -1,8 +1,8 @@ -import { React, useContext, useEffect, useState } from "react"; +import { React, useContext } from "react"; import Nav from "react-bootstrap/Nav"; import Dropdown from "react-bootstrap/Dropdown"; import Navbar from "react-bootstrap/Navbar"; -import { Link, useLocation } from "react-router-dom"; +import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { @@ -10,16 +10,11 @@ import { faPhotoVideo, faDesktop, faStream, - faUserCircle, faSignOutAlt, - faCheck, } from "@fortawesome/free-solid-svg-icons"; -import { useDispatch } from "react-redux"; import NavItems from "../nav-items/nav-items"; import UserContext from "../../../context/user-context"; -import localStorageKeys from "../../util/local-storage-keys"; -import { api } from "../../../redux/api/api.generated"; -import { displayError } from "../../util/list/toast-component/display-toast"; + import "./top-bar.scss"; /** @@ -30,35 +25,6 @@ import "./top-bar.scss"; function TopBar() { const { t } = useTranslation("common"); const context = useContext(UserContext); - const location = useLocation(); - const dispatch = useDispatch(); - const [tenantChangeDisabled, setTenantChangeDisabled] = useState(false); - - /** - * Change tenant on select tenant - * - * @param {object} props - The props. - * @param {object} props.target Event target - */ - function onTenantChange({ target }) { - dispatch(api.endpoints.tenantChangedClearCache.initiate()); - context.selectedTenant.set( - context.tenants.get.find((tenant) => tenant.tenantKey === target.id) - ); - localStorage.setItem( - localStorageKeys.SELECTED_TENANT, - JSON.stringify( - context.tenants.get.find((tenant) => tenant.tenantKey === target.id) - ) - ); - } - - useEffect(() => { - const { pathname } = location; - const matches = pathname.match(/(\/edit|\/create)/i); - - setTenantChangeDisabled(matches !== null); - }, [location]); return ( {t("topbar.brand")} - <> - {!context.tenants.get && ( -
- {context.userName.get} ({context.selectedTenant.get?.title}) -
- )} - {context.tenants?.get && ( - - - - - {context.userName?.get} ({context.selectedTenant?.get?.title}) - - - - {context.tenants.get.map((tenant) => ( - { - if (tenantChangeDisabled) { - displayError( - t(`topbar.error-messages.tenant-change-disabled`), - null - ); - } else { - onTenantChange(target); - } - }} - id={tenant.tenantKey} - key={tenant.tenantKey} - className="dropdown-item" - > - - {tenant.title} - - ))} - - - )} - +
+ {context.userName.get} ({context.selectedTenant.get?.title}) +
Date: Thu, 7 Sep 2023 10:35:03 +0200 Subject: [PATCH 043/115] DISPLAY-1024: add tenant select to navbar --- .../navigation/nav-items/nav-items.jsx | 96 ++++++++++++++++--- .../navigation/nav-items/nav-items.scss | 11 +++ src/translations/da/common.json | 2 + 3 files changed, 95 insertions(+), 14 deletions(-) diff --git a/src/components/navigation/nav-items/nav-items.jsx b/src/components/navigation/nav-items/nav-items.jsx index 33e8e275..ea77aa4a 100644 --- a/src/components/navigation/nav-items/nav-items.jsx +++ b/src/components/navigation/nav-items/nav-items.jsx @@ -1,5 +1,5 @@ -import { React, useContext, useEffect } from "react"; -import { Nav } from "react-bootstrap"; +import { React, useContext, useEffect, useState } from "react"; +import { Nav, NavDropdown } from "react-bootstrap"; import { Link, NavLink, useLocation } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -10,11 +10,16 @@ import { faPhotoVideo, faPlusCircle, faCog, + faHome, + faCheck, } from "@fortawesome/free-solid-svg-icons"; +import { useDispatch } from "react-redux"; import ListContext from "../../../context/list-context"; import UserContext from "../../../context/user-context"; import useModal from "../../../context/modal-context/modal-context-hook"; import RestrictedNavRoute from "./restricted-nav-route"; +import localStorageKeys from "../../util/local-storage-keys"; +import { api } from "../../../redux/api/api.generated"; import "./nav-items.scss"; /** @@ -23,14 +28,37 @@ import "./nav-items.scss"; * @returns {object} Nav items */ function NavItems() { + const { SELECTED_TENANT } = localStorageKeys; + const [tenantDropdownDisabled, setTenantDropdownDisabled] = useState(false); const { t } = useTranslation("common", { keyPrefix: "nav-items" }); const { setSelected } = useModal(); - const context = useContext(UserContext); const { page, createdBy, isPublished } = useContext(ListContext); const { pathname } = useLocation(); + const { + tenants: { get: tenants }, + selectedTenant: { set: setSelectedTenant, get: selectedTenant }, + accessConfig: { get: accessConfig }, + } = useContext(UserContext); + + const dispatch = useDispatch(); + + /** + * Change tenant on select tenant + * + * @param {object} props - The props. + * @param {object} props.target Event target + */ + function onTenantChange({ target }) { + dispatch(api.endpoints.tenantChangedClearCache.initiate()); + const newTenant = tenants.find(({ tenantKey }) => tenantKey === target.id); + setSelectedTenant(newTenant); + localStorage.setItem(SELECTED_TENANT, JSON.stringify(newTenant)); + } // Reset list context and selected on page change. useEffect(() => { + const matches = pathname.match(/(\/edit|\/create)/i); + setTenantDropdownDisabled(matches !== null); setSelected([]); if (page) { page.set(1); @@ -45,7 +73,7 @@ function NavItems() { return ( <> - {context.accessConfig?.get && ( + {accessConfig && ( <> - + - + - + - + - + `nav-link ${isActive ? "disabled" : ""}` } @@ -179,7 +205,49 @@ function NavItems() { - + + + + `nav-link d-flex ${isActive ? "disabled" : ""}` + } + to="/locations/list" + > + + {t("locations")} + + {tenants && tenants.length > 1 && ( + + {tenants.map(({ tenantKey, title }) => ( + onTenantChange(target)} + disabled={tenantDropdownDisabled} + id={tenantKey} + key={tenantKey} + > + + {title} + + ))} + + )} + + + - + Date: Thu, 7 Sep 2023 10:35:12 +0200 Subject: [PATCH 044/115] DISPLAY-1024: add locations in accessconfig --- src/app.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app.jsx b/src/app.jsx index 6f03b69d..84e2b000 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -145,6 +145,9 @@ function App() { externalUsers: { roles: ["ROLE_ADMIN"], }, + locations: { + roles: ["ROLE_ADMIN"], + }, }); }); }, []); From 52736b654c94d0ab9a0fc107e98533f6fbd67fc4 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 7 Sep 2023 12:35:21 +0200 Subject: [PATCH 045/115] DISPLAY-1024: move locations i navbar + mobile look --- .../navigation/nav-items/nav-items.jsx | 84 +++++++++---------- .../navigation/nav-items/nav-items.scss | 10 ++- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/components/navigation/nav-items/nav-items.jsx b/src/components/navigation/nav-items/nav-items.jsx index ea77aa4a..3af0a6e1 100644 --- a/src/components/navigation/nav-items/nav-items.jsx +++ b/src/components/navigation/nav-items/nav-items.jsx @@ -75,6 +75,48 @@ function NavItems() { <> {accessConfig && ( <> + + + + `nav-link d-flex ${isActive ? "disabled" : ""}` + } + to="/locations/list" + > + + {t("locations")} + + {tenants && tenants.length > 1 && ( + + {tenants.map(({ tenantKey, title }) => ( + onTenantChange(target)} + disabled={tenantDropdownDisabled} + id={tenantKey} + key={tenantKey} + > + + {title} + + ))} + + )} + + - - - - `nav-link d-flex ${isActive ? "disabled" : ""}` - } - to="/locations/list" - > - - {t("locations")} - - {tenants && tenants.length > 1 && ( - - {tenants.map(({ tenantKey, title }) => ( - onTenantChange(target)} - disabled={tenantDropdownDisabled} - id={tenantKey} - key={tenantKey} - > - - {title} - - ))} - - )} - - Date: Thu, 7 Sep 2023 12:48:34 +0200 Subject: [PATCH 046/115] DISPLAY-1024: cypress tests --- cypress.json | 2 +- src/components/navigation/topbar/top-bar.spec.js | 4 ++-- src/components/user/login.spec.js | 10 +++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cypress.json b/cypress.json index 2f133862..ed6d6219 100644 --- a/cypress.json +++ b/cypress.json @@ -6,7 +6,7 @@ "viewportHeight": 4000, "defaultCommandTimeout": 10000, "retries": { - "runMode": 3, + "runMode": 2, "openMode": 0 } } diff --git a/src/components/navigation/topbar/top-bar.spec.js b/src/components/navigation/topbar/top-bar.spec.js index e6f63252..f22f62da 100644 --- a/src/components/navigation/topbar/top-bar.spec.js +++ b/src/components/navigation/topbar/top-bar.spec.js @@ -87,8 +87,8 @@ describe("Nav items loads", () => { cy.get("#topbar_signout").should("not.be.visible"); cy.get("#basic-navbar-nav-burger").click(); cy.get("#basic-navbar-nav").should("exist"); - cy.get("#basic-navbar-nav").find(".nav-item").should("have.length", 12); - cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 4); + cy.get("#basic-navbar-nav").find(".nav-item").should("have.length", 13); + cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 5); cy.get("#basic-navbar-nav").find("#topbar_signout").should("be.visible"); }); }); diff --git a/src/components/user/login.spec.js b/src/components/user/login.spec.js index 1a4d7ad5..ef19fc0f 100644 --- a/src/components/user/login.spec.js +++ b/src/components/user/login.spec.js @@ -42,11 +42,15 @@ describe("Login works", () => { cy.wait(["@token"]); - cy.get(".user-dropdown-name").should("have.text", "John Doe (ABC Tenant)"); - cy.get(".user-dropdown").get("#topbar_user").click(); + cy.get(".topbar") + .find(".name") + .should("have.text", "John Doe (ABC Tenant)"); + cy.get(".dropdown-toggle").find(".dropdown-toggle").click(); cy.get("#DEF").click(); cy.wait(["@groups-two"]); - cy.get(".user-dropdown-name").should("have.text", "John Doe (DEF Tenant)"); + cy.get(".topbar") + .find(".name") + .should("have.text", "John Doe (DEF Tenant)"); }); it("Login with tenant that has role editor", () => { From b45a21d57f1de1ece0a06e276b14fb74f7bfed5f Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 7 Sep 2023 13:03:22 +0200 Subject: [PATCH 047/115] DISPLAY-1024: cypress --- CHANGELOG.md | 2 ++ src/components/user/login.spec.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b289df46..ac363477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- [#215](https://github.com/os2display/display-admin-client/pull/217) + Move tenant dropdown. - [#215](https://github.com/os2display/display-admin-client/pull/215) Fixed OIDC login buttons. - [#213](https://github.com/os2display/display-admin-client/pull/213) diff --git a/src/components/user/login.spec.js b/src/components/user/login.spec.js index ef19fc0f..e9474442 100644 --- a/src/components/user/login.spec.js +++ b/src/components/user/login.spec.js @@ -45,7 +45,7 @@ describe("Login works", () => { cy.get(".topbar") .find(".name") .should("have.text", "John Doe (ABC Tenant)"); - cy.get(".dropdown-toggle").find(".dropdown-toggle").click(); + cy.get(".sidebar-nav").find(".dropdown-toggle").click(); cy.get("#DEF").click(); cy.wait(["@groups-two"]); cy.get(".topbar") @@ -108,6 +108,6 @@ describe("Login works", () => { cy.get("#login").click(); cy.wait(["@token"]); cy.get(".name").should("have.text", "John Doe (ABC Tenant)"); - cy.get(".sidebar-nav").find(".nav-item").should("have.length", 10); + cy.get(".sidebar-nav").find(".nav-item").should("have.length", 11); }); }); From 5b04c9020674d5d586778d25e120f1308e163c7c Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Thu, 7 Sep 2023 13:13:45 +0200 Subject: [PATCH 048/115] DISPLAY-1024: cypress tests --- src/components/navigation/topbar/top-bar.spec.js | 2 +- src/components/user/login.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/navigation/topbar/top-bar.spec.js b/src/components/navigation/topbar/top-bar.spec.js index f22f62da..3a7c1288 100644 --- a/src/components/navigation/topbar/top-bar.spec.js +++ b/src/components/navigation/topbar/top-bar.spec.js @@ -88,7 +88,7 @@ describe("Nav items loads", () => { cy.get("#basic-navbar-nav-burger").click(); cy.get("#basic-navbar-nav").should("exist"); cy.get("#basic-navbar-nav").find(".nav-item").should("have.length", 13); - cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 5); + cy.get("#basic-navbar-nav").find(".nav-add-new").should("have.length", 4); cy.get("#basic-navbar-nav").find("#topbar_signout").should("be.visible"); }); }); diff --git a/src/components/user/login.spec.js b/src/components/user/login.spec.js index e9474442..f2ee8240 100644 --- a/src/components/user/login.spec.js +++ b/src/components/user/login.spec.js @@ -42,13 +42,13 @@ describe("Login works", () => { cy.wait(["@token"]); - cy.get(".topbar") + cy.get("#topbar") .find(".name") .should("have.text", "John Doe (ABC Tenant)"); cy.get(".sidebar-nav").find(".dropdown-toggle").click(); cy.get("#DEF").click(); cy.wait(["@groups-two"]); - cy.get(".topbar") + cy.get("#topbar") .find(".name") .should("have.text", "John Doe (DEF Tenant)"); }); From 34f4ca018cd70dcb5a27bb2c7782676c73fe2585 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:42:07 +0200 Subject: [PATCH 049/115] DISPLAY-993: Added login methods to config --- .../itkdev/etc/confd/templates/config.tmpl | 7 +- .../etc/confd/templates/config.tmpl | 7 +- public/example_config.json | 7 +- src/components/user/login.jsx | 152 ++++++++++-------- src/config-loader.js | 5 + 5 files changed, 111 insertions(+), 67 deletions(-) diff --git a/infrastructure/itkdev/etc/confd/templates/config.tmpl b/infrastructure/itkdev/etc/confd/templates/config.tmpl index ec79a18c..d49f11ed 100644 --- a/infrastructure/itkdev/etc/confd/templates/config.tmpl +++ b/infrastructure/itkdev/etc/confd/templates/config.tmpl @@ -1,4 +1,9 @@ { "api": "{{ getenv "API_PATH" "/" }}", - "touchButtonRegions": "{{ getenv "APP_TOUCH_BUTTON_REGIONS" "false"}}" + "touchButtonRegions": "{{ getenv "APP_TOUCH_BUTTON_REGIONS" "false"}}", + "login": { + "ad": "{{ getenv "APP_ENABLE_AD_LOGIN" "true"}}", + "external": "{{ getenv "APP_ENABLE_EXTERNAL_LOGIN" "true"}}", + "usernamePassword": "{{ getenv "APP_ENABLE_USERNAME_PASSWORD_LOGIN" "true"}}" + } } diff --git a/infrastructure/os2display/etc/confd/templates/config.tmpl b/infrastructure/os2display/etc/confd/templates/config.tmpl index ec79a18c..a83ac02d 100644 --- a/infrastructure/os2display/etc/confd/templates/config.tmpl +++ b/infrastructure/os2display/etc/confd/templates/config.tmpl @@ -1,4 +1,9 @@ { "api": "{{ getenv "API_PATH" "/" }}", - "touchButtonRegions": "{{ getenv "APP_TOUCH_BUTTON_REGIONS" "false"}}" + "touchButtonRegions": "{{ getenv "APP_TOUCH_BUTTON_REGIONS" "false"}}", + "login": { + "ad": "{{ getenv "APP_ENABLE_AD_LOGIN" "false"}}", + "external": "{{ getenv "APP_ENABLE_EXTERNAL_LOGIN" "false"}}", + "usernamePassword": "{{ getenv "APP_ENABLE_USERNAME_PASSWORD_LOGIN" "true"}}" + } } diff --git a/public/example_config.json b/public/example_config.json index edd68924..636a89d1 100644 --- a/public/example_config.json +++ b/public/example_config.json @@ -1,4 +1,9 @@ { "api": "/", - "touchButtonRegions": false + "touchButtonRegions": false, + "login": { + "ad": true, + "external": true, + "usernamePassword": true + } } diff --git a/src/components/user/login.jsx b/src/components/user/login.jsx index 2b5bdd0c..07b749ac 100644 --- a/src/components/user/login.jsx +++ b/src/components/user/login.jsx @@ -34,13 +34,14 @@ function Login() { // Context const context = useContext(UserContext); - // Local stage + // Local state const [ready, setReady] = useState(false); const [error, setError] = useState(false); const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); const [loggedIn, setLoggedIn] = useState(false); const [errorMessage, setErrorMessage] = useState(""); + const [enabledLogins, setEnabledLogins] = useState(null); /** * Login, both called from oidc login and manuel login. @@ -113,8 +114,8 @@ function Login() { api.endpoints.postCredentialsItem.initiate({ credentials: JSON.stringify({ email, - password, - }), + password + }) }) ) .then((response) => { @@ -137,6 +138,17 @@ function Login() { }); }; + useEffect(() => { + ConfigLoader.loadConfig().then((config) => { + // Defaults to all enabled. + setEnabledLogins(config.login ?? { + "ad": true, + "external": true, + "usernamePassword": true + }); + }); + }, []); + useEffect(() => { let isMounted = true; let code = null; @@ -152,12 +164,12 @@ function Login() { ConfigLoader.loadConfig().then((config) => { const searchParams = new URLSearchParams({ state, - code, + code }); fetch(`${config.api}v1/authentication/oidc/token?${searchParams}`, { mode: "cors", - credentials: "include", + credentials: "include" }) .then((resp) => resp.json()) .then((data) => { @@ -220,14 +232,14 @@ function Login() { { return { label: item.title, - value: item.tenantKey, + value: item.tenantKey }; }) || [] } @@ -245,64 +257,76 @@ function Login() { <>

{t("login-header")}

-

- {t("oidc-mit-id-header")} -

- -
- - } - /> - +

+ {t("oidc-mit-id-header")} +

+ +
+ {enabledLogins?.ad && ( + + } + /> + )} + + {enabledLogins?.external && ( + + } + /> + )} +
+ + )} + + {enabledLogins?.usernamePassword && ( + <> +

+ {t("os2-display-user-header")} +

+ +
+ setEmail(ev.target.value)} + value={email} + name="email" + label={t("email")} + required /> - } - /> -
- -

- {t("os2-display-user-header")} -

- - - setEmail(ev.target.value)} - value={email} - name="email" - label={t("email")} - required - /> - - setPassword(ev.target.value)} - value={password} - name="password" - label={t("password")} - type="password" - required - /> - - - + + setPassword(ev.target.value)} + value={password} + name="password" + label={t("password")} + type="password" + required + /> + + + + )} )}
diff --git a/src/config-loader.js b/src/config-loader.js index 2e4435ff..8fb98030 100644 --- a/src/config-loader.js +++ b/src/config-loader.js @@ -30,6 +30,11 @@ const ConfigLoader = { resolve({ api: "/api/", touchButtonRegions: false, + login: { + ad: true, + external: true, + usernamePassword: true + } }); } }) From c74c929f8723aea5b035a30de171c85e2b63c966 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:55:20 +0200 Subject: [PATCH 050/115] DISPLAY-993: Removed link to unused logo --- public/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/public/index.html b/public/index.html index 73167684..93bf3dd8 100644 --- a/public/index.html +++ b/public/index.html @@ -5,7 +5,6 @@ - OS2Display admin - +