From: Lucas Di Pentima Date: Wed, 15 Dec 2021 19:31:32 +0000 (-0300) Subject: 18219: Replaces properties form on collection panel. X-Git-Tag: 2.4.0~21^2~14 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/d60348e41975a5e48f09773f4d0accec11e93da4 18219: Replaces properties form on collection panel. * Removes properties form subpanel from the main collection panel. * Adds property chips to collection's info & details panel. * Allows property editing from the details panel. * Replaces resource-specific property form components with a generic one. Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima --- diff --git a/src/store/collection-panel/collection-panel-action.ts b/src/store/collection-panel/collection-panel-action.ts index ee476524..c50ff6a8 100644 --- a/src/store/collection-panel/collection-panel-action.ts +++ b/src/store/collection-panel/collection-panel-action.ts @@ -9,15 +9,12 @@ import { import { CollectionResource } from 'models/collection'; import { RootState } from "store/store"; import { ServiceRepository } from "services/services"; -import { TagProperty } from "models/tag"; import { snackbarActions } from "../snackbar/snackbar-actions"; import { resourcesActions } from "store/resources/resources-actions"; import { unionize, ofType, UnionOf } from 'common/unionize'; import { SnackbarKind } from 'store/snackbar/snackbar-actions'; import { navigateTo } from 'store/navigation/navigation-action'; import { loadDetailsPanel } from 'store/details-panel/details-panel-action'; -import { addProperty, deleteProperty } from "lib/resource-properties"; -import { getResource } from "store/resources/resources"; export const collectionPanelActions = unionize({ SET_COLLECTION: ofType(), @@ -27,8 +24,6 @@ export const collectionPanelActions = unionize({ export type CollectionPanelAction = UnionOf; -export const COLLECTION_TAG_FORM_NAME = 'collectionTagForm'; - export const loadCollectionPanel = (uuid: string, forceReload = false) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const { collectionPanel: { item } } = getState(); @@ -44,37 +39,6 @@ export const loadCollectionPanel = (uuid: string, forceReload = false) => return collection; }; -export const createCollectionTag = (data: TagProperty) => - (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - const item = getState().collectionPanel.item; - if (!item) { return; } - - const properties = Object.assign({}, item.properties); - const key = data.keyID || data.key; - const value = data.valueID || data.value; - const cachedCollection = getResource(item.uuid)(getState().resources); - services.collectionService.update( - item.uuid, { - properties: addProperty(properties, key, value) - } - ).then(updatedCollection => { - updatedCollection = {...cachedCollection, ...updatedCollection}; - dispatch(collectionPanelActions.SET_COLLECTION(updatedCollection)); - dispatch(resourcesActions.SET_RESOURCES([updatedCollection])); - dispatch(snackbarActions.OPEN_SNACKBAR({ - message: "Property has been successfully added.", - hideDuration: 2000, - kind: SnackbarKind.SUCCESS })); - dispatch(loadDetailsPanel(updatedCollection.uuid)); - return updatedCollection; - }).catch (e => - dispatch(snackbarActions.OPEN_SNACKBAR({ - message: e.errors[0], - hideDuration: 2000, - kind: SnackbarKind.ERROR })) - ); - }; - export const navigateToProcess = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { try { @@ -84,29 +48,3 @@ export const navigateToProcess = (uuid: string) => dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'This process does not exist!', hideDuration: 2000, kind: SnackbarKind.ERROR })); } }; - -export const deleteCollectionTag = (key: string, value: string) => - (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - const item = getState().collectionPanel.item; - if (!item) { return; } - - const properties = Object.assign({}, item.properties); - const cachedCollection = getResource(item.uuid)(getState().resources); - services.collectionService.update( - item.uuid, { - properties: deleteProperty(properties, key, value) - } - ).then(updatedCollection => { - updatedCollection = {...cachedCollection, ...updatedCollection}; - dispatch(collectionPanelActions.SET_COLLECTION(updatedCollection)); - dispatch(resourcesActions.SET_RESOURCES([updatedCollection])); - dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Tag has been successfully deleted.", hideDuration: 2000, kind: SnackbarKind.SUCCESS })); - dispatch(loadDetailsPanel(updatedCollection.uuid)); - return updatedCollection; - }).catch (e => { - dispatch(snackbarActions.OPEN_SNACKBAR({ - message: e.errors[0], - hideDuration: 2000, - kind: SnackbarKind.ERROR })); - }); - }; diff --git a/src/store/details-panel/details-panel-action.ts b/src/store/details-panel/details-panel-action.ts index bda35441..90ca0f4f 100644 --- a/src/store/details-panel/details-panel-action.ts +++ b/src/store/details-panel/details-panel-action.ts @@ -7,13 +7,9 @@ import { RootState } from 'store/store'; import { Dispatch } from 'redux'; import { dialogActions } from 'store/dialog/dialog-actions'; import { getResource } from 'store/resources/resources'; -import { ProjectResource } from "models/project"; import { ServiceRepository } from 'services/services'; -import { TagProperty } from 'models/tag'; -import { startSubmit, stopSubmit } from 'redux-form'; import { resourcesActions } from 'store/resources/resources-actions'; import {snackbarActions, SnackbarKind} from 'store/snackbar/snackbar-actions'; -import { addProperty, deleteProperty } from 'lib/resource-properties'; import { FilterBuilder } from 'services/api/filter-builder'; import { OrderBuilder } from 'services/api/order-builder'; import { CollectionResource } from 'models/collection'; @@ -29,8 +25,8 @@ export const detailsPanelActions = unionize({ export type DetailsPanelAction = UnionOf; -export const PROJECT_PROPERTIES_FORM_NAME = 'projectPropertiesFormName'; -export const PROJECT_PROPERTIES_DIALOG_NAME = 'projectPropertiesDialogName'; +export const RESOURCE_PROPERTIES_FORM_NAME = 'resourcePropertiesFormName'; +export const RESOURCE_PROPERTIES_DIALOG_NAME = 'resourcePropertiesDialogName'; export const loadDetailsPanel = (uuid: string) => (dispatch: Dispatch, getState: () => RootState) => { @@ -55,9 +51,9 @@ export const openDetailsPanel = (uuid?: string, tabNr: number = 0) => } }; -export const openProjectPropertiesDialog = () => +export const openResourcePropertiesDialog = () => (dispatch: Dispatch) => { - dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_PROPERTIES_DIALOG_NAME, data: { } })); + dispatch(dialogActions.OPEN_DIALOG({ id: RESOURCE_PROPERTIES_DIALOG_NAME, data: { } })); }; export const refreshCollectionVersionsList = (uuid: string) => @@ -76,49 +72,6 @@ export const refreshCollectionVersionsList = (uuid: string) => ); }; -export const deleteProjectProperty = (key: string, value: string) => - async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - const { detailsPanel, resources } = getState(); - const project = getResource(detailsPanel.resourceUuid)(resources) as ProjectResource; - if (!project) { return; } - - const properties = Object.assign({}, project.properties); - - try { - const updatedProject = await services.projectService.update( - project.uuid, { - properties: deleteProperty(properties, key, value), - }); - dispatch(resourcesActions.SET_RESOURCES([updatedProject])); - dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully deleted.", hideDuration: 2000, kind: SnackbarKind.SUCCESS })); - } catch (e) { - dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.errors[0], hideDuration: 2000, kind: SnackbarKind.ERROR })); - } - }; - -export const createProjectProperty = (data: TagProperty) => - async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - const { detailsPanel, resources } = getState(); - const project = getResource(detailsPanel.resourceUuid)(resources) as ProjectResource; - if (!project) { return; } - - dispatch(startSubmit(PROJECT_PROPERTIES_FORM_NAME)); - try { - const key = data.keyID || data.key; - const value = data.valueID || data.value; - const properties = Object.assign({}, project.properties); - const updatedProject = await services.projectService.update( - project.uuid, { - properties: addProperty(properties, key, value), - } - ); - dispatch(resourcesActions.SET_RESOURCES([updatedProject])); - dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully added.", hideDuration: 2000, kind: SnackbarKind.SUCCESS })); - dispatch(stopSubmit(PROJECT_PROPERTIES_FORM_NAME)); - } catch (e) { - dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.errors[0], hideDuration: 2000, kind: SnackbarKind.ERROR })); - } - }; export const toggleDetailsPanel = () => (dispatch: Dispatch, getState: () => RootState) => { // because of material-ui issue resizing details panel breaks tabs. // triggering window resize event fixes that. diff --git a/src/store/resources/resources-actions.ts b/src/store/resources/resources-actions.ts index 6c05da32..8e6d16f9 100644 --- a/src/store/resources/resources-actions.ts +++ b/src/store/resources/resources-actions.ts @@ -3,11 +3,15 @@ // SPDX-License-Identifier: AGPL-3.0 import { unionize, ofType, UnionOf } from 'common/unionize'; -import { extractUuidKind, Resource } from 'models/resource'; +import { extractUuidKind, Resource, ResourceWithProperties } from 'models/resource'; import { Dispatch } from 'redux'; import { RootState } from 'store/store'; import { ServiceRepository } from 'services/services'; import { getResourceService } from 'services/services'; +import { addProperty, deleteProperty } from 'lib/resource-properties'; +import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; +import { getResource } from './resources'; +import { TagProperty } from 'models/tag'; export const resourcesActions = unionize({ SET_RESOURCES: ofType(), @@ -33,3 +37,59 @@ export const loadResource = (uuid: string, showErrors?: boolean) => } catch {} return undefined; }; + +export const deleteResourceProperty = (uuid: string, key: string, value: string) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const { resources } = getState(); + + const rsc = getResource(uuid)(resources) as ResourceWithProperties; + if (!rsc) { return; } + + const kind = extractUuidKind(uuid); + const service = getResourceService(kind)(services); + if (!service) { return; } + + const properties = Object.assign({}, rsc.properties); + + try { + let updatedRsc = await service.update( + uuid, { + properties: deleteProperty(properties, key, value), + }); + updatedRsc = {...rsc, ...updatedRsc}; + dispatch(updateResources([updatedRsc])); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully deleted.", hideDuration: 2000, kind: SnackbarKind.SUCCESS })); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.errors[0], hideDuration: 2000, kind: SnackbarKind.ERROR })); + } + }; + +export const createResourceProperty = (data: TagProperty) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const { uuid } = data; + const { resources } = getState(); + + const rsc = getResource(uuid)(resources) as ResourceWithProperties; + if (!rsc) { return; } + + const kind = extractUuidKind(uuid); + const service = getResourceService(kind)(services); + if (!service) { return; } + + try { + const key = data.keyID || data.key; + const value = data.valueID || data.value; + const properties = Object.assign({}, rsc.properties); + let updatedRsc = await service.update( + rsc.uuid, { + properties: addProperty(properties, key, value), + } + ); + updatedRsc = {...rsc, ...updatedRsc}; + dispatch(updateResources([updatedRsc])); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully added.", hideDuration: 2000, kind: SnackbarKind.SUCCESS })); + } catch (e) { + const errorMsg = e.errors && e.errors.length > 0 ? e.errors[0] : "Error while adding property"; + dispatch(snackbarActions.OPEN_SNACKBAR({ message: errorMsg, hideDuration: 2000, kind: SnackbarKind.ERROR })); + } + }; diff --git a/src/views-components/details-panel/project-details.tsx b/src/views-components/details-panel/project-details.tsx index 41ba6f00..82e3e754 100644 --- a/src/views-components/details-panel/project-details.tsx +++ b/src/views-components/details-panel/project-details.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { openProjectPropertiesDialog } from 'store/details-panel/details-panel-action'; +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'; @@ -55,7 +55,7 @@ interface ProjectDetailsComponentActionProps { } const mapDispatchToProps = (dispatch: Dispatch) => ({ - onClick: () => dispatch(openProjectPropertiesDialog()), + onClick: () => dispatch(openResourcePropertiesDialog()), }); type ProjectDetailsComponentProps = ProjectDetailsComponentDataProps & ProjectDetailsComponentActionProps & WithStyles; diff --git a/src/views-components/project-properties-dialog/project-properties-dialog.tsx b/src/views-components/project-properties-dialog/project-properties-dialog.tsx deleted file mode 100644 index 19d3bb56..00000000 --- a/src/views-components/project-properties-dialog/project-properties-dialog.tsx +++ /dev/null @@ -1,81 +0,0 @@ -// 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 { ProjectResource } from 'models/project'; -import { PROJECT_PROPERTIES_DIALOG_NAME, deleteProjectProperty } 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 { ProjectPropertiesForm } from 'views-components/project-properties-dialog/project-properties-form'; -import { getResource } from 'store/resources/resources'; -import { getPropertyChip } from "../resource-properties-form/property-chip"; - -type CssRules = 'tag'; - -const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ - tag: { - marginRight: theme.spacing.unit, - marginBottom: theme.spacing.unit - } -}); - -interface ProjectPropertiesDialogDataProps { - project: ProjectResource; -} - -interface ProjectPropertiesDialogActionProps { - handleDelete: (key: string, value: string) => void; -} - -const mapStateToProps = ({ detailsPanel, resources, properties }: RootState): ProjectPropertiesDialogDataProps => ({ - project: getResource(detailsPanel.resourceUuid)(resources) as ProjectResource, -}); - -const mapDispatchToProps = (dispatch: Dispatch): ProjectPropertiesDialogActionProps => ({ - handleDelete: (key: string, value: string) => () => dispatch(deleteProjectProperty(key, value)), -}); - -type ProjectPropertiesDialogProps = ProjectPropertiesDialogDataProps & ProjectPropertiesDialogActionProps & WithDialogProps<{}> & WithStyles; - -export const ProjectPropertiesDialog = connect(mapStateToProps, mapDispatchToProps)( - withStyles(styles)( - withDialog(PROJECT_PROPERTIES_DIALOG_NAME)( - ({ classes, open, closeDialog, handleDelete, project }: ProjectPropertiesDialogProps) => - - Properties - - - {project && project.properties && - Object.keys(project.properties).map(k => - Array.isArray(project.properties[k]) - ? project.properties[k].map((v: string) => - getPropertyChip( - k, v, - handleDelete(k, v), - classes.tag)) - : getPropertyChip( - k, project.properties[k], - handleDelete(k, project.properties[k]), - classes.tag) - ) - } - - - - - - ) - )); diff --git a/src/views-components/project-properties-dialog/project-properties-form.tsx b/src/views-components/resource-properties-dialog/resource-properties-dialog-form.tsx similarity index 56% rename from src/views-components/project-properties-dialog/project-properties-form.tsx rename to src/views-components/resource-properties-dialog/resource-properties-dialog-form.tsx index f36bacf4..cfb999cc 100644 --- a/src/views-components/project-properties-dialog/project-properties-form.tsx +++ b/src/views-components/resource-properties-dialog/resource-properties-dialog-form.tsx @@ -3,17 +3,18 @@ // SPDX-License-Identifier: AGPL-3.0 import { reduxForm, reset } from 'redux-form'; -import { PROJECT_PROPERTIES_FORM_NAME, createProjectProperty } from 'store/details-panel/details-panel-action'; +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 ProjectPropertiesForm = reduxForm({ - form: PROJECT_PROPERTIES_FORM_NAME, +export const ResourcePropertiesDialogForm = reduxForm({ + form: RESOURCE_PROPERTIES_FORM_NAME, onSubmit: (data, dispatch: Dispatch) => { - dispatch(createProjectProperty(data)); - dispatch(reset(PROJECT_PROPERTIES_FORM_NAME)); + dispatch(createResourceProperty(data)); + dispatch(reset(RESOURCE_PROPERTIES_FORM_NAME)); } })(Form); diff --git a/src/views-components/resource-properties-dialog/resource-properties-dialog.tsx b/src/views-components/resource-properties-dialog/resource-properties-dialog.tsx new file mode 100644 index 00000000..b634715f --- /dev/null +++ b/src/views-components/resource-properties-dialog/resource-properties-dialog.tsx @@ -0,0 +1,91 @@ +// 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 = (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(deleteResourceProperty(uuid, key, value)), +}); + +type ResourcePropertiesDialogProps = ResourcePropertiesDialogDataProps & ResourcePropertiesDialogActionProps & WithDialogProps<{}> & WithStyles; + +export const ResourcePropertiesDialog = connect(mapStateToProps, mapDispatchToProps)( + withStyles(styles)( + withDialog(RESOURCE_PROPERTIES_DIALOG_NAME)( + ({ classes, open, closeDialog, handleDelete, resource }: ResourcePropertiesDialogProps) => + + Edit properties + + + {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) + ) + } + + + + + + ) + )); diff --git a/src/views-components/resource-properties-form/resource-properties-form.tsx b/src/views-components/resource-properties-form/resource-properties-form.tsx index 38d76e46..94a3c955 100644 --- a/src/views-components/resource-properties-form/resource-properties-form.tsx +++ b/src/views-components/resource-properties-form/resource-properties-form.tsx @@ -11,16 +11,18 @@ import { ProgressButton } from 'components/progress-button/progress-button'; import { GridClassKey } from '@material-ui/core/Grid'; export interface ResourcePropertiesFormData { + uuid: string; [PROPERTY_KEY_FIELD_NAME]: string; [PROPERTY_KEY_FIELD_ID]: string; [PROPERTY_VALUE_FIELD_NAME]: string; [PROPERTY_VALUE_FIELD_ID]: string; } -export type ResourcePropertiesFormProps = InjectedFormProps & WithStyles; +export type ResourcePropertiesFormProps = {uuid: string; } & InjectedFormProps & WithStyles; -export const ResourcePropertiesForm = ({ handleSubmit, submitting, invalid, classes }: ResourcePropertiesFormProps ) => -
+export const ResourcePropertiesForm = ({ handleSubmit, change, submitting, invalid, classes, uuid }: ResourcePropertiesFormProps ) => { + change('uuid', uuid); // Sets the uuid field to the uuid of the resource. + return @@ -40,7 +42,7 @@ export const ResourcePropertiesForm = ({ handleSubmit, submitting, invalid, clas -
; + }; export const Button = withStyles(theme => ({ root: { marginTop: theme.spacing.unit } diff --git a/src/views/collection-panel/collection-panel.tsx b/src/views/collection-panel/collection-panel.tsx index 794e093f..5aeaef18 100644 --- a/src/views/collection-panel/collection-panel.tsx +++ b/src/views/collection-panel/collection-panel.tsx @@ -3,6 +3,7 @@ // SPDX-License-Identifier: AGPL-3.0 import React from 'react'; +import { Dispatch } from 'redux'; import { StyleRulesCallback, WithStyles, @@ -11,22 +12,21 @@ import { Grid, Tooltip, Typography, - Card, CardHeader, CardContent, + Card } from '@material-ui/core'; import { connect, DispatchProp } from "react-redux"; import { RouteComponentProps } from 'react-router'; import { ArvadosTheme } from 'common/custom-theme'; import { RootState } from 'store/store'; -import { MoreOptionsIcon, CollectionIcon, ReadOnlyIcon, CollectionOldVersionIcon } from 'components/icon/icon'; +import { MoreOptionsIcon, CollectionIcon, ReadOnlyIcon, CollectionOldVersionIcon, RenameIcon } 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 { CollectionTagForm } from './collection-tag-form'; -import { deleteCollectionTag, navigateToProcess, collectionPanelActions } from 'store/collection-panel/collection-panel-action'; +import { navigateToProcess, collectionPanelActions } from 'store/collection-panel/collection-panel-action'; import { getResource } from 'store/resources/resources'; import { openContextMenu, resourceUuidToContextMenuKind } from 'store/context-menu/context-menu-actions'; import { formatDate, formatFileSize } from "common/formatters"; -import { openDetailsPanel } from 'store/details-panel/details-panel-action'; +import { openDetailsPanel, openResourcePropertiesDialog } 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'; @@ -148,7 +148,6 @@ export const CollectionPanel = withStyles(styles)( const { classes, item, dispatch, isWritable, isOldVersion, isLoadingFiles, tooManyFiles } = this.props; const panelsData: MPVPanelState[] = [ {name: "Details"}, - {name: "Properties"}, {name: "Files"}, ]; return item @@ -203,37 +202,6 @@ export const CollectionPanel = withStyles(styles)( - - - - - {isWritable && - - } - - {Object.keys(item.properties).length > 0 - ? Object.keys(item.properties).map(k => - Array.isArray(item.properties[k]) - ? item.properties[k].map((v: string) => - getPropertyChip( - k, v, - isWritable - ? this.handleDelete(k, v) - : undefined, - classes.tag)) - : getPropertyChip( - k, item.properties[k], - isWritable - ? this.handleDelete(k, item.properties[k]) - : undefined, - classes.tag) - ) - :
No properties set on this collection.
- } -
-
-
-
() => { - this.props.dispatch(deleteCollectionTag(key, value)); - } - openCollectionDetails = (e: React.MouseEvent) => { const { item } = this.props; if (item) { @@ -295,9 +259,25 @@ export const CollectionPanel = withStyles(styles)( ) ); -export const CollectionDetailsAttributes = (props: { item: CollectionResource, twoCol: boolean, classes?: Record, showVersionBrowser?: () => void }) => { +interface CollectionDetailsActionProps { + onClick: () => void; +} + +interface CollectionDetailsProps { + item: CollectionResource; + classes?: any; + twoCol?: boolean; + showVersionBrowser?: () => void; +} + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + onClick: () => dispatch(openResourcePropertiesDialog()), +}); + +export const CollectionDetailsAttributes = connect(null, mapDispatchToProps)( +(props: CollectionDetailsProps & CollectionDetailsActionProps) => { const item = props.item; - const classes = props.classes || { label: '', value: '', button: '' }; + const classes = props.classes || { label: '', value: '', button: '', tag: '' }; const isOldVersion = item && item.currentVersionUuid !== item.uuid; const mdSize = props.twoCol ? 6 : 12; const showVersionBrowser = props.showVersionBrowser; @@ -361,5 +341,22 @@ export const CollectionDetailsAttributes = (props: { item: CollectionResource, t + + + { !props.twoCol + ?
+ +
+ : '' } +
+ { Object.keys(item.properties).length > 0 + ? Object.keys(item.properties).map(k => + Array.isArray(item.properties[k]) + ? item.properties[k].map((v: string) => + getPropertyChip(k, v, undefined, classes.tag)) + : getPropertyChip(k, item.properties[k], undefined, classes.tag)) + :
No properties
} +
; -}; +}); diff --git a/src/views/collection-panel/collection-tag-form.tsx b/src/views/collection-panel/collection-tag-form.tsx deleted file mode 100644 index 6d9cbd59..00000000 --- a/src/views/collection-panel/collection-tag-form.tsx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) The Arvados Authors. All rights reserved. -// -// SPDX-License-Identifier: AGPL-3.0 - -import { reduxForm, reset } from 'redux-form'; -import { createCollectionTag, COLLECTION_TAG_FORM_NAME } from 'store/collection-panel/collection-panel-action'; -import { ResourcePropertiesForm, ResourcePropertiesFormData } from 'views-components/resource-properties-form/resource-properties-form'; -import { withStyles } from '@material-ui/core'; -import { Dispatch } from 'redux'; - -const Form = withStyles(({ spacing }) => ({ container: { marginBottom: spacing.unit * 2 } }))(ResourcePropertiesForm); - -export const CollectionTagForm = reduxForm({ - form: COLLECTION_TAG_FORM_NAME, - onSubmit: (data, dispatch: Dispatch) => { - dispatch(createCollectionTag(data)); - dispatch(reset(COLLECTION_TAG_FORM_NAME)); - } -})(Form); diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 25d70776..ea24c872 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -54,7 +54,7 @@ import { AdvancedTabDialog } from 'views-components/advanced-tab-dialog/advanced 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 { ProjectPropertiesDialog } from 'views-components/project-properties-dialog/project-properties-dialog'; +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'; @@ -242,7 +242,7 @@ export const WorkbenchPanel = - +