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<CssRules>) => {
- 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");
selectedResourceUuid === null ? action.isForMulti : true
);
+ const targetResources = selectedResourceUuid ? {[selectedResourceUuid]: true} as TCheckedList : checkedList
+
return (
<React.Fragment>
<Toolbar
<IconButton
data-cy='multiselect-button'
disabled={disabledButtons.has(name)}
- onClick={() => props.executeMulti(action, checkedList, iconProps.resources)}
+ onClick={() => props.executeMulti(action, targetResources, iconProps.resources)}
className={classes.icon}
>
{currentPathIsTrash || (useAlts && useAlts(selectedResourceUuid, iconProps)) ? altIcon && altIcon({}) : icon({})}
<IconButton
data-cy='multiselect-button'
onClick={() => {
- props.executeMulti(action, checkedList, iconProps.resources)}}
+ props.executeMulti(action, targetResources, iconProps.resources)}}
className={classes.icon}
>
{action.icon({})}
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) {
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<string, MultiSelectMenuActionSet> = {
[COLLECTION]: msCollectionActionSet,
[PROCESS]: msProcessActionSet,
[PROJECT]: msProjectActionSet,
[WORKFLOW]: msWorkflowActionSet,
+ [USER]: UserDetailsActionSet,
};
} 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 {
RUNNING_PROCESS_ADMIN,
PROCESS_ADMIN,
PROJECT,
+ ROOT_PROJECT,
PROJECT_ADMIN,
+ ROOT_PROJECT_ADMIN,
FROZEN_PROJECT,
FROZEN_PROJECT_ADMIN,
READONLY_PROJECT,
[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)],
};
DEACTIVATE_USER = 'Deactivate user',
SETUP_USER = 'Setup user',
LOGIN_AS_USER = 'Login as user',
+ USER_ACCOUNT = 'User Account',
};
export type MultiSelectMenuAction = {
--- /dev/null
+// 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<any>(openUserAttributes(resources[0].uuid));
+ },
+ },
+ {
+ name: MultiSelectMenuActionNames.API_DETAILS,
+ icon: AdvancedIcon,
+ hasAlts: false,
+ isForMulti: false,
+ execute: (dispatch, resources) => {
+ dispatch<any>(openAdvancedTabDialog(resources[0].uuid));
+ },
+ },
+ {
+ name: MultiSelectMenuActionNames.USER_ACCOUNT,
+ icon: UserPanelIcon,
+ hasAlts: false,
+ isForMulti: false,
+ execute: (dispatch, resources) => {
+ dispatch<any>(navigateToUserProfile(resources[0].uuid));
+ },
+ },
+ ],
+];
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'
| 'namePlate'
| 'faveIcon'
| 'frozenIcon'
- | 'contextMenuSection'
+ | 'accountStatusSection'
| 'toolbarSection'
| 'tag'
| 'description';
marginBottom: '1rem',
flex: '0 0 auto',
padding: 0,
- borderLeft: '2px solid transparent',
- },
- selected: {
- border: '2pcx solid #ccc',
+ border: '2px solid transparent',
},
showMore: {
cursor: 'pointer',
},
userNameContainer: {
display: 'flex',
+ alignItems: 'center',
},
cardHeaderContainer: {
width: '100%',
height: '1rem',
color: theme.palette.text.primary,
},
- contextMenuSection: {
+ accountStatusSection: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
- paddingTop: '0.25rem',
+ paddingLeft: '1rem',
},
toolbarSection: {
marginTop: '-1rem',
},
});
-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;
return (
<Card
- className={classNames(classes.root, isSelected ? classes.selected : '')}
+ className={classes.root}
onClick={() => handleCardClick(uuid)}
data-cy='user-details-card'
>
- <CardHeader
- className={classes.cardHeader}
- title={
- <section className={classes.userNameContainer}>
- <Typography
- noWrap
- variant='h6'
- >
- {fullName}
- </Typography>
- </section>
- }
- action={
- <section className={classes.contextMenuSection}>
- {!currentResource.isActive && (
- <Typography>
- <UserResourceAccountStatus uuid={uuid} />
- </Typography>
- )}
- <Tooltip
- title='More options'
- disableFocusListener
- >
- <IconButton
- aria-label='More options'
- data-cy='kebab-icon'
- onClick={(ev) => handleContextMenu(ev, currentResource as any, isAdmin)}
+ <Grid
+ container
+ wrap='nowrap'
+ className={classes.cardHeaderContainer}
+ >
+ <CardHeader
+ className={classes.cardHeader}
+ title={
+ <section className={classes.userNameContainer}>
+ <Typography
+ noWrap
+ variant='h6'
>
- <MoreVerticalIcon />
- </IconButton>
- </Tooltip>
- </section>
- }
- />
+ {fullName}
+ </Typography>
+ <section className={classes.accountStatusSection}>
+ {!currentResource.isActive && (
+ <Typography>
+ <UserResourceAccountStatus uuid={uuid} />
+ </Typography>
+ )}
+ </section>
+ </section>
+ }
+ />
+ {isSelected && <MultiselectToolbar />}
+ </Grid>
</Card>
);
};
return (
<Card
- className={classNames(classes.root, isSelected ? classes.selected : '')}
+ className={classes.root}
onClick={() => handleCardClick(uuid)}
data-cy='project-details-card'
>
- <Grid container wrap='nowrap' className={classes.cardHeaderContainer}>
+ <Grid
+ container
+ wrap='nowrap'
+ className={classes.cardHeaderContainer}
+ >
<CardHeader
className={classes.cardHeader}
title={