From 50c0bb3df2ffc5f185bb5eff6874c778b775bcef Mon Sep 17 00:00:00 2001 From: Lisa Knox Date: Tue, 26 Mar 2024 11:30:09 -0400 Subject: [PATCH] 21224: set up user card toolbar Arvados-DCO-1.1-Signed-off-by: Lisa Knox --- .../MultiselectToolbar.tsx | 22 ++++- .../ms-kind-action-differentiator.ts | 4 +- .../ms-toolbar-action-filters.ts | 6 ++ .../multiselect-toolbar/ms-menu-actions.ts | 1 + .../ms-user-details-action-set.ts | 41 +++++++++ .../project-details-card.tsx | 88 +++++++++---------- 6 files changed, 109 insertions(+), 53 deletions(-) create mode 100644 services/workbench2/src/views-components/multiselect-toolbar/ms-user-details-action-set.ts diff --git a/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx b/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx index a7364da26b..69c9dfc5ef 100644 --- a/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx +++ b/services/workbench2/src/components/multiselect-toolbar/MultiselectToolbar.tsx @@ -92,12 +92,24 @@ type IconProps = { publicFavorites: PublicFavoritesState; } +const disallowedPaths = [ + "/favorites", + "/public-favorites", + "/trash", + "/group", +] + +const isPathDisallowed = (location: string): boolean => { + return disallowedPaths.some(path => location.includes(path)) +} + export const MultiselectToolbar = connect( mapStateToProps, mapDispatchToProps )( withStyles(styles)((props: MultiselectToolbarProps & WithStyles) => { - const { classes, checkedList, iconProps, user, disabledButtons, selectedResourceUuid, location, isSubPanel } = props; + const { classes, checkedList, iconProps, user, disabledButtons, location, isSubPanel } = props; + const selectedResourceUuid = isPathDisallowed(location) ? null : props.selectedResourceUuid; const singleResourceKind = selectedResourceUuid && !isSubPanel ? [resourceToMsResourceKind(selectedResourceUuid, iconProps.resources, user)] : null const currentResourceKinds = singleResourceKind ? singleResourceKind : Array.from(selectedToKindSet(checkedList)); const currentPathIsTrash = location.includes("/trash"); @@ -126,6 +138,8 @@ export const MultiselectToolbar = connect( selectedResourceUuid === null ? action.isForMulti : true ); + const targetResources = selectedResourceUuid ? {[selectedResourceUuid]: true} as TCheckedList : checkedList + return ( props.executeMulti(action, checkedList, iconProps.resources)} + onClick={() => props.executeMulti(action, targetResources, iconProps.resources)} className={classes.icon} > {currentPathIsTrash || (useAlts && useAlts(selectedResourceUuid, iconProps)) ? altIcon && altIcon({}) : icon({})} @@ -168,7 +182,7 @@ export const MultiselectToolbar = connect( { - props.executeMulti(action, checkedList, iconProps.resources)}} + props.executeMulti(action, targetResources, iconProps.resources)}} className={classes.icon} > {action.icon({})} @@ -227,7 +241,7 @@ const resourceToMsResourceKind = (uuid: string, resources: ResourcesState, user: const { isAdmin } = user; const kind = extractUuidKind(uuid); - const isFrozen = resourceIsFrozen(resource, resources); + const isFrozen = resource?.kind && resource.kind === ResourceKind.PROJECT ? resourceIsFrozen(resource, resources) : false; const isEditable = (user.isAdmin || (resource || ({} as EditableResource)).isEditable) && !readonly && !isFrozen; switch (kind) { diff --git a/services/workbench2/src/components/multiselect-toolbar/ms-kind-action-differentiator.ts b/services/workbench2/src/components/multiselect-toolbar/ms-kind-action-differentiator.ts index 5a84d4c573..1e49ae664f 100644 --- a/services/workbench2/src/components/multiselect-toolbar/ms-kind-action-differentiator.ts +++ b/services/workbench2/src/components/multiselect-toolbar/ms-kind-action-differentiator.ts @@ -8,16 +8,18 @@ import { msCollectionActionSet } from "views-components/multiselect-toolbar/ms-c import { msProjectActionSet } from "views-components/multiselect-toolbar/ms-project-action-set"; import { msProcessActionSet } from "views-components/multiselect-toolbar/ms-process-action-set"; import { msWorkflowActionSet } from "views-components/multiselect-toolbar/ms-workflow-action-set"; +import { UserDetailsActionSet } from "views-components/multiselect-toolbar/ms-user-details-action-set"; export function findActionByName(name: string, actionSet: MultiSelectMenuActionSet) { return actionSet[0].find(action => action.name === name); } -const { COLLECTION, PROCESS, PROJECT, WORKFLOW } = ResourceKind; +const { COLLECTION, PROCESS, PROJECT, WORKFLOW, USER } = ResourceKind; export const kindToActionSet: Record = { [COLLECTION]: msCollectionActionSet, [PROCESS]: msProcessActionSet, [PROJECT]: msProjectActionSet, [WORKFLOW]: msWorkflowActionSet, + [USER]: UserDetailsActionSet, }; diff --git a/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-action-filters.ts b/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-action-filters.ts index 9faba26936..ddd2a1d83b 100644 --- a/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-action-filters.ts +++ b/services/workbench2/src/components/multiselect-toolbar/ms-toolbar-action-filters.ts @@ -15,6 +15,7 @@ import { } from 'views-components/multiselect-toolbar/ms-project-action-set'; import { msProcessActionSet, msCommonProcessActionFilter, msAdminProcessActionFilter, msRunningProcessActionFilter } from 'views-components/multiselect-toolbar/ms-process-action-set'; import { msWorkflowActionSet, msWorkflowActionFilter, msReadOnlyWorkflowActionFilter } from 'views-components/multiselect-toolbar/ms-workflow-action-set'; +import { UserDetailsActionSet } from 'views-components/multiselect-toolbar/ms-user-details-action-set'; import { ResourceKind } from 'models/resource'; export enum msMenuResourceKind { @@ -75,7 +76,9 @@ const { RUNNING_PROCESS_ADMIN, PROCESS_ADMIN, PROJECT, + ROOT_PROJECT, PROJECT_ADMIN, + ROOT_PROJECT_ADMIN, FROZEN_PROJECT, FROZEN_PROJECT_ADMIN, READONLY_PROJECT, @@ -113,4 +116,7 @@ export const multiselectActionsFilters: TMultiselectActionsFilters = { [WORKFLOW]: [msWorkflowActionSet, msWorkflowActionFilter], [READONLY_WORKFLOW]: [msWorkflowActionSet, msReadOnlyWorkflowActionFilter], + + [ROOT_PROJECT]: [UserDetailsActionSet as MultiSelectMenuActionSet, allActionNames(UserDetailsActionSet as MultiSelectMenuActionSet)], + [ROOT_PROJECT_ADMIN]: [UserDetailsActionSet as MultiSelectMenuActionSet, allActionNames(UserDetailsActionSet as MultiSelectMenuActionSet)], }; diff --git a/services/workbench2/src/views-components/multiselect-toolbar/ms-menu-actions.ts b/services/workbench2/src/views-components/multiselect-toolbar/ms-menu-actions.ts index da00e0be28..ef5eb8aaf6 100644 --- a/services/workbench2/src/views-components/multiselect-toolbar/ms-menu-actions.ts +++ b/services/workbench2/src/views-components/multiselect-toolbar/ms-menu-actions.ts @@ -48,6 +48,7 @@ export enum MultiSelectMenuActionNames { DEACTIVATE_USER = 'Deactivate user', SETUP_USER = 'Setup user', LOGIN_AS_USER = 'Login as user', + USER_ACCOUNT = 'User Account', }; export type MultiSelectMenuAction = { diff --git a/services/workbench2/src/views-components/multiselect-toolbar/ms-user-details-action-set.ts b/services/workbench2/src/views-components/multiselect-toolbar/ms-user-details-action-set.ts new file mode 100644 index 0000000000..33698259c1 --- /dev/null +++ b/services/workbench2/src/views-components/multiselect-toolbar/ms-user-details-action-set.ts @@ -0,0 +1,41 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { AdvancedIcon, AttributesIcon, UserPanelIcon } from 'components/icon/icon'; +import { openAdvancedTabDialog } from 'store/advanced-tab/advanced-tab'; +import { openUserAttributes } from 'store/users/users-actions'; +import { navigateToUserProfile } from 'store/navigation/navigation-action'; +import { MultiSelectMenuActionSet, MultiSelectMenuActionNames } from './ms-menu-actions'; + +export const UserDetailsActionSet: MultiSelectMenuActionSet= [ + [ + { + name: MultiSelectMenuActionNames.ATTRIBUTES, + icon: AttributesIcon, + hasAlts: false, + isForMulti: false, + execute: (dispatch, resources) => { + dispatch(openUserAttributes(resources[0].uuid)); + }, + }, + { + name: MultiSelectMenuActionNames.API_DETAILS, + icon: AdvancedIcon, + hasAlts: false, + isForMulti: false, + execute: (dispatch, resources) => { + dispatch(openAdvancedTabDialog(resources[0].uuid)); + }, + }, + { + name: MultiSelectMenuActionNames.USER_ACCOUNT, + icon: UserPanelIcon, + hasAlts: false, + isForMulti: false, + execute: (dispatch, resources) => { + dispatch(navigateToUserProfile(resources[0].uuid)); + }, + }, + ], +]; diff --git a/services/workbench2/src/views-components/project-details-card/project-details-card.tsx b/services/workbench2/src/views-components/project-details-card/project-details-card.tsx index aad665682b..61aa0be519 100644 --- a/services/workbench2/src/views-components/project-details-card/project-details-card.tsx +++ b/services/workbench2/src/views-components/project-details-card/project-details-card.tsx @@ -14,22 +14,19 @@ import { ResourceKind } from 'models/resource'; import { UserResource } from 'models/user'; import { UserResourceAccountStatus } from 'views-components/data-explorer/renderers'; import { FavoriteStar, PublicFavoriteStar } from 'views-components/favorite-star/favorite-star'; -import { MoreVerticalIcon, FreezeIcon } from 'components/icon/icon'; +import { FreezeIcon } from 'components/icon/icon'; import { Resource } from 'models/resource'; -import { IconButton } from '@material-ui/core'; import { ContextMenuResource } from 'store/context-menu/context-menu-actions'; import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions'; import { CollectionResource } from 'models/collection'; import { ContextMenuKind } from 'views-components/context-menu/context-menu'; import { Dispatch } from 'redux'; -import classNames from 'classnames'; import { loadDetailsPanel } from 'store/details-panel/details-panel-action'; import { ExpandChevronRight } from 'components/expand-chevron-right/expand-chevron-right'; import { MultiselectToolbar } from 'components/multiselect-toolbar/MultiselectToolbar'; type CssRules = | 'root' - | 'selected' | 'cardHeaderContainer' | 'cardHeader' | 'descriptionToggle' @@ -41,7 +38,7 @@ type CssRules = | 'namePlate' | 'faveIcon' | 'frozenIcon' - | 'contextMenuSection' + | 'accountStatusSection' | 'toolbarSection' | 'tag' | 'description'; @@ -52,10 +49,7 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ marginBottom: '1rem', flex: '0 0 auto', padding: 0, - borderLeft: '2px solid transparent', - }, - selected: { - border: '2pcx solid #ccc', + border: '2px solid transparent', }, showMore: { cursor: 'pointer', @@ -69,6 +63,7 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ }, userNameContainer: { display: 'flex', + alignItems: 'center', }, cardHeaderContainer: { width: '100%', @@ -116,11 +111,11 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ height: '1rem', color: theme.palette.text.primary, }, - contextMenuSection: { + accountStatusSection: { display: 'flex', flexDirection: 'row', alignItems: 'center', - paddingTop: '0.25rem', + paddingLeft: '1rem', }, toolbarSection: { marginTop: '-1rem', @@ -136,7 +131,7 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ }, }); -const mapStateToProps = ({auth, selectedResourceUuid, resources, properties}: RootState) => { +const mapStateToProps = ({ auth, selectedResourceUuid, resources, properties }: RootState) => { const currentResource = getResource(properties.currentRouteUuid)(resources); const frozenByUser = currentResource && getResource((currentResource as ProjectResource).frozenByUuid as string)(resources); const frozenByFullName = frozenByUser && (frozenByUser as Resource & { fullName: string }).fullName; @@ -253,44 +248,37 @@ const UserCard: React.FC = ({ classes, currentResource, handleCon return ( handleCardClick(uuid)} data-cy='user-details-card' > - - - {fullName} - - - } - action={ -
- {!currentResource.isActive && ( - - - - )} - - handleContextMenu(ev, currentResource as any, isAdmin)} + + + - - - -
- } - /> + {fullName} + +
+ {!currentResource.isActive && ( + + + + )} +
+ + } + /> + {isSelected && } +
); }; @@ -310,11 +298,15 @@ const ProjectCard: React.FC = ({ classes, currentResource, fro return ( handleCardClick(uuid)} data-cy='project-details-card' > - +