17573: Adds storage selection checkboxes to edit dialog.
authorLucas Di Pentima <lucas.dipentima@curii.com>
Wed, 14 Jul 2021 21:04:22 +0000 (18:04 -0300)
committerLucas Di Pentima <lucas.dipentima@curii.com>
Wed, 14 Jul 2021 22:28:47 +0000 (19:28 -0300)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima@curii.com>

src/store/collections/collection-update-actions.ts
src/store/context-menu/context-menu-actions.ts
src/views-components/details-panel/collection-details.tsx
src/views-components/dialog-forms/update-collection-dialog.ts
src/views-components/dialog-update/dialog-collection-update.tsx
src/views-components/form-fields/collection-form-fields.tsx
src/views/collection-content-address-panel/collection-content-address-panel.tsx
src/views/favorite-panel/favorite-panel.tsx
src/views/project-panel/project-panel.tsx
src/views/public-favorites-panel/public-favorites-panel.tsx

index 3f3b662fc99b9901c5d90417974859dbda0f9fda..a9077cfb7455db4fd5c4b6a133502e9eb6da72ed 100644 (file)
@@ -19,6 +19,7 @@ export interface CollectionUpdateFormDialogData {
     uuid: string;
     name: string;
     description?: string;
+    storageClassesDesired?: string[];
 }
 
 export const COLLECTION_UPDATE_FORM_NAME = 'collectionUpdateFormName';
