cy.get('[data-cy=additional-info-icon]').click();
cy.get('[data-cy=details-panel]').within(() => {
- cy.get('[data-cy=property-editor-btn]').click();
+ cy.get('[data-cy=details-panel-edit-btn]').click();
});
cy.get('[data-cy=resource-properties-dialog').contains('Edit properties');
import { unionize, ofType, UnionOf } from 'common/unionize';
import { RootState } from 'store/store';
import { Dispatch } from 'redux';
-import { dialogActions } from 'store/dialog/dialog-actions';
import { getResource } from 'store/resources/resources';
import { ServiceRepository } from 'services/services';
import { resourcesActions } from 'store/resources/resources-actions';
-import {snackbarActions, SnackbarKind} from 'store/snackbar/snackbar-actions';
+import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { FilterBuilder } from 'services/api/filter-builder';
import { OrderBuilder } from 'services/api/order-builder';
import { CollectionResource } from 'models/collection';
export type DetailsPanelAction = UnionOf<typeof detailsPanelActions>;
-export const RESOURCE_PROPERTIES_FORM_NAME = 'resourcePropertiesFormName';
-export const RESOURCE_PROPERTIES_DIALOG_NAME = 'resourcePropertiesDialogName';
-
export const loadDetailsPanel = (uuid: string) =>
(dispatch: Dispatch, getState: () => RootState) => {
if (getState().detailsPanel.isOpened) {
}
};
-export const openResourcePropertiesDialog = () =>
- (dispatch: Dispatch) => {
- dispatch<any>(dialogActions.OPEN_DIALOG({ id: RESOURCE_PROPERTIES_DIALOG_NAME, data: { } }));
- };
-
export const refreshCollectionVersionsList = (uuid: string) =>
(dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
services.collectionService.list({
// SPDX-License-Identifier: AGPL-3.0
import React from 'react';
-import { CollectionIcon } from 'components/icon/icon';
+import { CollectionIcon, RenameIcon } from 'components/icon/icon';
import { CollectionResource } from 'models/collection';
import { DetailsData } from "./details-data";
import { CollectionDetailsAttributes } from 'views/collection-panel/collection-panel';
import { RootState } from 'store/store';
import { filterResources, getResource } from 'store/resources/resources';
import { connect } from 'react-redux';
-import { Grid, ListItem, StyleRulesCallback, Typography, withStyles, WithStyles } from '@material-ui/core';
+import { Button, Grid, ListItem, StyleRulesCallback, Typography, withStyles, WithStyles } from '@material-ui/core';
import { formatDate, formatFileSize } from 'common/formatters';
import { UserNameFromID } from '../data-explorer/renderers';
import { Dispatch } from 'redux';
import { navigateTo } from 'store/navigation/navigation-action';
import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions';
+import { openCollectionUpdateDialog } from 'store/collections/collection-update-actions';
-export type CssRules = 'versionBrowserHeader' | 'versionBrowserItem' | 'versionBrowserField';
+export type CssRules = 'versionBrowserHeader' | 'versionBrowserItem' | 'versionBrowserField' | 'editIcon';
const styles: StyleRulesCallback<CssRules> = theme => ({
versionBrowserHeader: {
},
versionBrowserField: {
textAlign: 'center',
- }
+ },
+ editIcon: {
+ paddingRight: theme.spacing.unit/2,
+ fontSize: '1.125rem',
+ },
});
export class CollectionDetails extends DetailsData<CollectionResource> {
}
private getCollectionInfo() {
- return <CollectionDetailsAttributes twoCol={false} item={this.item} />;
+ return <CollectionInfo />;
}
private getVersionBrowser() {
}
}
+interface CollectionInfoDataProps {
+ currentCollection: CollectionResource | undefined;
+}
+
+interface CollectionInfoDispatchProps {
+ editCollection: (collection: CollectionResource | undefined) => void;
+}
+
+const ciMapStateToProps = (state: RootState): CollectionInfoDataProps => {
+ return {
+ currentCollection: getResource<CollectionResource>(state.detailsPanel.resourceUuid)(state.resources),
+ };
+};
+
+const ciMapDispatchToProps = (dispatch: Dispatch): CollectionInfoDispatchProps => ({
+ editCollection: (collection: CollectionResource) =>
+ dispatch<any>(openCollectionUpdateDialog({
+ uuid: collection.uuid,
+ name: collection.name,
+ description: collection.description,
+ properties: collection.properties,
+ storageClassesDesired: collection.storageClassesDesired,
+ })),
+});
+
+type CollectionInfoProps = CollectionInfoDataProps & CollectionInfoDispatchProps & WithStyles<CssRules>;
+
+const CollectionInfo = withStyles(styles)(
+ connect(ciMapStateToProps, ciMapDispatchToProps)(
+ ({ currentCollection, editCollection, classes }: CollectionInfoProps) =>
+ currentCollection !== undefined
+ ? <div data-cy='details-panel-edit-btn'>
+ <Button onClick={() => editCollection(currentCollection)}>
+ <RenameIcon className={classes.editIcon} /> Edit
+ </Button>
+ <CollectionDetailsAttributes twoCol={false} item={currentCollection} />
+ </div>
+ : <div />
+ )
+);
+
interface CollectionVersionBrowserProps {
currentCollection: CollectionResource | undefined;
versions: CollectionResource[];
handleContextMenu: (event: React.MouseEvent<HTMLElement>, collection: CollectionResource) => void;
}
-const mapStateToProps = (state: RootState): CollectionVersionBrowserProps => {
+const vbMapStateToProps = (state: RootState): CollectionVersionBrowserProps => {
const currentCollection = getResource<CollectionResource>(state.detailsPanel.resourceUuid)(state.resources);
const versions = (currentCollection
&& filterResources(rsc =>
return { currentCollection, versions };
};
-const mapDispatchToProps = () =>
+const vbMapDispatchToProps = () =>
(dispatch: Dispatch): CollectionVersionBrowserDispatchProps => ({
showVersion: (collection) => dispatch<any>(navigateTo(collection.uuid)),
handleContextMenu: (event: React.MouseEvent<HTMLElement>, collection: CollectionResource) => {
});
const CollectionVersionBrowser = withStyles(styles)(
- connect(mapStateToProps, mapDispatchToProps)(
+ connect(vbMapStateToProps, vbMapDispatchToProps)(
({ currentCollection, versions, showVersion, handleContextMenu, classes }: CollectionVersionBrowserProps & CollectionVersionBrowserDispatchProps & WithStyles<CssRules>) => {
return <div data-cy="collection-version-browser">
<Grid container>
import React from 'react';
import { connect } from 'react-redux';
-import { openResourcePropertiesDialog } from 'store/details-panel/details-panel-action';
import { ProjectIcon, RenameIcon, FilterGroupIcon } from 'components/icon/icon';
import { ProjectResource } from 'models/project';
import { formatDate } from 'common/formatters';
import { DetailsData } from "./details-data";
import { DetailsAttribute } from "components/details-attribute/details-attribute";
import { RichTextEditorLink } from 'components/rich-text-editor-link/rich-text-editor-link';
-import { withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
+import { withStyles, StyleRulesCallback, WithStyles, Button } from '@material-ui/core';
import { ArvadosTheme } from 'common/custom-theme';
import { Dispatch } from 'redux';
import { getPropertyChip } from '../resource-properties-form/property-chip';
import { ResourceOwnerWithName } from '../data-explorer/renderers';
import { GroupClass } from "models/group";
+import { openProjectUpdateDialog, ProjectUpdateFormDialogData } from 'store/projects/project-update-actions';
export class ProjectDetails extends DetailsData<ProjectResource> {
getIcon(className?: string) {
marginBottom: theme.spacing.unit
},
editIcon: {
+ paddingRight: theme.spacing.unit/2,
fontSize: '1.125rem',
- cursor: 'pointer'
}
});
}
interface ProjectDetailsComponentActionProps {
- onClick: () => void;
+ onClick: (prj: ProjectUpdateFormDialogData) => () => void;
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
- onClick: () => dispatch<any>(openResourcePropertiesDialog()),
+ onClick: (prj: ProjectUpdateFormDialogData) =>
+ () => dispatch<any>(openProjectUpdateDialog(prj)),
});
type ProjectDetailsComponentProps = ProjectDetailsComponentDataProps & ProjectDetailsComponentActionProps & WithStyles<CssRules>;
const ProjectDetailsComponent = connect(null, mapDispatchToProps)(
withStyles(styles)(
({ classes, project, onClick }: ProjectDetailsComponentProps) => <div>
+ {project.groupClass !== GroupClass.FILTER ?
+ <Button onClick={onClick({
+ uuid: project.uuid,
+ name: project.name,
+ description: project.description,
+ properties: project.properties,
+ })}>
+ <RenameIcon className={classes.editIcon} /> Edit
+ </Button>
+ : ''
+ }
<DetailsAttribute label='Type' value={project.groupClass === GroupClass.FILTER ? 'Filter group' : resourceLabel(ResourceKind.PROJECT)} />
<DetailsAttribute label='Owner' linkToUuid={project.ownerUuid}
uuidEnhancer={(uuid: string) => <ResourceOwnerWithName uuid={uuid} />} />
: '---'
}
</DetailsAttribute>
- <DetailsAttribute label='Properties'>
- {project.groupClass !== GroupClass.FILTER ?
- <div onClick={onClick}>
- <RenameIcon className={classes.editIcon} />
- </div>
- : ''
- }
- </DetailsAttribute>
+ <DetailsAttribute label='Properties' />
{
Object.keys(project.properties).map(k =>
Array.isArray(project.properties[k])
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { reduxForm, reset } from 'redux-form';
-import { RESOURCE_PROPERTIES_FORM_NAME } from 'store/details-panel/details-panel-action';
-import { ResourcePropertiesForm, ResourcePropertiesFormData } from 'views-components/resource-properties-form/resource-properties-form';
-import { withStyles } from '@material-ui/core';
-import { Dispatch } from 'redux';
-import { createResourceProperty } from 'store/resources/resources-actions';
-
-const Form = withStyles(({ spacing }) => ({ container: { marginBottom: spacing.unit * 2 } }))(ResourcePropertiesForm);
-
-export const ResourcePropertiesDialogForm = reduxForm<ResourcePropertiesFormData, {uuid: string}>({
- form: RESOURCE_PROPERTIES_FORM_NAME,
- onSubmit: (data, dispatch: Dispatch) => {
- dispatch<any>(createResourceProperty(data));
- dispatch(reset(RESOURCE_PROPERTIES_FORM_NAME));
- }
-})(Form);
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import React from "react";
-import { Dispatch } from "redux";
-import { connect } from "react-redux";
-import { RootState } from 'store/store';
-import { withDialog, WithDialogProps } from "store/dialog/with-dialog";
-import { RESOURCE_PROPERTIES_DIALOG_NAME } from 'store/details-panel/details-panel-action';
-import {
- Dialog,
- DialogTitle,
- DialogContent,
- DialogActions,
- Button,
- withStyles,
- StyleRulesCallback,
- WithStyles
-} from '@material-ui/core';
-import { ArvadosTheme } from 'common/custom-theme';
-import { ResourcePropertiesDialogForm } from 'views-components/resource-properties-dialog/resource-properties-dialog-form';
-import { getResource } from 'store/resources/resources';
-import { getPropertyChip } from "../resource-properties-form/property-chip";
-import { deleteResourceProperty } from "store/resources/resources-actions";
-import { ResourceWithProperties } from "models/resource";
-
-type CssRules = 'tag';
-
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
- tag: {
- marginRight: theme.spacing.unit,
- marginBottom: theme.spacing.unit
- }
-});
-
-interface ResourcePropertiesDialogDataProps {
- resource: ResourceWithProperties;
-}
-
-interface ResourcePropertiesDialogActionProps {
- handleDelete: (uuid: string, key: string, value: string) => void;
-}
-
-const mapStateToProps = ({ detailsPanel, resources, properties }: RootState): ResourcePropertiesDialogDataProps => ({
- resource: getResource(detailsPanel.resourceUuid)(resources) as ResourceWithProperties,
-});
-
-const mapDispatchToProps = (dispatch: Dispatch): ResourcePropertiesDialogActionProps => ({
- handleDelete: (uuid: string, key: string, value: string) => () => dispatch<any>(deleteResourceProperty(uuid, key, value)),
-});
-
-type ResourcePropertiesDialogProps = ResourcePropertiesDialogDataProps & ResourcePropertiesDialogActionProps & WithDialogProps<{}> & WithStyles<CssRules>;
-
-export const ResourcePropertiesDialog = connect(mapStateToProps, mapDispatchToProps)(
- withStyles(styles)(
- withDialog(RESOURCE_PROPERTIES_DIALOG_NAME)(
- ({ classes, open, closeDialog, handleDelete, resource }: ResourcePropertiesDialogProps) =>
- <Dialog open={open}
- onClose={closeDialog}
- fullWidth
- maxWidth='sm'>
- <div data-cy='resource-properties-dialog'>
- <DialogTitle>Edit properties</DialogTitle>
- <DialogContent>
- <ResourcePropertiesDialogForm uuid={resource ? resource.uuid : ''} />
- {resource && resource.properties &&
- Object.keys(resource.properties).map(k =>
- Array.isArray(resource.properties[k])
- ? resource.properties[k].map((v: string) =>
- getPropertyChip(
- k, v,
- handleDelete(resource.uuid, k, v),
- classes.tag))
- : getPropertyChip(
- k, resource.properties[k],
- handleDelete(resource.uuid, k, resource.properties[k]),
- classes.tag)
- )
- }
- </DialogContent>
- <DialogActions>
- <Button
- data-cy='close-dialog-btn'
- variant='text'
- color='primary'
- onClick={closeDialog}>
- Close
- </Button>
- </DialogActions>
- </div>
- </Dialog>
- )
- ));
// SPDX-License-Identifier: AGPL-3.0
import React from 'react';
-import { Dispatch } from 'redux';
import {
StyleRulesCallback,
WithStyles,
import { RouteComponentProps } from 'react-router';
import { ArvadosTheme } from 'common/custom-theme';
import { RootState } from 'store/store';
-import { MoreOptionsIcon, CollectionIcon, ReadOnlyIcon, CollectionOldVersionIcon, RenameIcon } from 'components/icon/icon';
+import { MoreOptionsIcon, CollectionIcon, ReadOnlyIcon, CollectionOldVersionIcon } from 'components/icon/icon';
import { DetailsAttribute } from 'components/details-attribute/details-attribute';
import { CollectionResource, getCollectionUrl } from 'models/collection';
import { CollectionPanelFiles } from 'views-components/collection-panel-files/collection-panel-files';
import { getResource } from 'store/resources/resources';
import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions';
import { formatDate, formatFileSize } from "common/formatters";
-import { openDetailsPanel, openResourcePropertiesDialog } from 'store/details-panel/details-panel-action';
+import { openDetailsPanel } from 'store/details-panel/details-panel-action';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { getPropertyChip } from 'views-components/resource-properties-form/property-chip';
import { IllegalNamingWarning } from 'components/warning/warning';
)
);
-interface CollectionDetailsActionProps {
- onClick: () => void;
-}
-
interface CollectionDetailsProps {
item: CollectionResource;
classes?: any;
showVersionBrowser?: () => void;
}
-const mapDispatchToProps = (dispatch: Dispatch) => ({
- onClick: () => dispatch<any>(openResourcePropertiesDialog()),
-});
-
-export const CollectionDetailsAttributes = connect(null, mapDispatchToProps)(
-(props: CollectionDetailsProps & CollectionDetailsActionProps) => {
+export const CollectionDetailsAttributes = (props: CollectionDetailsProps) => {
const item = props.item;
const classes = props.classes || { label: '', value: '', button: '', tag: '' };
const isOldVersion = item && item.currentVersionUuid !== item.uuid;
</Grid>
<Grid item xs={12} md={mdSize}>
<DetailsAttribute classLabel={classes.label} classValue={classes.value}
- label='Properties'>
- { !props.twoCol
- ? <div data-cy='property-editor-btn' onClick={props.onClick}>
- <RenameIcon className={classes.editIcon} />
- </div>
- : '' }
- </DetailsAttribute>
+ label='Properties' />
{ Object.keys(item.properties).length > 0
? Object.keys(item.properties).map(k =>
Array.isArray(item.properties[k])
: <div className={classes.value}>No properties</div> }
</Grid>
</Grid>;
-});
+};
import { ProcessInputDialog } from 'views-components/process-input-dialog/process-input-dialog';
import { VirtualMachineUserPanel } from 'views/virtual-machine-panel/virtual-machine-user-panel';
import { VirtualMachineAdminPanel } from 'views/virtual-machine-panel/virtual-machine-admin-panel';
-import { ResourcePropertiesDialog } from 'views-components/resource-properties-dialog/resource-properties-dialog';
import { RepositoriesPanel } from 'views/repositories-panel/repositories-panel';
import { KeepServicePanel } from 'views/keep-service-panel/keep-service-panel';
import { ApiClientAuthorizationPanel } from 'views/api-client-authorization-panel/api-client-authorization-panel';
<PartialCopyToCollectionDialog />
<ProcessCommandDialog />
<ProcessInputDialog />
- <ResourcePropertiesDialog />
<RestoreCollectionVersionDialog />
<RemoveApiClientAuthorizationDialog />
<RemoveGroupDialog />