소스 검색

Improve and fix admin pages loading UX

Improve the loading UX when the user goes directly to an admin page
(ex.: `/projects`):

- Show 'Checking permissions...' message while waiting for the API to
confirm or deny the user's access to the page.
- Show 'User doesn't have permissions to view this page' message if
that's the case.

Previously, a simple 'Not Found' message has be shown instead.

Fixes some instances where 'Not Found' message was shown when going
directly to an admin page, even if the user has permissions to that page.
Sergiu Miclea 6 년 전
부모
커밋
328e0be64c

+ 23 - 4
src/components/App.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import { Switch, Route } from 'react-router-dom'
 import styled, { injectGlobal } from 'styled-components'
+import { observe } from 'mobx'
 
 import Fonts from './atoms/Fonts'
 import Notifications from './organisms/Notifications'
@@ -85,6 +86,7 @@ class App extends React.Component<{}, State> {
   }
 
   async componentWillMount() {
+    observe(userStore, 'loggedUser', () => { this.setState({}) })
     userStore.tokenLogin()
     await configLoader.load()
     this.setState({ isConfigReady: true })
@@ -96,12 +98,29 @@ class App extends React.Component<{}, State> {
     }
 
     let renderOptionalPage = (name: string, component: any, path?: string, exact?: boolean) => {
-      const isAdmin = userStore.loggedUser && typeof userStore.loggedUser.isAdmin === 'boolean'
-        ? userStore.loggedUser.isAdmin : true
-      let isDisabled = configLoader.config.disabledPages.find(p => p === name)
-      if (navigationMenu.find(m => m.value === name && !isDisabled && (!m.requiresAdmin || isAdmin))) {
+      if (configLoader.config.disabledPages.find(p => p === name)) {
+        return null
+      }
+      let requiresAdmin = Boolean(navigationMenu.find(n => n.value === name && n.requiresAdmin))
+      if (!requiresAdmin) {
         return <Route path={`${path || `/${name}`}`} component={component} exact={exact} />
       }
+      const renderNotFound = (title: string, subtitle: string) => (
+        <Route
+          path={`${path || `/${name}`}`}
+          exact={exact}
+          render={() => <NotFoundPage title={title} subtitle={subtitle} />}
+        />
+      )
+      if (!userStore.loggedUser || userStore.loggedUser.isAdmin == null) {
+        return renderNotFound('Checking permissions...', 'Please wait while checking user\'s permissions.')
+      }
+      if (userStore.loggedUser && userStore.loggedUser.isAdmin === false) {
+        return renderNotFound('User doesn\'t have permissions to view this page', 'Please login in with an administrator acount to view this page.')
+      }
+      if (userStore.loggedUser && userStore.loggedUser.isAdmin) {
+        return <Route path={`${path || `/${name}`}`} exact={exact} component={component} />
+      }
       return null
     }
 

+ 1 - 1
src/components/pages/DashboardPage/DashboardPage.jsx

@@ -103,7 +103,7 @@ class ProjectsPage extends React.Component<{ history: any }, State> {
   }
 
   async loadAdminData(showLoading: boolean) {
-    await Utils.waitFor(() => Boolean(userStore.loggedUser && userStore.loggedUser.isAdmin), 3000, 100)
+    await Utils.waitFor(() => Boolean(userStore.loggedUser && userStore.loggedUser.isAdmin), 30000, 100)
     if (userStore.loggedUser && userStore.loggedUser.isAdmin) {
       await userStore.getAllUsers({ skipLog: true, showLoading })
     }

+ 7 - 4
src/components/pages/NotFoundPage/NotFoundPage.jsx

@@ -39,13 +39,16 @@ const Message = styled.div`
   margin-top: 16px;
   color: ${Palette.grayscale[8]};
 `
-
-const NotFoundPage = () => {
+type Props = {
+  title?: string,
+  subtitle?: string,
+}
+const NotFoundPage = (props: Props) => {
   return (
     <EmptyTemplate>
       <Wrapper>
-        <Title>Page Not Found</Title>
-        <Message>Sorry, but the page you are trying to view does not exist.</Message>
+        <Title>{props.title || 'Page Not Found'}</Title>
+        <Message>{props.subtitle || 'Sorry, but the page you are trying to view does not exist.'}</Message>
       </Wrapper>
     </EmptyTemplate>
   )

+ 2 - 1
src/constants.js

@@ -30,7 +30,8 @@ export const servicesUrl = {
   licence: licenceUrl,
 }
 
-export const navigationMenu = [
+export type NavigationMenuType = { label: string, value: string, hidden?: boolean, requiresAdmin?: boolean }
+export const navigationMenu: NavigationMenuType[] = [
   { label: 'Dashboard', value: 'dashboard' },
   { label: 'Replicas', value: 'replicas' },
   { label: 'Migrations', value: 'migrations' },

+ 8 - 7
src/stores/UserStore.js

@@ -20,7 +20,6 @@ import type { Project } from '../types/Project'
 import UserSource from '../sources/UserSource'
 import projectStore from './ProjectStore'
 import notificationStore from '../stores/NotificationStore'
-import DomUtils from '../utils/DomUtils'
 
 /**
  * This is the authentication / authorization flow:
@@ -101,7 +100,7 @@ class UserStore {
     }
 
     let userData: User = await UserSource.getUserInfo(this.loggedUser.id)
-    runInAction(() => { this.loggedUser = { ...this.loggedUser, ...userData, isAdmin: false } })
+    runInAction(() => { this.loggedUser = { ...this.loggedUser, ...userData, isAdmin: null } })
   }
 
   @action async tokenLogin(): Promise<void> {
@@ -169,18 +168,20 @@ class UserStore {
     if (!this.loggedUser) {
       return
     }
-    this.loggedUser.isAdmin = false
+    this.loggedUser = { ...this.loggedUser, isAdmin: null }
     try {
       let isAdmin = await UserSource.isAdmin(this.loggedUser.id)
       runInAction(() => {
         if (this.loggedUser) {
-          this.loggedUser.isAdmin = isAdmin
+          this.loggedUser = { ...this.loggedUser, isAdmin }
         }
       })
     } catch (err) {
-      if (window.location.href.indexOf(`${DomUtils.urlHashPrefix}project`) > -1 || window.location.href.indexOf(`${DomUtils.urlHashPrefix}user`) > -1) {
-        window.location.href = `${DomUtils.urlHashPrefix}`
-      }
+      runInAction(() => {
+        if (this.loggedUser) {
+          this.loggedUser = { ...this.loggedUser, isAdmin: false }
+        }
+      })
     }
   }
 

+ 1 - 1
src/types/User.js

@@ -26,7 +26,7 @@ export type User = {
   enabled?: boolean,
   project_id?: string,
   domain_id?: string,
-  isAdmin?: boolean,
+  isAdmin?: ?boolean,
   password?: string,
   extra?: any,
 }