X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/67434b45485a5660431f0be0b9c55533d3dad729..6f80d59b4d111454ce848f18c93aec8a891ff908:/services/workbench2/src/views-components/project-details-card/project-details-card.tsx 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 423cf3aebf..49884e45de 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 @@ -3,7 +3,7 @@ // SPDX-License-Identifier: AGPL-3.0 import React from 'react'; -import { StyleRulesCallback, Card, CardHeader, WithStyles, withStyles, Typography, CardContent, Tooltip, Collapse } from '@material-ui/core'; +import { StyleRulesCallback, Card, CardHeader, WithStyles, withStyles, Typography, CardContent, Tooltip, Collapse, Grid } from '@material-ui/core'; import { ArvadosTheme } from 'common/custom-theme'; import { RootState } from 'store/store'; import { connect } from 'react-redux'; @@ -14,31 +14,30 @@ 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, openUserContextMenu } 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'; +import { setSelectedResourceUuid } from 'store/selected-resource/selected-resource-actions'; +import { deselectAllOthers } from 'store/multiselect/multiselect-actions'; type CssRules = | 'root' - | 'selected' + | 'cardHeaderContainer' | 'cardHeader' - | 'descriptionLabel' + | 'projectToolbar' + | 'descriptionToggle' | 'showMore' | 'noDescription' - | 'nameContainer' + | 'userNameContainer' | 'cardContent' - | 'subHeader' + | 'nameSection' | 'namePlate' | 'faveIcon' | 'frozenIcon' - | 'contextMenuSection' + | 'accountStatusSection' | 'chipSection' | 'tag' | 'description'; @@ -48,88 +47,99 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ width: '100%', marginBottom: '1rem', flex: '0 0 auto', - paddingTop: '0.2rem', - border: '2px solid transparent', - }, - selected: { - border: '2px solid #ccc', + padding: 0, + minHeight: '3rem', }, showMore: { - color: theme.palette.primary.main, cursor: 'pointer', }, noDescription: { color: theme.palette.grey['600'], fontStyle: 'italic', + padding: '0 0 0.5rem 1rem', + marginTop: '-0.5rem', }, - nameContainer: { + userNameContainer: { display: 'flex', + alignItems: 'center', + minHeight: '2.7rem', + }, + cardHeaderContainer: { + width: '100%', + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', }, cardHeader: { - paddingTop: '0.4rem', + minWidth: '30rem', + padding: '0.2rem 0.4rem 0.2rem 1rem', }, - descriptionLabel: { - paddingTop: '1rem', - marginBottom: 0, - minHeight: '2.5rem', - marginRight: '0.8rem', + projectToolbar: { + //shows only the first 3 buttons + width: '12rem !important', + }, + descriptionToggle: { + display: 'flex', + flexDirection: 'row', + cursor: 'pointer', + paddingBottom: '0.5rem', }, cardContent: { display: 'flex', flexDirection: 'column', - transition: 'height 0.3s ease', + paddingTop: 0, + paddingLeft: '0.1rem', }, - subHeader: { + nameSection: { display: 'flex', flexDirection: 'row', - justifyContent: 'space-between', - marginTop: '-2rem', + alignItems: 'center', }, namePlate: { display: 'flex', flexDirection: 'row', + alignItems: 'center', + margin: 0, + minHeight: '2.7rem', }, faveIcon: { fontSize: '0.8rem', - margin: 'auto 0 0.5rem 0.3rem', + margin: 'auto 0 1rem 0.3rem', color: theme.palette.text.primary, }, frozenIcon: { fontSize: '0.5rem', marginLeft: '0.3rem', - marginTop: '0.57rem', height: '1rem', color: theme.palette.text.primary, }, - contextMenuSection: { + accountStatusSection: { display: 'flex', flexDirection: 'row', alignItems: 'center', - marginTop: '0.6rem', + paddingLeft: '1rem', }, chipSection: { - display: 'flex', - flexWrap: 'wrap', + marginBottom: '-2rem', }, tag: { - marginRight: '1rem', - marginTop: '0.5rem', + marginRight: '0.75rem', + marginBottom: '0.5rem', }, description: { - marginTop: '1rem', + maxWidth: '95%', + marginTop: 0, }, }); -const mapStateToProps = (state: RootState) => { - const currentRoute = state.router.location?.pathname.split('/') || []; - const currentItemUuid = currentRoute[currentRoute.length - 1]; - const currentResource = getResource(currentItemUuid)(state.resources); - const frozenByUser = currentResource && getResource((currentResource as ProjectResource).frozenByUuid as string)(state.resources); +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; - const isSelected = currentItemUuid === state.detailsPanel.resourceUuid && state.detailsPanel.isOpened === true; + const isSelected = selectedResourceUuid === properties.currentRouteUuid; return { - isAdmin: state.auth.user?.isAdmin, + isAdmin: auth.user?.isAdmin, currentResource, frozenByFullName, isSelected, @@ -139,35 +149,10 @@ const mapStateToProps = (state: RootState) => { const mapDispatchToProps = (dispatch: Dispatch) => ({ handleCardClick: (uuid: string) => { dispatch(loadDetailsPanel(uuid)); + dispatch(setSelectedResourceUuid(uuid)); + dispatch(deselectAllOthers(uuid)); }, - handleContextMenu: (event: React.MouseEvent, resource: any, isAdmin: boolean) => { - event.stopPropagation(); - // When viewing the contents of a filter group, all contents should be treated as read only. - let readOnly = false; - if (resource.groupClass === 'filter') { - readOnly = true; - } - const menuKind = dispatch(resourceUuidToContextMenuKind(resource.uuid, readOnly)); - if (menuKind === ContextMenuKind.ROOT_PROJECT) { - dispatch(openUserContextMenu(event, resource as UserResource)); - } else if (menuKind && resource) { - dispatch( - openContextMenu(event, { - name: resource.name, - uuid: resource.uuid, - ownerUuid: resource.ownerUuid, - isTrashed: 'isTrashed' in resource ? resource.isTrashed : false, - kind: resource.kind, - menuKind, - isAdmin, - isFrozen: !!resource.frozenByUuid, - description: resource.description, - storageClassesDesired: (resource as CollectionResource).storageClassesDesired, - properties: 'properties' in resource ? resource.properties : {}, - }) - ); - } - }, + }); type DetailsCardProps = WithStyles & { @@ -175,7 +160,6 @@ type DetailsCardProps = WithStyles & { frozenByFullName?: string; isAdmin: boolean; isSelected: boolean; - handleContextMenu: (event: React.MouseEvent, resource: ContextMenuResource, isAdmin: boolean) => void; handleCardClick: (resource: any) => void; }; @@ -183,7 +167,6 @@ type UserCardProps = WithStyles & { currentResource: UserResource; isAdmin: boolean; isSelected: boolean; - handleContextMenu: (event: React.MouseEvent, resource: ContextMenuResource, isAdmin: boolean) => void; handleCardClick: (resource: any) => void; }; @@ -192,7 +175,6 @@ type ProjectCardProps = WithStyles & { frozenByFullName: string | undefined; isAdmin: boolean; isSelected: boolean; - handleContextMenu: (event: React.MouseEvent, resource: ContextMenuResource, isAdmin: boolean) => void; handleCardClick: (resource: any) => void; }; @@ -201,7 +183,7 @@ export const ProjectDetailsCard = connect( mapDispatchToProps )( withStyles(styles)((props: DetailsCardProps) => { - const { classes, currentResource, frozenByFullName, handleContextMenu, handleCardClick, isAdmin, isSelected } = props; + const { classes, currentResource, frozenByFullName, handleCardClick, isAdmin, isSelected } = props; if (!currentResource) { return null; } @@ -213,7 +195,6 @@ export const ProjectDetailsCard = connect( currentResource={currentResource as UserResource} isAdmin={isAdmin} isSelected={isSelected} - handleContextMenu={(ev) => handleContextMenu(ev, currentResource as any, isAdmin)} handleCardClick={handleCardClick} /> ); @@ -225,7 +206,6 @@ export const ProjectDetailsCard = connect( frozenByFullName={frozenByFullName} isAdmin={isAdmin} isSelected={isSelected} - handleContextMenu={(ev) => handleContextMenu(ev, currentResource as any, isAdmin)} handleCardClick={handleCardClick} /> ); @@ -235,136 +215,166 @@ export const ProjectDetailsCard = connect( }) ); -const UserCard: React.FC = ({ classes, currentResource, handleContextMenu, handleCardClick, isAdmin, isSelected }) => { +const UserCard: React.FC = ({ classes, currentResource, handleCardClick, isSelected }) => { const { fullName, uuid } = currentResource as UserResource & { fullName: string }; return ( - handleCardClick(uuid)} data-cy='user-details-card'> - - - {fullName} - - - } - action={ -
- {!currentResource.isActive && ( - - - - )} - - handleContextMenu(ev, currentResource as any, isAdmin)} + handleCardClick(uuid)} + data-cy='user-details-card' + > + + + - - - -
- } - /> + {fullName} + +
+ {!currentResource.isActive && ( + + + + )} +
+ + } + /> + {isSelected && } +
); }; -const ProjectCard: React.FC = ({ classes, currentResource, frozenByFullName, handleContextMenu, handleCardClick, isAdmin, isSelected }) => { +const ProjectCard: React.FC = ({ classes, currentResource, frozenByFullName, handleCardClick, isSelected }) => { const { name, description, uuid } = currentResource as ProjectResource; const [showDescription, setShowDescription] = React.useState(false); + const [showProperties, setShowProperties] = React.useState(false); const toggleDescription = () => { setShowDescription(!showDescription); }; + const toggleProperties = () => { + setShowProperties(!showProperties); + }; + return ( - handleCardClick(uuid)} data-cy='project-details-card'> - - - {name} - - - - {!!frozenByFullName && ( - Project was frozen by {frozenByFullName}} - > - - - )} - - } - action={ -
- - handleContextMenu(ev, currentResource as any, isAdmin)} - > - - - -
- } - /> - -
-
- - {typeof currentResource.properties === 'object' && - Object.keys(currentResource.properties).map((k) => - Array.isArray(currentResource.properties[k]) - ? currentResource.properties[k].map((v: string) => getPropertyChip(k, v, undefined, classes.tag)) - : getPropertyChip(k, currentResource.properties[k], undefined, classes.tag) + handleCardClick(uuid)} + data-cy='project-details-card' + > + + +
+ + {name} + + + + {!!frozenByFullName && ( + Project was frozen by {frozenByFullName}} + > + + )} - -
-
ev.stopPropagation()}> - {description ? ( - +
+ } + /> + {isSelected && } +
+
ev.stopPropagation()}> + {description ? ( +
+ +
+ - {!showDescription ? "Show full description" : "Hide full description"} - - ) : ( - no description available - )} + + +
-
- -
ev.stopPropagation()}> - - {description} - + ) : ( + + no description available + + )} + {typeof currentResource.properties === 'object' && Object.keys(currentResource.properties).length > 0 ? ( +
+ +
+ +
+ + + {Object.keys(currentResource.properties).map((k) => + Array.isArray(currentResource.properties[k]) + ? currentResource.properties[k].map((v: string) => getPropertyChip(k, v, undefined, classes.tag)) + : getPropertyChip(k, currentResource.properties[k], undefined, classes.tag) + )} + + +
+
+
- - + ) : null} +
); };