diff --git a/.circleci/Dockerfile.cypress b/.circleci/Dockerfile.cypress index d4e29881c..ba4822910 100644 --- a/.circleci/Dockerfile.cypress +++ b/.circleci/Dockerfile.cypress @@ -1,12 +1,18 @@ FROM cypress/browsers:chrome67 +# FROM cypress/browsers:node16.13.0-chrome95-ff94 + +# Install libnss3 +RUN apt-get update && apt-get install libgtk2.0-0t64 libgtk-3-0t64 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb \ + && rm -rf /var/lib/apt/lists/* ENV APP /usr/src/app WORKDIR $APP COPY package.json $APP/package.json -RUN npm run cypress:install > /dev/null +COPY viz-lib $APP/viz-lib +COPY plywood $APP/plywood +COPY client $APP/client -COPY client/cypress $APP/client/cypress -COPY cypress.json $APP/cypress.json +COPY . $APP RUN ./node_modules/.bin/cypress verify diff --git a/Dockerfile b/Dockerfile index 957ecb7c9..4cd5d9e4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,21 @@ -FROM node:14.17 AS frontend-builder +FROM node:16.20 AS frontend-builder # Controls whether to build the frontend assets ARG skip_frontend_build +RUN useradd -m -d /frontend datareporter +USER datareporter + WORKDIR /frontend -COPY bin/build_frontend.sh . -COPY client/ /frontend/client -COPY viz-lib/ /frontend/viz-lib -COPY plywood/server/ /frontend/plywood/server/ -RUN if [ "x$skip_frontend_build" = "x" ] ; then \ - echo "Building frontend";\ - ./build_frontend.sh;\ +COPY --chown=datareporter client /frontend/client +COPY --chown=datareporter viz-lib/ /frontend/viz-lib +COPY --chown=datareporter plywood/server /frontend/plywood/server/ +COPY --chown=datareporter plywood/server/client /frontend/plywood/server/client + +# install node dependencies +RUN if [ "x$skip_frontend_build" = "x" ]; then \ + cd client && npm i && npm run build;\ else \ - echo "Skipping frontend build" &&\ mkdir -p /frontend/client/dist &&\ touch /frontend/client/dist/multi_org.html &&\ touch /frontend/client/dist/index.html;\ @@ -22,7 +25,7 @@ FROM python:3.8-slim-buster EXPOSE 5000 -RUN useradd --create-home redash +RUN useradd --create-home datareporter # Ubuntu packages RUN apt-get update && \ @@ -80,14 +83,15 @@ ARG POETRY_OPTIONS="--no-root --no-interaction --no-ansi" ARG install_groups="main,all_ds,dev" RUN /etc/poetry/bin/poetry install --only $install_groups $POETRY_OPTIONS -COPY --chown=redash . /app -COPY --chown=redash --from=frontend-builder /frontend/client/dist /app/client/dist -RUN chown redash:redash -R /app -RUN find /app -USER redash +COPY --chown=datareporter . /app +COPY --chown=datareporter --from=frontend-builder /frontend/client/dist /app/client/dist +RUN chown datareporter:datareporter -R /app +USER datareporter + ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python # The version is being set arbitrarily by the builder ARG version ENV DATAREPORTER_VERSION=$version + ENTRYPOINT ["/app/bin/docker-entrypoint"] -CMD ["server"] \ No newline at end of file +CMD ["server"] diff --git a/SETUP.md b/SETUP.md index 3d9618815..df9e1107c 100644 --- a/SETUP.md +++ b/SETUP.md @@ -33,16 +33,14 @@ nvm use v14 > /dev/null ``` * Build UI - Required to build ui for - * Enter project root directory - * `cd client` - * `npm install` Installs all node dependencies to for redash - * `npm run build` Builds front end to the folder `client/dist/` + * `cd client` Enter project root directory + * `npm install` Installs all node dependencies to for redash + * `npm run build` Builds front end to the folder `client/dist/` * Build Plywood - * Enter project root directory - * `cd plywood/server` - * `npm install` Installs all node dependencies to for plywood - * `npm run build` Builds plywood server end to the folder `plywood/server/dist/` + * `cd plywood/server` Enter project root directory + * `npm install` Installs all node dependencies to for plywood + * `npm run build` Builds plywood server end to the folder `plywood/server/dist/` * Setup docker compose * `make up` or `docker compose up --build` to start required services like postgres app server @@ -61,7 +59,7 @@ nvm use v14 > /dev/null ## Local Development -Consider using [pyenv](https://github.com/pyenv/pyenv#installation) for installing local Python pyenv app. Datareporter container images are shipped with Python 3.8.7, [ubuntu guide](https://www.dedicatedcore.com/blog/install-pyenv-ubuntu/) +Consider using [pyenv](https://github.com/pyenv/pyenv#installation) for installing local Python pyenv app. Data Reporter container images are shipped with Python 3.8.7, [ubuntu guide](https://www.dedicatedcore.com/blog/install-pyenv-ubuntu/) ``` # install necessary python version pyenv install 3.8.7 @@ -94,17 +92,22 @@ Then run the tests: docker compose run --rm server tests ``` In order to test viz-lib folder you need to install dependencies and run tests because you cant have 2 react versions in the same project. To do that run below commands in the viz-lib folder: +```sh +npm install antd@^4 react@^16.8 react-dom@^16.8 && npm run test ``` -npm install antd@^3 react@^16.8 react-dom@^16.8 && npm run test + +### Running cypress +```sh +bash bin/restart_cypress.sh ``` ### Components -#### Datareporter server +#### Data Reporter server * **directory**: `redash` * **debug**: Please follow the instruction from [redash](https://redash.io/help/open-source/dev-guide/debugging) * **changes:** * All changes are immediately visible as the python application is interpreted and it's running directly from source code. -#### Datareporter frontend +#### Data Reporter frontend * **submodules** - for debug and changes they follow root fronted app: * Lib viz * **directory:** `viz-lib` @@ -114,7 +117,7 @@ npm install antd@^3 react@^16.8 react-dom@^16.8 && npm run test * **debug:** Can be debugged from browser open application at `http://localhost:8080` || `5000` and use browser debugger. * **changes:** * By default, changes are not reflected. You need go into `client` directory and start `npm run watch`. - That will start watched for source code changes for Datareporter frontend and all submodules. + That will start watched for source code changes for Data Reporter frontend and all submodules. * At liniux system you may face problem of too many file system watchers. That will result in error message ```Error: ENOSPC: System limit for number of file watchers reached, watch ``` To solve it you need to increase the number of available watches by : diff --git a/bin/build_frontend.sh b/bin/build_frontend.sh deleted file mode 100755 index ba156241a..000000000 --- a/bin/build_frontend.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -e - -cd client -echo "Clean install viz-lib & redash-client" -npm ci --unsafe-perm || cat /root/.npm/_logs/* -echo "Build viz-lib & redash-client" -npm run build || cat /root/.npm/_logs/* diff --git a/bin/restart_cypress.sh b/bin/restart_cypress.sh new file mode 100644 index 000000000..597582990 --- /dev/null +++ b/bin/restart_cypress.sh @@ -0,0 +1,10 @@ +# check if we are in client folder, use dirname +if [ "$(basename $(pwd))" != "client" ]; then + cd client +fi +if [ ! -d "node_modules" ]; then + npm install +fi +npm run cypress build +npm run cypress start +npm run cypress open diff --git a/bin/restart_server.sh b/bin/restart_server.sh index fc0273178..d0f21cad7 100644 --- a/bin/restart_server.sh +++ b/bin/restart_server.sh @@ -12,6 +12,7 @@ if [ "$state" = "yes" ] || [ "$state" = "first" ]; then docker rm datareporter-server-1 docker rmi datareporter-server docker-compose up -d + docker compose run --rm server create_db docker-compose stop server && docker-compose run --rm --service-ports server debug && docker-compose start server else @@ -24,4 +25,4 @@ else docker-compose up -d docker-compose stop server && docker-compose run --rm --service-ports server debug && docker-compose start server -fi \ No newline at end of file +fi diff --git a/bin/server_tests.sh b/bin/server_tests.sh new file mode 100644 index 000000000..2c4c4bc4a --- /dev/null +++ b/bin/server_tests.sh @@ -0,0 +1,6 @@ + +# First lets ensure that the "tests" database is created: +docker-compose run --rm postgres psql -h postgres -U postgres -c "create database tests" + +# Then run the tests: +docker-compose run --rm server tests \ No newline at end of file diff --git a/client/.babelrc b/client/.babelrc index 1773f9500..b31065e14 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -1,23 +1,30 @@ { "presets": [ - ["@babel/preset-env", { - "exclude": [ - "@babel/plugin-transform-async-to-generator", - "@babel/plugin-transform-arrow-functions" - ], - "useBuiltIns": "usage" - }], + [ + "@babel/preset-env", + { + "exclude": ["@babel/plugin-transform-async-to-generator", "@babel/plugin-transform-arrow-functions"], + "corejs": "2", + "useBuiltIns": "usage" + } + ], "@babel/preset-react", "@babel/preset-typescript", "@babel/preset-flow" ], "plugins": [ - "babel-plugin-flow-to-typescript", "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-object-assign", - ["babel-plugin-transform-builtin-extend", { - "globals": ["Error"] - }], - "@babel/plugin-transform-runtime" - ] + [ + "babel-plugin-transform-builtin-extend", + { + "globals": ["Error"] + } + ] + ], + "env": { + "test": { + "plugins": ["istanbul"] + } + } } diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 8bc0055d0..d1bb2599a 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -5,10 +5,11 @@ module.exports = { "react-app", "plugin:compat/recommended", "prettier", + "plugin:jsx-a11y/recommended", // Remove any typescript-eslint rules that would conflict with prettier "prettier/@typescript-eslint", ], - plugins: ["jest", "compat", "no-only-tests", "@typescript-eslint"], + plugins: ["jest", "compat", "no-only-tests", "@typescript-eslint", "jsx-a11y"], settings: { "import/resolver": "webpack", }, @@ -19,7 +20,35 @@ module.exports = { rules: { // allow debugger during development "no-debugger": process.env.NODE_ENV === "production" ? 2 : 0, - "jsx-a11y/anchor-is-valid": "off", + "jsx-a11y/anchor-is-valid": [ + // TMP + "off", + { + components: ["Link"], + aspects: ["noHref", "invalidHref", "preferButton"], + }, + ], + "jsx-a11y/no-redundant-roles": "error", + "jsx-a11y/no-autofocus": "off", + "jsx-a11y/click-events-have-key-events": "off", // TMP + "jsx-a11y/no-static-element-interactions": "off", // TMP + "jsx-a11y/no-noninteractive-element-interactions": "off", // TMP + "no-console": ["warn", { allow: ["warn", "error"] }], + "no-restricted-imports": [ + "error", + { + paths: [ + { + name: "antd", + message: "Please use 'import XXX from antd/lib/XXX' import instead.", + }, + { + name: "antd/lib", + message: "Please use 'import XXX from antd/lib/XXX' import instead.", + }, + ], + }, + ], }, overrides: [ { @@ -34,6 +63,8 @@ module.exports = { // Do not complain about useless contructors in declaration files "no-useless-constructor": "off", "@typescript-eslint/no-useless-constructor": "error", + // Many API fields and generated types use camelcase + "@typescript-eslint/camelcase": "off", }, }, ], diff --git a/client/app/assets/less/ant.less b/client/app/assets/less/ant.less index 574d420fb..4ccb8d2d4 100644 --- a/client/app/assets/less/ant.less +++ b/client/app/assets/less/ant.less @@ -16,7 +16,6 @@ @import "~antd/lib/pagination/style/index"; @import "~antd/lib/table/style/index"; @import "~antd/lib/popover/style/index"; -@import "~antd/lib/icon/style/index"; @import "~antd/lib/tag/style/index"; @import "~antd/lib/grid/style/index"; @import "~antd/lib/switch/style/index"; @@ -226,6 +225,16 @@ } } + &-tbody > tr&-row { + &:hover, + &:focus, + &:focus-within { + & > td { + background: @table-row-hover-bg; + } + } + } + // Custom styles &-headerless &-tbody > tr:first-child > td { @@ -392,6 +401,18 @@ left: 0; } } + + &:focus, + &:focus-within { + color: @menu-highlight-color; + } + } +} + +.@{dropdown-prefix-cls}-menu-item { + &:focus, + &:focus-within { + background-color: @item-hover-bg; } } @@ -402,3 +423,18 @@ .@{checkbox-prefix-cls} + span { padding-right: 0; } + +// make sure Multiple select has room for icons +.@{select-prefix-cls}-multiple { + &.@{select-prefix-cls}-show-arrow, + &.@{select-prefix-cls}-show-search, + &.@{select-prefix-cls}-loading { + .@{select-prefix-cls}-selector { + padding-right: 30px; + } + } +} + +.ant-btn-icon-only.ant-btn-sm { + width: auto!important; +} diff --git a/client/app/assets/less/icons.less b/client/app/assets/less/icons.less index c87a2fe8f..6922a6005 100644 --- a/client/app/assets/less/icons.less +++ b/client/app/assets/less/icons.less @@ -13,7 +13,6 @@ [class^="icon-"], [class*=" icon-"] { /* use !important to prevent issues with browser extensions that change fonts */ font-family: 'icomoon' !important; - speak: never; font-style: normal; font-weight: normal; font-variant: normal; diff --git a/client/app/assets/less/inc/schema-browser.less b/client/app/assets/less/inc/schema-browser.less index 3f2e66f28..f00523975 100644 --- a/client/app/assets/less/inc/schema-browser.less +++ b/client/app/assets/less/inc/schema-browser.less @@ -1,102 +1,107 @@ -div.table-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - cursor: pointer; - padding: 2px 22px 2px 10px; - border-radius: @redash-radius; - position: relative; - height: 22px; - - .copy-to-editor { - display: none; - } - - &:hover { - background: fade(@redash-gray, 10%); - - .copy-to-editor { - display: flex; - } - } -} - .schema-container { height: 100%; z-index: 10; background-color: white; -} -.schema-browser { - overflow: hidden; - border: none; - padding-top: 10px; - position: relative; - height: 100%; - - .schema-loading-state { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - } - - .collapse.in { - background: transparent; - } - - .copy-to-editor { - color: fade(@redash-gray, 90%); - cursor: pointer; - position: absolute; - top: 0; - right: 0; - bottom: 0; - width: 20px; - display: flex; - align-items: center; - justify-content: center; - } - - .table-open { - padding: 0 22px 0 26px; + .schema-browser { overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + border: none; + padding-top: 10px; position: relative; - height: 18px; + height: 100%; - .column-type { - color: fade(@text-color, 80%); - font-size: 10px; - margin-left: 2px; - text-transform: uppercase; + .schema-loading-state { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + + .collapse.in { + background: transparent; } .copy-to-editor { - display: none; + visibility: hidden; + color: fade(@redash-gray, 90%); + width: 20px; + display: flex; + align-items: center; + justify-content: center; + transition: none; } - &:hover { - background: fade(@redash-gray, 10%); + .schema-list-item { + display: flex; + border-radius: @redash-radius; + height: 22px; + + .table-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; + padding: 2px 22px 2px 10px; + } + + &:hover, + &:focus, + &:focus-within { + background: fade(@redash-gray, 10%); - .copy-to-editor { + .copy-to-editor { + visibility: visible; + } + } + } + + .table-open { + .table-open-item { display: flex; + height: 18px; + width: calc(100% - 22px); + padding-left: 22px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: none; + + div:first-child { + flex: 1; + } + + .column-type { + color: fade(@text-color, 80%); + font-size: 10px; + margin-left: 2px; + text-transform: uppercase; + } + + &:hover, + &:focus, + &:focus-within { + background: fade(@redash-gray, 10%); + + .copy-to-editor { + visibility: visible; + } + } } } } -} -.schema-control { - display: flex; - flex-wrap: nowrap; - padding: 0; + .schema-control { + display: flex; + flex-wrap: nowrap; + padding: 0; - .ant-btn { - height: auto; + .ant-btn { + height: auto; + } } -} -.parameter-label { - display: block; + .parameter-label { + display: block; + } } diff --git a/client/app/assets/less/inc/table.less b/client/app/assets/less/inc/table.less index 37ee1be9d..7017881d4 100755 --- a/client/app/assets/less/inc/table.less +++ b/client/app/assets/less/inc/table.less @@ -103,7 +103,7 @@ padding-top: 5px !important; } - .btn-favourite, + .btn-favorite, .btn-archive { font-size: 15px; } @@ -115,18 +115,23 @@ line-height: 1.7 !important; } -.btn-favourite { +.btn-favorite { color: #d4d4d4; transition: all 0.25s ease-in-out; + .fa-star { + color: @yellow-darker; + } + &:hover, &:focus { color: @yellow-darker; cursor: pointer; - } - .fa-star { - color: @yellow-darker; + .fa-star { + filter: saturate(75%); + opacity: 0.75; + } } } diff --git a/client/app/assets/less/inc/variables.less b/client/app/assets/less/inc/variables.less index c454fb4bd..c6e8155b6 100755 --- a/client/app/assets/less/inc/variables.less +++ b/client/app/assets/less/inc/variables.less @@ -48,8 +48,8 @@ /* -------------------------------------------------------- Typograpgy -----------------------------------------------------------*/ -@text-color: #160f66; -@link: #160f66; +@text-color: #767676; +@link: #02a4c4; @link-hover-decoration: none; @headings-color: #333; @@ -220,7 +220,7 @@ -----------------------------------------------------------*/ @modal-content-fallback-border-color: transparent; @modal-content-border-color: transparent; -@modal-backdrop-bg: #000; +@modal-backdrop-bg: #000; @modal-header-border-color: transparent; @modal-title-line-height: transparent; @modal-footer-border-color: transparent; diff --git a/client/app/assets/less/redash/report.less b/client/app/assets/less/redash/report.less index ac6da3551..555630bf5 100644 --- a/client/app/assets/less/redash/report.less +++ b/client/app/assets/less/redash/report.less @@ -343,7 +343,7 @@ min-width: 200px; .ant-select { display: inline-block; - width: calc(100% - 37px); + width: calc(100% - 13px); } .icon { display: inline-block; diff --git a/client/app/assets/less/server.less b/client/app/assets/less/server.less index 783376820..029305869 100644 --- a/client/app/assets/less/server.less +++ b/client/app/assets/less/server.less @@ -27,7 +27,7 @@ html, body { } .signed-out { - + width: auto; } .logo { diff --git a/client/app/components/ApplicationArea/ApplicationLayout/DesktopNavbar.jsx b/client/app/components/ApplicationArea/ApplicationLayout/DesktopNavbar.jsx index b7b6b2d63..c198cd4ee 100644 --- a/client/app/components/ApplicationArea/ApplicationLayout/DesktopNavbar.jsx +++ b/client/app/components/ApplicationArea/ApplicationLayout/DesktopNavbar.jsx @@ -1,18 +1,20 @@ -import { first } from "lodash"; -import React, { useState } from "react"; +import React, { useMemo } from "react"; +import { first, includes } from "lodash"; import Menu from "antd/lib/menu"; -import Tooltip from "antd/lib/tooltip"; +import Tooltip from "@/components/Tooltip"; +import Link from "@/components/Link"; +import PlainButton from "@/components/PlainButton"; import CreateDashboardDialog from "@/components/dashboards/CreateDashboardDialog"; +import { useCurrentRoute } from "@/components/ApplicationArea/Router"; import { Auth, currentUser } from "@/services/auth"; import settingsMenu from "@/services/settingsMenu"; -import routes from "@/services/routes"; -import location from "@/services/location"; import logoUrl from "@/assets/images/report_icon_small.png"; import VersionInfo from "./VersionInfo"; + import "./DesktopNavbar.less"; -function NavbarSection({ inlineCollapsed, children, ...props }) { +function NavbarSection({ children, ...props }) { return ( {children} @@ -20,11 +22,45 @@ function NavbarSection({ inlineCollapsed, children, ...props }) { ); } +function useNavbarActiveState() { + const currentRoute = useCurrentRoute(); + + return useMemo( + () => ({ + dashboards: includes( + [ + "Dashboards.List", + "Dashboards.Favorites", + "Dashboards.My", + "Dashboards.ViewOrEdit", + "Dashboards.LegacyViewOrEdit", + ], + currentRoute.id + ), + queries: includes( + [ + "Queries.List", + "Queries.Favorites", + "Queries.Archived", + "Queries.My", + "Queries.View", + "Queries.New", + "Queries.Edit", + ], + currentRoute.id + ), + dataSources: includes(["DataSources.List"], currentRoute.id), + alerts: includes(["Alerts.List", "Alerts.New", "Alerts.View", "Alerts.Edit"], currentRoute.id), + reports: includes(["Reports.List", "Reports.View", "Reports.Edit", "Reports.New"], currentRoute.id), + }), + [currentRoute.id] + ); +} + export default function DesktopNavbar() { - const [collapsed, setCollapsed] = useState(true); + const activeState = useNavbarActiveState(); const firstSettingsTab = first(settingsMenu.getAvailableItems()); - const headerBlock = routes.getRoute(location.path) ? routes.getRoute(location.path).headerBlock : {}; const canCreateQuery = currentUser.hasPermission("create_query"); const canCreateDashboard = currentUser.hasPermission("create_dashboard"); @@ -48,64 +84,66 @@ export default function DesktopNavbar() { return (
- - - Data reporter - + +
+ + Data reporter + +
- + {currentUser.hasPermission("list_dashboards") && ( - + - - - + + + )} {currentUser.hasPermission("view_query") && ( - + - + - + )} {currentUser.hasPermission("view_query") && ( - + - - - - + + + + )} {currentUser.hasPermission("list_alerts") && ( - + - + - + )} - + {(canCreateQuery || canCreateDashboard || canCreateAlert) && } {(canCreateQuery || canCreateDashboard || canCreateAlert) && ( - - - - - - + + + + }> {canCreateQuery && ( - + New Query - + )} {canCreateQuery && ( - + New Report - + )} {canCreateDashboard && ( - CreateDashboardDialog.showModal()}> + CreateDashboardDialog.showModal()}> New Dashboard - + )} {canCreateAlert && ( - + New Alert - + )} )} - - {/* - - - - */} + -
- -
- + + {currentUser.name} + }> - Profile + Profile {currentUser.hasPermission("super_admin") && ( - System Status + System Status )} - Auth.logout()}> + Auth.logout()}> Log out - + - +
- - - {currentUser.name} - -
- + diff --git a/client/app/components/ApplicationArea/ApplicationLayout/MobileNavbar.jsx b/client/app/components/ApplicationArea/ApplicationLayout/MobileNavbar.jsx index c7d0c0bc2..142543f58 100644 --- a/client/app/components/ApplicationArea/ApplicationLayout/MobileNavbar.jsx +++ b/client/app/components/ApplicationArea/ApplicationLayout/MobileNavbar.jsx @@ -2,9 +2,10 @@ import { first } from "lodash"; import React from "react"; import PropTypes from "prop-types"; import Button from "antd/lib/button"; -import Icon from "antd/lib/icon"; +import MenuOutlinedIcon from "@ant-design/icons/MenuOutlined"; import Dropdown from "antd/lib/dropdown"; import Menu from "antd/lib/menu"; +import Link from "@/components/Link"; import { Auth, currentUser } from "@/services/auth"; import settingsMenu from "@/services/settingsMenu"; import logoUrl from "@/assets/images/report_icon_small.png"; @@ -18,9 +19,9 @@ export default function MobileNavbar({ getPopupContainer }) { return (
+ {currentUser.hasPermission("list_dashboards") && ( - Dashboards + Dashboards )} {currentUser.hasPermission("view_query") && ( - Queries - - )} - {currentUser.hasPermission("view_query") && ( - - Reports + Queries )} {currentUser.hasPermission("list_alerts") && ( - Alerts + Alerts )} - Edit Profile + Edit Profile {firstSettingsTab && ( - Settings + Settings )} {currentUser.hasPermission("super_admin") && ( - System Status + System Status )} {currentUser.hasPermission("super_admin") && } {/* eslint-disable-next-line react/jsx-no-target-blank */} - + Help - + Auth.logout()}> Log out diff --git a/client/app/components/ApplicationArea/ErrorMessage.jsx b/client/app/components/ApplicationArea/ErrorMessage.jsx index 852e1452f..6edd3f77b 100644 --- a/client/app/components/ApplicationArea/ErrorMessage.jsx +++ b/client/app/components/ApplicationArea/ErrorMessage.jsx @@ -1,8 +1,10 @@ -import { isObject, get } from "lodash"; +import { get, isObject } from "lodash"; import React from "react"; import PropTypes from "prop-types"; import "./ErrorMessage.less"; +import DynamicComponent from "@/components/DynamicComponent"; +import { ErrorMessageDetails } from "@/components/ApplicationArea/ErrorMessageDetails"; function getErrorMessageByStatus(status, defaultMessage) { switch (status) { @@ -31,21 +33,30 @@ function getErrorMessage(error) { return message; } -export default function ErrorMessage({ error }) { +export default function ErrorMessage({ error, message }) { if (!error) { return null; } console.error(error); + const errorDetailsProps = { + error, + message: message || getErrorMessage(error), + }; + return ( -
+
- +