@@ -37,6 +38,7 @@ export const updateCollection = (collection: CollectionUpdateFormDialogData) =>
 
         services.collectionService.update(uuid, {
             name: collection.name,
+            storageClassesDesired: collection.storageClassesDesired,
             description: collection.description }
         ).then(updatedCollection => {
             dispatch(collectionPanelActions.LOAD_COLLECTION_SUCCESS({ item: updatedCollection as CollectionResource }));
index 038b31e2decef3d6a12cceee199cb596e6aeb241..556d83b4900a3731ee714ab983cd76ef1a99baea 100644 (file)
@@ -39,6 +39,7 @@ export type ContextMenuResource = {
     isEditable?: boolean;
     outputUuid?: string;
     workflowUuid?: string;
+    storageClassesDesired?: string[];
 };
 
 export const isKeyboardClick = (event: React.MouseEvent<HTMLElement>) => event.nativeEvent.detail === 0;
index 0e747fed390eb508262c1bd0da29bd16ebb66dd9..c61b3340c35ee92b02d8c7f2fb0346d56d6c5666 100644 (file)
@@ -87,6 +87,8 @@ const mapDispatchToProps = () =>
                 dispatch<any>(openContextMenu(event, {
                     name: collection.name,
                     uuid: collection.uuid,
+                    description: collection.description,
+                    storageClassesDesired: collection.storageClassesDesired,
                     ownerUuid: collection.ownerUuid,
                     isTrashed: collection.isTrashed,
                     kind: collection.kind,
index 36e5cc390d65207d34dd60fc39f9c4813d823fc0..e5d52f0bd28ca54a390a9c7d71ddcc4c77d28ede 100644 (file)
@@ -2,19 +2,23 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { compose } from "redux";
+import { compose, Dispatch } from "redux";
 import { reduxForm } from 'redux-form';
 import { withDialog } from "store/dialog/with-dialog";
 import { DialogCollectionUpdate } from 'views-components/dialog-update/dialog-collection-update';
-import { COLLECTION_UPDATE_FORM_NAME, CollectionUpdateFormDialogData, updateCollection } from 'store/collections/collection-update-actions';
+import {
+    COLLECTION_UPDATE_FORM_NAME,
+    CollectionUpdateFormDialogData,
+    updateCollection
+} from 'store/collections/collection-update-actions';
 
 export const UpdateCollectionDialog = compose(
     withDialog(COLLECTION_UPDATE_FORM_NAME),
     reduxForm<CollectionUpdateFormDialogData>({
         touchOnChange: true,
         form: COLLECTION_UPDATE_FORM_NAME,
-        onSubmit: (data, dispatch) => {
-            dispatch(updateCollection(data));
+        onSubmit: (data: CollectionUpdateFormDialogData, dispatch: Dispatch) => {
+            dispatch<any>(updateCollection(data));
         }
     })
 )(DialogCollectionUpdate);
\ No newline at end of file
index c30ceaac9e24527f65d13880395351f0ed2caa11..cce64d27ac8a987449b4714abbb8d6b1e62249bb 100644 (file)
@@ -7,7 +7,11 @@ import { InjectedFormProps } from 'redux-form';
 import { WithDialogProps } from 'store/dialog/with-dialog';
 import { CollectionUpdateFormDialogData } from 'store/collections/collection-update-actions';
 import { FormDialog } from 'components/form-dialog/form-dialog';
-import { CollectionNameField, CollectionDescriptionField } from 'views-components/form-fields/collection-form-fields';
+import {
+    CollectionNameField,
+    CollectionDescriptionField,
+    CollectionStorageClassesField
+} from 'views-components/form-fields/collection-form-fields';
 
 type DialogCollectionProps = WithDialogProps<{}> & InjectedFormProps<CollectionUpdateFormDialogData>;
 
@@ -22,4 +26,5 @@ export const DialogCollectionUpdate = (props: DialogCollectionProps) =>
 const CollectionEditFields = () => <span>
     <CollectionNameField />
     <CollectionDescriptionField />
+    <CollectionStorageClassesField />
 </span>;
index b882d684f79b0d4fd8a83f1dd0137a7268f95eab..db18f76d8f6ed0de21ec9601db2a6664419720d4 100644 (file)
@@ -13,6 +13,8 @@ import { ProjectTreePickerField, CollectionTreePickerField } from "views-compone
 import { PickerIdProp } from 'store/tree-picker/picker-id';
 import { connect } from "react-redux";
 import { RootState } from "store/store";
+import { MultiCheckboxField } from "components/checkbox-field/checkbox-field";
+import { getStorageClasses } from "common/config";
 
 interface CollectionNameFieldProps {
     validate: Validator[];
@@ -55,3 +57,23 @@ export const CollectionPickerField = (props: PickerIdProp) =>
         pickerId={props.pickerId}
         component={CollectionTreePickerField}
         validate={COLLECTION_PROJECT_VALIDATION} />;
+
+interface StorageClassesProps {
+    items: string[];
+}
+
+export const CollectionStorageClassesField = connect(
+    (state: RootState) => {
+        return {
+            items: getStorageClasses(state.auth.config)
+        };
+    })(
+    (props: StorageClassesProps) =>
+        <Field
+            name='storageClassesDesired'
+            label='Storage classes'
+            minSelection={1}
+            rowLayout={true}
+            helperText='At least one class should be selected'
+            component={MultiCheckboxField}
+            items={props.items} />);
\ No newline at end of file
index 13e131b04a1265b85e13cb51c8ea71f65f357c17..88638085fa736fc891c43aafbe2072e834eed291 100644 (file)
@@ -34,6 +34,9 @@ import {
     ResourceLastModifiedDate,
     ResourceStatus
 } from 'views-components/data-explorer/renderers';
+import { getResource, ResourcesState } from 'store/resources/resources';
+import { RootState } from 'store/store';
+import { CollectionResource } from 'models/collection';
 
 type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
 
@@ -110,18 +113,29 @@ export const collectionContentAddressPanelColumns: DataColumns<string> = [
     }
 ];
 
-export interface CollectionContentAddressPanelActionProps {
-    onContextMenu: (event: React.MouseEvent<any>, uuid: string) => void;
+interface CollectionContentAddressPanelActionProps {
+    onContextMenu: (resources: ResourcesState) => (event: React.MouseEvent<any>, uuid: string) => void;
     onItemClick: (item: string) => void;
     onItemDoubleClick: (item: string) => void;
 }
 
+interface CollectionContentAddressPanelDataProps {
+    resources: ResourcesState;
+}
+
+const mapStateToProps = ({ resources }: RootState): CollectionContentAddressPanelDataProps => ({
+    resources
+})
+
 const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressPanelActionProps => ({
-    onContextMenu: (event, resourceUuid) => {
+    onContextMenu: (resources: ResourcesState) => (event, resourceUuid) => {
+        const resource = getResource<CollectionResource>(resourceUuid)(resources);
         const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
         if (kind) {
             dispatch<any>(openContextMenu(event, {
-                name: '',
+                name: resource ? resource.name : '',
+                description: resource ? resource.description : '',
+                storageClassesDesired: resource ? resource.storageClassesDesired : [],
                 uuid: resourceUuid,
                 ownerUuid: '',
                 kind: ResourceKind.NONE,
@@ -145,8 +159,8 @@ interface CollectionContentAddressDataProps {
 }
 
 export const CollectionsContentAddressPanel = withStyles(styles)(
-    connect(null, mapDispatchToProps)(
-        class extends React.Component<CollectionContentAddressPanelActionProps & CollectionContentAddressDataProps & WithStyles<CssRules>> {
+    connect(mapStateToProps, mapDispatchToProps)(
+        class extends React.Component<CollectionContentAddressPanelActionProps & CollectionContentAddressPanelDataProps & CollectionContentAddressDataProps & WithStyles<CssRules>> {
             render() {
                 return <Grid item xs={12}>
                     <Button
@@ -160,7 +174,7 @@ export const CollectionsContentAddressPanel = withStyles(styles)(
                         hideSearchInput
                         onRowClick={this.props.onItemClick}
                         onRowDoubleClick={this.props.onItemDoubleClick}
-                        onContextMenu={this.props.onContextMenu}
+                        onContextMenu={this.props.onContextMenu(this.props.resources)}
                         contextMenuColumn={true}
                         title={`Content address: ${this.props.match.params.id}`}
                         dataTableDefaultView={
index 82336b4a64e2d2b469782bcf35634122c17c26aa..404baeb9d1c274ac199dc6ced4fb557963a47e1e 100644 (file)
@@ -39,6 +39,7 @@ import { GroupContentsResource } from 'services/groups-service/groups-service';
 import { GroupClass, GroupResource } from 'models/group';
 import { getProperty } from 'store/properties/properties';
 import { PROJECT_PANEL_CURRENT_UUID } from 'store/project-panel/project-panel-action';
+import { CollectionResource } from 'models/collection';
 
 type CssRules = "toolbar" | "button";
 
@@ -151,7 +152,7 @@ export const FavoritePanel = withStyles(styles)(
 
                 const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid, readonly));
 
-                if (menuKind&& resource) {
+                if (menuKind && resource) {
                     this.props.dispatch<any>(openContextMenu(event, {
                         name: resource.name,
                         uuid: resource.uuid,
@@ -160,6 +161,7 @@ export const FavoritePanel = withStyles(styles)(
                         kind: resource.kind,
                         menuKind,
                         description: resource.description,
+                        storageClassesDesired: (resource as CollectionResource).storageClassesDesired,
                     }));
                 }
                 this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
index ed8a706a79710b0943ccb6a1025d8a6ccfb202d5..672645111743769f0dcec12644277479e43f0c8a 100644 (file)
@@ -45,6 +45,7 @@ import {
 } from 'store/resource-type-filters/resource-type-filters';
 import { GroupContentsResource } from 'services/groups-service/groups-service';
 import { GroupClass, GroupResource } from 'models/group';
+import { CollectionResource } from 'models/collection';
 
 type CssRules = 'root' | "button";
 
@@ -185,6 +186,7 @@ export const ProjectPanel = withStyles(styles)(
                         kind: resource.kind,
                         menuKind,
                         description: resource.description,
+                        storageClassesDesired: (resource as CollectionResource).storageClassesDesired,
                     }));
                 }
                 this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
index 1b7185deb1a747d661d82078925016fd88dad37a..ee09654a998ec0ea74df07be3c8bb25e28046374 100644 (file)
@@ -37,6 +37,7 @@ import { PUBLIC_FAVORITE_PANEL_ID } from 'store/public-favorites-panel/public-fa
 import { PublicFavoritesState } from 'store/public-favorites/public-favorites-reducer';
 import { getResource, ResourcesState } from 'store/resources/resources';
 import { GroupContentsResource } from 'services/groups-service/groups-service';
+import { CollectionResource } from 'models/collection';
 
 type CssRules = "toolbar" | "button";
 
@@ -134,6 +135,7 @@ const mapDispatchToProps = (dispatch: Dispatch): PublicFavoritePanelActionProps
             dispatch<any>(openContextMenu(event, {
                 name: resource.name,
                 description: resource.description,
+                storageClassesDesired: (resource as CollectionResource).storageClassesDesired,
                 uuid: resourceUuid,
                 ownerUuid: '',
                 kind: ResourceKind.NONE,