From d839ac44e90bfca5e07e8cc5ddf56c30deabc008 Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Tue, 29 Mar 2022 09:17:44 -0400 Subject: [PATCH] 18559: Add empty user profile page for 404 Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- .../user-profile/user-profile-actions.ts | 26 +- .../user-profile-panel-root.tsx | 291 +++++++++--------- .../user-profile-panel/user-profile-panel.tsx | 3 +- 3 files changed, 176 insertions(+), 144 deletions(-) diff --git a/src/store/user-profile/user-profile-actions.ts b/src/store/user-profile/user-profile-actions.ts index c8556862..0fdeef9e 100644 --- a/src/store/user-profile/user-profile-actions.ts +++ b/src/store/user-profile/user-profile-actions.ts @@ -3,7 +3,7 @@ // SPDX-License-Identifier: AGPL-3.0 import { RootState } from "store/store"; import { Dispatch } from 'redux'; -import { initialize } from "redux-form"; +import { initialize, reset } from "redux-form"; import { ServiceRepository } from "services/services"; import { bindDataExplorerActions } from "store/data-explorer/data-explorer-action"; import { propertiesActions } from 'store/properties/properties-actions'; @@ -16,21 +16,37 @@ export const USER_PROFILE_PANEL_ID = 'userProfilePanel'; export const USER_PROFILE_FORM = 'userProfileForm'; export const DEACTIVATE_DIALOG = 'deactivateDialog'; export const SETUP_DIALOG = 'setupDialog'; +export const IS_PROFILE_INACCESSIBLE = 'isProfileInaccessible'; export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID); export const getCurrentUserProfilePanelUuid = getProperty(USER_PROFILE_PANEL_ID); +export const getUserProfileIsInaccessible = getProperty(IS_PROFILE_INACCESSIBLE); export const loadUserProfilePanel = (userUuid?: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + // Reset isInacessible to ensure error screen is hidden + dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: false })); // Get user uuid from route or use current user uuid const uuid = userUuid || getState().auth.user?.uuid; if (uuid) { await dispatch(propertiesActions.SET_PROPERTY({ key: USER_PROFILE_PANEL_ID, value: uuid })); - const user = await services.userService.get(uuid); - dispatch(initialize(USER_PROFILE_FORM, user)); - dispatch(updateResources([user])); - dispatch(UserProfileGroupsActions.REQUEST_ITEMS()); + try { + const user = await services.userService.get(uuid, false); + dispatch(initialize(USER_PROFILE_FORM, user)); + dispatch(updateResources([user])); + dispatch(UserProfileGroupsActions.REQUEST_ITEMS()); + } catch (e) { + if (e.status === 404) { + await dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: true })); + dispatch(reset(USER_PROFILE_FORM)); + } else { + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Could not load user profile', + kind: SnackbarKind.ERROR + })); + } + } } } diff --git a/src/views/user-profile-panel/user-profile-panel-root.tsx b/src/views/user-profile-panel/user-profile-panel-root.tsx index 0beeab4f..f89ca5c5 100644 --- a/src/views/user-profile-panel/user-profile-panel-root.tsx +++ b/src/views/user-profile-panel/user-profile-panel-root.tsx @@ -28,23 +28,26 @@ import { DataTableDefaultView } from 'components/data-table-default-view/data-ta import { PROFILE_EMAIL_VALIDATION } from "validators/validators"; import { USER_PROFILE_PANEL_ID } from 'store/user-profile/user-profile-actions'; import { noop } from 'lodash'; -import { CopyIcon, GroupsIcon, MoreOptionsIcon } from 'components/icon/icon'; +import { CopyIcon, DetailsIcon, GroupsIcon, MoreOptionsIcon } from 'components/icon/icon'; import { DataColumns } from 'components/data-table/data-table'; import { ResourceLinkHeadUuid, ResourceLinkHeadPermissionLevel, ResourceLinkHead, ResourceLinkDelete, ResourceLinkTailIsVisible } from 'views-components/data-explorer/renderers'; import { createTree } from 'models/tree'; import { getResource, ResourcesState } from 'store/resources/resources'; import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; import CopyToClipboard from 'react-copy-to-clipboard'; +import { DefaultView } from 'components/default-view/default-view'; -type CssRules = 'root' | 'adminRoot' | 'gridItem' | 'label' | 'readOnlyValue' | 'title' | 'description' | 'actions' | 'content' | 'copyIcon'; +type CssRules = 'root' | 'emptyRoot' | 'gridItem' | 'label' | 'readOnlyValue' | 'title' | 'description' | 'actions' | 'content' | 'copyIcon'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ root: { width: '100%', overflow: 'auto' }, - adminRoot: { - // ...theme.mixins.gutters() + emptyRoot: { + width: '100%', + overflow: 'auto', + padding: theme.spacing.unit * 4, }, gridItem: { height: 45, @@ -91,8 +94,9 @@ export interface UserProfilePanelRootDataProps { isSelf: boolean; isPristine: boolean; isValid: boolean; + isInaccessible: boolean; userUuid: string; - resources: ResourcesState + resources: ResourcesState; localCluster: string; } @@ -193,143 +197,154 @@ export const UserProfilePanelRoot = withStyles(styles)( } render() { - return - - - - - {this.state.value === TABS.PROFILE && - - - - - {this.props.userUuid} - - - this.onCopy!("Copied")}> - - - - - - - - - this.handleContextMenu(event, this.props.userUuid)}> - - - - - -
- - - - - - - - - + if (this.props.isInaccessible) { + return ( + + + + + + ); + } else { + return + + + + + {this.state.value === TABS.PROFILE && + + + + + {this.props.userUuid} + + + this.onCopy!("Copied")}> + + + + + - - - - - - - - - - - Role - - - - + + + this.handleContextMenu(event, this.props.userUuid)}> + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + Role + + + + + + + + + + - - - - } - {this.state.value === TABS.GROUPS && -
- - } /> -
} -
; + +
+ } + {this.state.value === TABS.GROUPS && +
+ + } /> +
} +
; + } } handleChange = (event: React.MouseEvent, value: number) => { diff --git a/src/views/user-profile-panel/user-profile-panel.tsx b/src/views/user-profile-panel/user-profile-panel.tsx index 207a98fe..a90d44a9 100644 --- a/src/views/user-profile-panel/user-profile-panel.tsx +++ b/src/views/user-profile-panel/user-profile-panel.tsx @@ -7,7 +7,7 @@ import { compose, Dispatch } from 'redux'; import { reduxForm, isPristine, isValid } from 'redux-form'; import { connect } from 'react-redux'; import { UserResource } from 'models/user'; -import { saveEditedUser } from 'store/user-profile/user-profile-actions'; +import { getUserProfileIsInaccessible, saveEditedUser } from 'store/user-profile/user-profile-actions'; import { UserProfilePanelRoot, UserProfilePanelRootDataProps } from 'views/user-profile-panel/user-profile-panel-root'; import { USER_PROFILE_FORM } from "store/user-profile/user-profile-actions"; import { matchUserProfileRoute } from 'routes/routes'; @@ -23,6 +23,7 @@ const mapStateToProps = (state: RootState): UserProfilePanelRootDataProps => { isSelf: state.auth.user!.uuid === uuid, isPristine: isPristine(USER_PROFILE_FORM)(state), isValid: isValid(USER_PROFILE_FORM)(state), + isInaccessible: getUserProfileIsInaccessible(state.properties) || false, localCluster: state.auth.localCluster, userUuid: uuid, resources: state.resources, -- 2.30.2