Merge branch 'master' into 13858-process-view-information-card
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Mon, 27 Aug 2018 11:52:38 +0000 (13:52 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Mon, 27 Aug 2018 11:52:38 +0000 (13:52 +0200)
refs #13858

Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>

41 files changed:
src/components/form-dialog/form-dialog.tsx
src/store/collections/collection-create-actions.ts [new file with mode: 0644]
src/store/collections/collection-move-actions.ts [moved from src/store/move-collection-dialog/move-collection-dialog.ts with 72% similarity]
src/store/collections/collection-update-actions.ts [new file with mode: 0644]
src/store/collections/collections-reducer.ts
src/store/collections/creator/collection-creator-action.ts [deleted file]
src/store/collections/creator/collection-creator-reducer.test.ts [deleted file]
src/store/collections/creator/collection-creator-reducer.ts [deleted file]
src/store/collections/updater/collection-updater-action.ts [deleted file]
src/store/collections/updater/collection-updater-reducer.ts [deleted file]
src/store/project/project-action.ts
src/store/project/project-reducer.test.ts
src/store/project/project-reducer.ts
src/store/projects/project-create-actions.ts [new file with mode: 0644]
src/store/projects/project-move-actions.ts [moved from src/store/move-project-dialog/move-project-dialog.ts with 71% similarity]
src/store/projects/project-update-actions.ts [new file with mode: 0644]
src/views-components/collection-partial-copy-dialog/collection-partial-copy-dialog.tsx
src/views-components/context-menu/action-sets/collection-action-set.ts
src/views-components/context-menu/action-sets/collection-resource-action-set.ts
src/views-components/context-menu/action-sets/project-action-set.ts
src/views-components/context-menu/action-sets/root-project-action-set.ts
src/views-components/create-collection-dialog/create-collection-dialog.tsx [deleted file]
src/views-components/create-project-dialog/create-project-dialog.tsx [deleted file]
src/views-components/dialog-copy/dialog-collection-copy.tsx [moved from src/views-components/dialog-collection-copy/dialog-collection-copy.tsx with 100% similarity]
src/views-components/dialog-create/dialog-collection-create.tsx
src/views-components/dialog-create/dialog-project-create.tsx
src/views-components/dialog-forms/copy-collection-dialog.ts
src/views-components/dialog-forms/create-collection-dialog.ts [new file with mode: 0644]
src/views-components/dialog-forms/create-project-dialog.ts [new file with mode: 0644]
src/views-components/dialog-forms/move-collection-dialog.ts [moved from src/views-components/move-collection-dialog/move-collection-dialog.ts with 63% similarity]
src/views-components/dialog-forms/move-project-dialog.ts [moved from src/views-components/move-project-dialog/move-project-dialog.ts with 59% similarity]
src/views-components/dialog-forms/update-collection-dialog.ts [new file with mode: 0644]
src/views-components/dialog-forms/update-project-dialog.ts [new file with mode: 0644]
src/views-components/dialog-move/dialog-move-to.tsx [moved from src/views-components/move-to-dialog/move-to-dialog.tsx with 88% similarity]
src/views-components/dialog-update/dialog-collection-update.tsx
src/views-components/dialog-update/dialog-project-update.tsx
src/views-components/form-fields/collection-form-fields.tsx [moved from src/views-components/form-dialog/collection-form-dialog.tsx with 93% similarity]
src/views-components/form-fields/project-form-fields.tsx [new file with mode: 0644]
src/views-components/update-collection-dialog/update-collection-dialog..tsx [deleted file]
src/views-components/update-project-dialog/update-project-dialog.tsx [deleted file]
src/views/workbench/workbench.tsx

index dee89249e231dc3b17e406572e4e8de5952b7870..150dc4b66519da12c402f851934e4df36876b7fb 100644 (file)
@@ -48,7 +48,8 @@ export const FormDialog = withStyles(styles)((props: DialogProjectProps & WithDi
         onClose={props.closeDialog}
         disableBackdropClick={props.submitting}
         disableEscapeKeyDown={props.submitting}
-        fullWidth>
+        fullWidth
+        maxWidth='sm'>
         <form>
             <DialogTitle className={props.classes.dialogTitle}>
                 {props.dialogTitle}
diff --git a/src/store/collections/collection-create-actions.ts b/src/store/collections/collection-create-actions.ts
new file mode 100644 (file)
index 0000000..d8d292c
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { reset, startSubmit, stopSubmit, initialize } from 'redux-form';
+import { RootState } from '~/store/store';
+import { uploadCollectionFiles } from '~/store/collections/uploader/collection-uploader-actions';
+import { projectPanelActions } from "~/store/project-panel/project-panel-action";
+import { snackbarActions } from "~/store/snackbar/snackbar-actions";
+import { dialogActions } from "~/store/dialog/dialog-actions";
+import { CollectionResource } from '~/models/collection';
+import { ServiceRepository } from '~/services/services';
+import { getCommonResourceServiceError, CommonResourceServiceError } from "~/common/api/common-resource-service";
+
+export interface CollectionCreateFormDialogData {
+    ownerUuid: string;
+    name: string;
+    description: string;
+    files: File[];
+}
+
+export const COLLECTION_CREATE_FORM_NAME = "collectionCreateFormName";
+
+export const openCollectionCreateDialog = (ownerUuid: string) =>
+    (dispatch: Dispatch) => {
+        dispatch(initialize(COLLECTION_CREATE_FORM_NAME, { ownerUuid }));
+        dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_CREATE_FORM_NAME, data: { ownerUuid } }));
+    };
+
+export const addCollection = (data: CollectionCreateFormDialogData) =>
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(createCollection(data));
+        dispatch(snackbarActions.OPEN_SNACKBAR({
+            message: "Collection has been successfully created.",
+            hideDuration: 2000
+        }));
+    };
+
+export const createCollection = (collection: Partial<CollectionResource>) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(startSubmit(COLLECTION_CREATE_FORM_NAME));
+        try {
+            const newCollection = await services.collectionService.create(collection);
+            await dispatch<any>(uploadCollectionFiles(newCollection.uuid));
+            dispatch(projectPanelActions.REQUEST_ITEMS());
+            dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_CREATE_FORM_NAME }));
+            dispatch(reset(COLLECTION_CREATE_FORM_NAME));
+        } catch (e) {
+            const error = getCommonResourceServiceError(e);
+            if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
+                dispatch(stopSubmit(COLLECTION_CREATE_FORM_NAME, { name: 'Collection with the same name already exists.' }));
+            }
+        }
+    };
\ No newline at end of file
similarity index 72%
rename from src/store/move-collection-dialog/move-collection-dialog.ts
rename to src/store/collections/collection-move-actions.ts
index 5950679956e7ac3b7e4655c09ff97e321580489e..6fc836f80e123064bd6f99d35a5ed3fe47291f86 100644 (file)
@@ -10,33 +10,33 @@ import { RootState } from '~/store/store';
 import { getCommonResourceServiceError, CommonResourceServiceError } from "~/common/api/common-resource-service";
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 import { projectPanelActions } from '~/store/project-panel/project-panel-action';
-import { MoveToFormDialogData } from '../move-to-dialog/move-to-dialog';
+import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 import { resetPickerProjectTree } from '~/store/project-tree-picker/project-tree-picker-actions';
 
-export const MOVE_COLLECTION_DIALOG = 'moveCollectionDialog';
+export const COLLECTION_MOVE_FORM_NAME = 'collectionMoveFormName';
 
 export const openMoveCollectionDialog = (resource: { name: string, uuid: string }) =>
     (dispatch: Dispatch) => {
         dispatch<any>(resetPickerProjectTree());
-        dispatch(initialize(MOVE_COLLECTION_DIALOG, resource));
-        dispatch(dialogActions.OPEN_DIALOG({ id: MOVE_COLLECTION_DIALOG, data: {} }));
+        dispatch(initialize(COLLECTION_MOVE_FORM_NAME, resource));
+        dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_MOVE_FORM_NAME, data: {} }));
     };
 
 export const moveCollection = (resource: MoveToFormDialogData) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch(startSubmit(MOVE_COLLECTION_DIALOG));
+        dispatch(startSubmit(COLLECTION_MOVE_FORM_NAME));
         try {
             const collection = await services.collectionService.get(resource.uuid);
             await services.collectionService.update(resource.uuid, { ...collection, ownerUuid: resource.ownerUuid });
             dispatch(projectPanelActions.REQUEST_ITEMS());
-            dispatch(dialogActions.CLOSE_DIALOG({ id: MOVE_COLLECTION_DIALOG }));
+            dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_MOVE_FORM_NAME }));
             dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Collection has been moved', hideDuration: 2000 }));
         } catch (e) {
             const error = getCommonResourceServiceError(e);
             if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
-                dispatch(stopSubmit(MOVE_COLLECTION_DIALOG, { ownerUuid: 'A collection with the same name already exists in the target project.' }));
+                dispatch(stopSubmit(COLLECTION_MOVE_FORM_NAME, { ownerUuid: 'A collection with the same name already exists in the target project.' }));
             } else {
-                dispatch(dialogActions.CLOSE_DIALOG({ id: MOVE_COLLECTION_DIALOG }));
+                dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_MOVE_FORM_NAME }));
                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not move the collection.', hideDuration: 2000 }));
             }
         }
diff --git a/src/store/collections/collection-update-actions.ts b/src/store/collections/collection-update-actions.ts
new file mode 100644 (file)
index 0000000..27c3bfc
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { initialize, startSubmit, stopSubmit } from 'redux-form';
+import { RootState } from "~/store/store";
+import { collectionPanelActions } from "~/store/collection-panel/collection-panel-action";
+import { updateDetails } from "~/store/details-panel/details-panel-action";
+import { dialogActions } from "~/store/dialog/dialog-actions";
+import { dataExplorerActions } from "~/store/data-explorer/data-explorer-action";
+import { snackbarActions } from "~/store/snackbar/snackbar-actions";
+import { ContextMenuResource } from '~/store/context-menu/context-menu-reducer';
+import { PROJECT_PANEL_ID } from "~/views/project-panel/project-panel";
+import { getCommonResourceServiceError, CommonResourceServiceError } from "~/common/api/common-resource-service";
+import { ServiceRepository } from "~/services/services";
+import { CollectionResource } from '~/models/collection';
+
+export interface CollectionUpdateFormDialogData {
+    uuid: string;
+    name: string;
+    description: string;
+}
+
+export const COLLECTION_UPDATE_FORM_NAME = 'collectionUpdateFormName';
+
+export const openCollectionUpdateDialog = (resource: ContextMenuResource) =>
+    (dispatch: Dispatch) => {
+        dispatch(initialize(COLLECTION_UPDATE_FORM_NAME, resource));
+        dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_UPDATE_FORM_NAME, data: {} }));
+    };
+
+export const editCollection = (data: CollectionUpdateFormDialogData) =>
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(updateCollection(data));
+        dispatch(snackbarActions.OPEN_SNACKBAR({
+            message: "Collection has been successfully updated.",
+            hideDuration: 2000
+        }));
+    };
+
+export const updateCollection = (collection: Partial<CollectionResource>) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const uuid = collection.uuid || '';
+        dispatch(startSubmit(COLLECTION_UPDATE_FORM_NAME));
+        try {
+            const updatedCollection = await services.collectionService.update(uuid, collection);
+            dispatch(collectionPanelActions.LOAD_COLLECTION_SUCCESS({ item: updatedCollection as CollectionResource }));
+            dispatch<any>(updateDetails(updatedCollection));
+            dispatch(dataExplorerActions.REQUEST_ITEMS({ id: PROJECT_PANEL_ID }));
+            dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_UPDATE_FORM_NAME }));
+        } catch(e) {
+            const error = getCommonResourceServiceError(e);
+            if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
+                dispatch(stopSubmit(COLLECTION_UPDATE_FORM_NAME, { name: 'Collection with the same name already exists.' }));
+            }
+        }
+    };
\ No newline at end of file
index b2ee4550662a99b4531bb6071d42ab80907f93e1..c7601505cbd270c587a1ae9ec3f0e04ad354980a 100644 (file)
@@ -3,18 +3,12 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { combineReducers } from 'redux';
-import { collectionCreatorReducer, CollectionCreatorState } from "./creator/collection-creator-reducer";
-import { collectionUpdaterReducer, CollectionUpdaterState } from "./updater/collection-updater-reducer";
 import { collectionUploaderReducer, CollectionUploaderState } from "./uploader/collection-uploader-reducer";
 
 export type CollectionsState = {
-    creator: CollectionCreatorState;
-    updater: CollectionUpdaterState;
     uploader: CollectionUploaderState
 };
 
 export const collectionsReducer = combineReducers({
-    creator: collectionCreatorReducer,
-    updater: collectionUpdaterReducer,
     uploader: collectionUploaderReducer
 });
diff --git a/src/store/collections/creator/collection-creator-action.ts b/src/store/collections/creator/collection-creator-action.ts
deleted file mode 100644 (file)
index 8c35ffa..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { default as unionize, ofType, UnionOf } from "unionize";
-import { Dispatch } from "redux";
-
-import { RootState } from "../../store";
-import { CollectionResource } from '~/models/collection';
-import { ServiceRepository } from "~/services/services";
-import { uploadCollectionFiles } from '../uploader/collection-uploader-actions';
-import { reset } from "redux-form";
-
-export const collectionCreateActions = unionize({
-    OPEN_COLLECTION_CREATOR: ofType<{ ownerUuid: string }>(),
-    CLOSE_COLLECTION_CREATOR: ofType<{}>(),
-    CREATE_COLLECTION: ofType<{}>(),
-    CREATE_COLLECTION_SUCCESS: ofType<{}>(),
-}, {
-        tag: 'type',
-        value: 'payload'
-    });
-
-export type CollectionCreateAction = UnionOf<typeof collectionCreateActions>;
-
-export const createCollection = (collection: Partial<CollectionResource>, files: File[]) =>
-    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const { ownerUuid } = getState().collections.creator;
-        const collectiontData = { ownerUuid, ...collection };
-        dispatch(collectionCreateActions.CREATE_COLLECTION(collectiontData));
-        const newCollection = await services.collectionService.create(collectiontData);
-        await dispatch<any>(uploadCollectionFiles(newCollection.uuid));
-        dispatch(collectionCreateActions.CREATE_COLLECTION_SUCCESS(collection));
-        dispatch(reset('collectionCreateDialog'));
-        return newCollection;
-    };
-
diff --git a/src/store/collections/creator/collection-creator-reducer.test.ts b/src/store/collections/creator/collection-creator-reducer.test.ts
deleted file mode 100644 (file)
index 5aa9dcf..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { collectionCreatorReducer } from "./collection-creator-reducer";
-import { collectionCreateActions } from "./collection-creator-action";
-
-describe('collection-reducer', () => {
-
-    it('should open collection creator dialog', () => {
-        const initialState = { opened: false, ownerUuid: "" };
-        const collection = { opened: true, ownerUuid: "" };
-
-        const state = collectionCreatorReducer(initialState, collectionCreateActions.OPEN_COLLECTION_CREATOR(initialState));
-        expect(state).toEqual(collection);
-    });
-
-    it('should close collection creator dialog', () => {
-        const initialState = { opened: true, ownerUuid: "" };
-        const collection = { opened: false, ownerUuid: "" };
-
-        const state = collectionCreatorReducer(initialState, collectionCreateActions.CLOSE_COLLECTION_CREATOR());
-        expect(state).toEqual(collection);
-    });
-
-    it('should reset collection creator dialog props', () => {
-        const initialState = { opened: true, ownerUuid: "test" };
-        const collection = { opened: false, ownerUuid: "" };
-
-        const state = collectionCreatorReducer(initialState, collectionCreateActions.CREATE_COLLECTION_SUCCESS());
-        expect(state).toEqual(collection);
-    });
-});
diff --git a/src/store/collections/creator/collection-creator-reducer.ts b/src/store/collections/creator/collection-creator-reducer.ts
deleted file mode 100644 (file)
index 72c999d..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { collectionCreateActions, CollectionCreateAction } from './collection-creator-action';
-
-export interface CollectionCreatorState {
-    opened: boolean;
-    ownerUuid: string;
-}
-
-const updateCreator = (state: CollectionCreatorState, creator?: Partial<CollectionCreatorState>) => ({
-    ...state,
-    ...creator
-});
-
-const initialState: CollectionCreatorState = {
-    opened: false,
-    ownerUuid: ''
-};
-
-export const collectionCreatorReducer = (state: CollectionCreatorState = initialState, action: CollectionCreateAction) => {
-    return collectionCreateActions.match(action, {
-        OPEN_COLLECTION_CREATOR: ({ ownerUuid }) => updateCreator(state, { ownerUuid, opened: true }),
-        CLOSE_COLLECTION_CREATOR: () => updateCreator(state, { opened: false }),
-        CREATE_COLLECTION: () => updateCreator(state),
-        CREATE_COLLECTION_SUCCESS: () => updateCreator(state, { opened: false, ownerUuid: "" }),
-        default: () => state
-    });
-};
diff --git a/src/store/collections/updater/collection-updater-action.ts b/src/store/collections/updater/collection-updater-action.ts
deleted file mode 100644 (file)
index 2f520d4..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { default as unionize, ofType, UnionOf } from "unionize";
-import { Dispatch } from "redux";
-
-import { RootState } from "../../store";
-import { ServiceRepository } from "~/services/services";
-import { CollectionResource } from '~/models/collection';
-import { initialize } from 'redux-form';
-import { collectionPanelActions } from "../../collection-panel/collection-panel-action";
-import { ContextMenuResource } from "../../context-menu/context-menu-reducer";
-import { updateDetails } from "~/store/details-panel/details-panel-action";
-
-export const collectionUpdaterActions = unionize({
-    OPEN_COLLECTION_UPDATER: ofType<{ uuid: string }>(),
-    CLOSE_COLLECTION_UPDATER: ofType<{}>(),
-    UPDATE_COLLECTION_SUCCESS: ofType<{}>(),
-}, {
-    tag: 'type',
-    value: 'payload'
-});
-
-
-export const COLLECTION_FORM_NAME = 'collectionEditDialog';
-
-export const openUpdater = (item: ContextMenuResource) =>
-    (dispatch: Dispatch, getState: () => RootState) => {
-        if (item) {
-            dispatch(collectionUpdaterActions.OPEN_COLLECTION_UPDATER({ uuid: item.uuid }));
-            dispatch(initialize(COLLECTION_FORM_NAME, { name: item.name, description: item.description }));
-        }
-    };
-
-export const updateCollection = (collection: Partial<CollectionResource>) =>
-    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const { uuid } = getState().collections.updater;
-        return services.collectionService
-            .update(uuid, collection)
-            .then(collection => {
-                    dispatch(collectionPanelActions.LOAD_COLLECTION_SUCCESS({ item: collection as CollectionResource }));
-                    dispatch(collectionUpdaterActions.UPDATE_COLLECTION_SUCCESS());
-                    dispatch<any>(updateDetails(collection));
-                }
-            );
-    };
-
-export type CollectionUpdaterAction = UnionOf<typeof collectionUpdaterActions>;
diff --git a/src/store/collections/updater/collection-updater-reducer.ts b/src/store/collections/updater/collection-updater-reducer.ts
deleted file mode 100644 (file)
index 97d010f..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { collectionUpdaterActions, CollectionUpdaterAction } from './collection-updater-action';
-
-export interface CollectionUpdaterState {
-    opened: boolean;
-    uuid: string;
-}
-
-const updateCollection = (state: CollectionUpdaterState, updater?: Partial<CollectionUpdaterState>) => ({
-    ...state,
-    ...updater
-});
-
-const initialState: CollectionUpdaterState = {
-    opened: false,
-    uuid: ''
-};
-
-export const collectionUpdaterReducer = (state: CollectionUpdaterState = initialState, action: CollectionUpdaterAction) => {
-    return collectionUpdaterActions.match(action, {
-        OPEN_COLLECTION_UPDATER: ({ uuid }) => updateCollection(state, { uuid, opened: true }),
-        CLOSE_COLLECTION_UPDATER: () => updateCollection(state, { opened: false, uuid: "" }),
-        UPDATE_COLLECTION_SUCCESS: () => updateCollection(state, { opened: false, uuid: "" }),
-        default: () => state
-    });
-};
index 2017658916cbfe7f5bc408c5eaaea4d547e6cc20..ff66a9cf8ac035b9628bba060ac5cb30ef0878c1 100644 (file)
@@ -9,17 +9,8 @@ import { FilterBuilder } from "~/common/api/filter-builder";
 import { RootState } from "../store";
 import { checkPresenceInFavorites } from "../favorites/favorites-actions";
 import { ServiceRepository } from "~/services/services";
-import { projectPanelActions } from "~/store/project-panel/project-panel-action";
-import { updateDetails } from "~/store/details-panel/details-panel-action";
 
 export const projectActions = unionize({
-    OPEN_PROJECT_CREATOR: ofType<{ ownerUuid: string }>(),
-    CLOSE_PROJECT_CREATOR: ofType<{}>(),
-    CREATE_PROJECT: ofType<Partial<ProjectResource>>(),
-    CREATE_PROJECT_SUCCESS: ofType<ProjectResource>(),
-    OPEN_PROJECT_UPDATER: ofType<{ uuid: string}>(),
-    CLOSE_PROJECT_UPDATER: ofType<{}>(),
-    UPDATE_PROJECT_SUCCESS: ofType<ProjectResource>(),
     REMOVE_PROJECT: ofType<string>(),
     PROJECTS_REQUEST: ofType<string>(),
     PROJECTS_SUCCESS: ofType<{ projects: ProjectResource[], parentItemId?: string }>(),
@@ -31,8 +22,6 @@ export const projectActions = unionize({
     value: 'payload'
 });
 
-export const PROJECT_FORM_NAME = 'projectEditDialog';
-
 export const getProjectList = (parentUuid: string = '') => 
     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         dispatch(projectActions.PROJECTS_REQUEST(parentUuid));
@@ -47,27 +36,4 @@ export const getProjectList = (parentUuid: string = '') =>
         });
     };
 
-export const createProject = (project: Partial<ProjectResource>) =>
-    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const { ownerUuid } = getState().projects.creator;
-        const projectData = { ownerUuid, ...project };
-        dispatch(projectActions.CREATE_PROJECT(projectData));
-        return services.projectService
-            .create(projectData)
-            .then(project => dispatch(projectActions.CREATE_PROJECT_SUCCESS(project)));
-    };
-
-export const updateProject = (project: Partial<ProjectResource>) =>
-    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const { uuid } = getState().projects.updater;
-        return services.projectService
-            .update(uuid, project)
-            .then(project => {
-                dispatch(projectActions.UPDATE_PROJECT_SUCCESS(project));
-                dispatch(projectPanelActions.REQUEST_ITEMS());
-                dispatch<any>(getProjectList(project.ownerUuid));
-                dispatch<any>(updateDetails(project));
-            });
-    };
-
 export type ProjectAction = UnionOf<typeof projectActions>;
index 8cd3121eecb871c8d9fa538bcfdb6983cf1322f6..56a62534655bd791cb05c459d74047683b8c40f3 100644 (file)
@@ -31,15 +31,7 @@ describe('project-reducer', () => {
                 status: TreeItemStatus.INITIAL
             }
             ],
-            currentItemId: "",
-            creator: {
-                opened: false,
-                ownerUuid: "",
-            },
-            updater: {
-                opened: false,
-                uuid: ''
-            }
+            currentItemId: ""
         });
     });
 
@@ -52,9 +44,7 @@ describe('project-reducer', () => {
                 active: true,
                 status: TreeItemStatus.PENDING
             }],
-            currentItemId: "1",
-            creator: { opened: false, ownerUuid: "" },
-            updater: { opened: false, uuid: '' }
+            currentItemId: "1"
         };
         const project = {
             items: [{
@@ -64,9 +54,7 @@ describe('project-reducer', () => {
                 active: false,
                 status: TreeItemStatus.PENDING
             }],
-            currentItemId: "",
-            creator: { opened: false, ownerUuid: "" },
-            updater: { opened: false, uuid: '' }
+            currentItemId: ""
         };
 
         const state = projectsReducer(initialState, projectActions.RESET_PROJECT_TREE_ACTIVITY(initialState.items[0].id));
@@ -82,9 +70,7 @@ describe('project-reducer', () => {
                 active: false,
                 status: TreeItemStatus.PENDING
             }],
-            currentItemId: "1",
-            creator: { opened: false, ownerUuid: "" },
-            updater: { opened: false, uuid: '' }
+            currentItemId: "1"
         };
         const project = {
             items: [{
@@ -94,9 +80,7 @@ describe('project-reducer', () => {
                 active: true,
                 status: TreeItemStatus.PENDING,
             }],
-            currentItemId: "1",
-            creator: { opened: false, ownerUuid: "" },
-            updater: { opened: false, uuid: '' }
+            currentItemId: "1"
         };
 
         const state = projectsReducer(initialState, projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(initialState.items[0].id));
@@ -113,9 +97,7 @@ describe('project-reducer', () => {
                 active: false,
                 status: TreeItemStatus.PENDING,
             }],
-            currentItemId: "1",
-            creator: { opened: false, ownerUuid: "" },
-            updater: { opened: false, uuid: '' }
+            currentItemId: "1"
         };
         const project = {
             items: [{
@@ -125,10 +107,7 @@ describe('project-reducer', () => {
                 active: false,
                 status: TreeItemStatus.PENDING,
             }],
-            currentItemId: "1",
-            creator: { opened: false, ownerUuid: "" },
-            updater: { opened: false, uuid: '' }
-
+            currentItemId: "1"
         };
 
         const state = projectsReducer(initialState, projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(initialState.items[0].id));
index bb0748657ee1e64b3d28b5e8bc923fc062ca6ea9..452f6be3e7be1a59eee7b42cda10108437e04327 100644 (file)
@@ -10,17 +10,9 @@ import { ProjectResource } from "~/models/project";
 
 export type ProjectState = {
     items: Array<TreeItem<ProjectResource>>,
-    currentItemId: string,
-    creator: ProjectCreator,
-    updater: ProjectUpdater
+    currentItemId: string
 };
 
-interface ProjectCreator {
-    opened: boolean;
-    ownerUuid: string;
-    error?: string;
-}
-
 interface ProjectUpdater {
     opened: boolean;
     uuid: string;
@@ -98,45 +90,14 @@ function updateProjectTree(tree: Array<TreeItem<ProjectResource>>, projects: Pro
     return items;
 }
 
-const updateCreator = (state: ProjectState, creator: Partial<ProjectCreator>) => ({
-    ...state,
-    creator: {
-        ...state.creator,
-        ...creator
-    }
-});
-
-const updateProject = (state: ProjectState, updater?: Partial<ProjectUpdater>) => ({
-    ...state,
-    updater: {
-        ...state.updater,
-        ...updater
-    }
-});
-
 const initialState: ProjectState = {
     items: [],
-    currentItemId: "",
-    creator: {
-        opened: false,
-        ownerUuid: ""
-    },
-    updater: {
-        opened: false,
-        uuid: ''
-    }
+    currentItemId: ""
 };
 
 
 export const projectsReducer = (state: ProjectState = initialState, action: ProjectAction) => {
     return projectActions.match(action, {
-        OPEN_PROJECT_CREATOR: ({ ownerUuid }) => updateCreator(state, { ownerUuid, opened: true }),
-        CLOSE_PROJECT_CREATOR: () => updateCreator(state, { opened: false }),
-        CREATE_PROJECT: () => updateCreator(state, { error: undefined }),
-        CREATE_PROJECT_SUCCESS: () => updateCreator(state, { opened: false, ownerUuid: "" }),
-        OPEN_PROJECT_UPDATER: ({ uuid }) => updateProject(state, { uuid, opened: true }),
-        CLOSE_PROJECT_UPDATER: () => updateProject(state, { opened: false, uuid: "" }),
-        UPDATE_PROJECT_SUCCESS: () => updateProject(state, { opened: false, uuid: "" }),
         REMOVE_PROJECT: () => state,
         PROJECTS_REQUEST: itemId => {
             const items = _.cloneDeep(state.items);
diff --git a/src/store/projects/project-create-actions.ts b/src/store/projects/project-create-actions.ts
new file mode 100644 (file)
index 0000000..d1bc827
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { reset, startSubmit, stopSubmit, initialize } from 'redux-form';
+import { RootState } from '~/store/store';
+import { snackbarActions } from '~/store/snackbar/snackbar-actions';
+import { dialogActions } from "~/store/dialog/dialog-actions";
+import { projectPanelActions } from '~/store/project-panel/project-panel-action';
+import { getProjectList } from '~/store/project/project-action';
+import { getCommonResourceServiceError, CommonResourceServiceError } from '~/common/api/common-resource-service';
+import { ProjectResource } from '~/models/project';
+import { ServiceRepository } from '~/services/services';
+
+
+export interface ProjectCreateFormDialogData {
+    ownerUuid: string;
+    name: string;
+    description: string;
+}
+
+export const PROJECT_CREATE_FORM_NAME = 'projectCreateFormName';
+
+export const openProjectCreateDialog = (ownerUuid: string) =>
+    (dispatch: Dispatch) => {
+        dispatch(initialize(PROJECT_CREATE_FORM_NAME, { ownerUuid }));
+        dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_CREATE_FORM_NAME, data: {} }));
+    };
+
+export const addProject = (data: ProjectCreateFormDialogData) =>
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(createProject(data));
+        dispatch(snackbarActions.OPEN_SNACKBAR({
+            message: "Project has been successfully created.",
+            hideDuration: 2000
+        }));
+    };
+
+
+const createProject = (project: Partial<ProjectResource>) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(startSubmit(PROJECT_CREATE_FORM_NAME));
+        try {
+            const newProject = await services.projectService.create(project);
+            dispatch<any>(getProjectList(newProject.ownerUuid));
+            dispatch(projectPanelActions.REQUEST_ITEMS());
+            dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_CREATE_FORM_NAME }));
+            dispatch(reset(PROJECT_CREATE_FORM_NAME));
+        } catch (e) {
+            const error = getCommonResourceServiceError(e);
+            if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
+                dispatch(stopSubmit(PROJECT_CREATE_FORM_NAME, { name: 'Project with the same name already exists.' }));
+            }
+        }
+    };
\ No newline at end of file
similarity index 71%
rename from src/store/move-project-dialog/move-project-dialog.ts
rename to src/store/projects/project-move-actions.ts
index 9e8e6f9b1ac1905406e7d1987df695ea73760ead..52d17ac814c43904de5f2b4347b3ad5b224ee961 100644 (file)
@@ -11,22 +11,22 @@ import { getCommonResourceServiceError, CommonResourceServiceError } from "~/com
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 import { projectPanelActions } from '~/store/project-panel/project-panel-action';
 import { getProjectList } from '~/store/project/project-action';
-import { MoveToFormDialogData } from '../move-to-dialog/move-to-dialog';
+import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 import { resetPickerProjectTree } from '~/store/project-tree-picker/project-tree-picker-actions';
-import { findTreeItem } from '../project/project-reducer';
+import { findTreeItem } from '~/store/project/project-reducer';
 
-export const MOVE_PROJECT_DIALOG = 'moveProjectDialog';
+export const PROJECT_MOVE_FORM_NAME = 'projectMoveFormName';
 
 export const openMoveProjectDialog = (resource: { name: string, uuid: string }) =>
     (dispatch: Dispatch) => {
         dispatch<any>(resetPickerProjectTree());
-        dispatch(initialize(MOVE_PROJECT_DIALOG, resource));
-        dispatch(dialogActions.OPEN_DIALOG({ id: MOVE_PROJECT_DIALOG, data: {} }));
+        dispatch(initialize(PROJECT_MOVE_FORM_NAME, resource));
+        dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_MOVE_FORM_NAME, data: {} }));
     };
 
 export const moveProject = (resource: MoveToFormDialogData) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch(startSubmit(MOVE_PROJECT_DIALOG));
+        dispatch(startSubmit(PROJECT_MOVE_FORM_NAME));
         try {
             const project = await services.projectService.get(resource.uuid);
             await services.projectService.update(resource.uuid, { ...project, ownerUuid: resource.ownerUuid });
@@ -36,16 +36,16 @@ export const moveProject = (resource: MoveToFormDialogData) =>
             if (findTreeItem(projects.items, resource.ownerUuid)) {
                 dispatch<any>(getProjectList(resource.ownerUuid));
             }
-            dispatch(dialogActions.CLOSE_DIALOG({ id: MOVE_PROJECT_DIALOG }));
+            dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_MOVE_FORM_NAME }));
             dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Project has been moved', hideDuration: 2000 }));
         } catch (e) {
             const error = getCommonResourceServiceError(e);
             if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
-                dispatch(stopSubmit(MOVE_PROJECT_DIALOG, { ownerUuid: 'A project with the same name already exists in the target project.' }));
+                dispatch(stopSubmit(PROJECT_MOVE_FORM_NAME, { ownerUuid: 'A project with the same name already exists in the target project.' }));
             } else if (error === CommonResourceServiceError.OWNERSHIP_CYCLE) {
-                dispatch(stopSubmit(MOVE_PROJECT_DIALOG, { ownerUuid: 'Cannot move a project into itself.' }));
+                dispatch(stopSubmit(PROJECT_MOVE_FORM_NAME, { ownerUuid: 'Cannot move a project into itself.' }));
             } else {
-                dispatch(dialogActions.CLOSE_DIALOG({ id: MOVE_PROJECT_DIALOG }));
+                dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_MOVE_FORM_NAME }));
                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not move the project.', hideDuration: 2000 }));
             }
         }
diff --git a/src/store/projects/project-update-actions.ts b/src/store/projects/project-update-actions.ts
new file mode 100644 (file)
index 0000000..e674d29
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { initialize, startSubmit, stopSubmit } from 'redux-form';
+import { RootState } from "~/store/store";
+import { updateDetails } from "~/store/details-panel/details-panel-action";
+import { dialogActions } from "~/store/dialog/dialog-actions";
+import { snackbarActions } from "~/store/snackbar/snackbar-actions";
+import { ContextMenuResource } from '~/store/context-menu/context-menu-reducer';
+import { getCommonResourceServiceError, CommonResourceServiceError } from "~/common/api/common-resource-service";
+import { ServiceRepository } from "~/services/services";
+import { ProjectResource } from '~/models/project';
+import { getProjectList } from '~/store/project/project-action';
+import { projectPanelActions } from '~/store/project-panel/project-panel-action';
+
+export interface ProjectUpdateFormDialogData {
+    uuid: string;
+    name: string;
+    description: string;
+}
+
+export const PROJECT_UPDATE_FORM_NAME = 'projectUpdateFormName';
+
+export const openProjectUpdateDialog = (resource: ContextMenuResource) =>
+    (dispatch: Dispatch) => {
+        dispatch(initialize(PROJECT_UPDATE_FORM_NAME, resource));
+        dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {} }));
+    };
+
+export const editProject = (data: ProjectUpdateFormDialogData) =>
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(updateProject(data));
+        dispatch(snackbarActions.OPEN_SNACKBAR({
+            message: "Project has been successfully updated.",
+            hideDuration: 2000
+        }));
+    };
+
+export const updateProject = (project: Partial<ProjectResource>) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const uuid = project.uuid || '';
+        dispatch(startSubmit(PROJECT_UPDATE_FORM_NAME));
+        try {
+            const updatedProject = await services.projectService.update(uuid, project);
+            dispatch(projectPanelActions.REQUEST_ITEMS());
+            dispatch<any>(getProjectList(updatedProject.ownerUuid));
+            dispatch<any>(updateDetails(updatedProject));
+            dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_UPDATE_FORM_NAME }));
+        } catch (e) {
+            const error = getCommonResourceServiceError(e);
+            if (error === CommonResourceServiceError.UNIQUE_VIOLATION) {
+                dispatch(stopSubmit(PROJECT_UPDATE_FORM_NAME, { name: 'Project with the same name already exists.' }));
+            }
+        }
+    };
\ No newline at end of file
index e230470b418e065b0c01956d340fbbcd07b85907..86fc360ef8d1626f60937730e2d577173cca726d 100644 (file)
@@ -6,9 +6,9 @@ import * as React from "react";
 import { compose } from "redux";
 import { reduxForm, InjectedFormProps } from 'redux-form';
 import { withDialog, WithDialogProps } from '~/store/dialog/with-dialog';
-import { CollectionPartialCopyFields } from '../form-dialog/collection-form-dialog';
-import { FormDialog } from '~/components/form-dialog/form-dialog';
 import { COLLECTION_PARTIAL_COPY, doCollectionPartialCopy, CollectionPartialCopyFormData } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
+import { CollectionPartialCopyFields } from '~/views-components/form-fields/collection-form-fields';
+import { FormDialog } from '~/components/form-dialog/form-dialog';
 
 export const CollectionPartialCopyDialog = compose(
     withDialog(COLLECTION_PARTIAL_COPY),
index 2d3cd55935e386f65fd3d134fd9f278b88187ef3..b3fdc3fbab642561d823cddd003a70b9ab288149 100644 (file)
@@ -6,9 +6,9 @@ import { ContextMenuActionSet } from "../context-menu-action-set";
 import { ToggleFavoriteAction } from "../actions/favorite-action";
 import { toggleFavorite } from "~/store/favorites/favorites-actions";
 import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, ProvenanceGraphIcon, AdvancedIcon, RemoveIcon } from "~/components/icon/icon";
-import { openUpdater } from "~/store/collections/updater/collection-updater-action";
+import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
 import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
-import { openMoveCollectionDialog } from '~/store/move-collection-dialog/move-collection-dialog';
+import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
 import { openCollectionCopyDialog } from "~/store/collections/collection-copy-actions";
 
 export const collectionActionSet: ContextMenuActionSet = [[
@@ -16,7 +16,7 @@ export const collectionActionSet: ContextMenuActionSet = [[
         icon: RenameIcon,
         name: "Edit collection",
         execute: (dispatch, resource) => {
-            dispatch<any>(openUpdater(resource));
+            dispatch<any>(openCollectionUpdateDialog(resource));
         }
     },
     {
index fbabeee312931939b50e1d2111b8e78c30f00252..a299b9370ca93f39a437338b486ab639935b323e 100644 (file)
@@ -6,9 +6,9 @@ import { ContextMenuActionSet } from "../context-menu-action-set";
 import { ToggleFavoriteAction } from "../actions/favorite-action";
 import { toggleFavorite } from "~/store/favorites/favorites-actions";
 import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, RemoveIcon } from "~/components/icon/icon";
-import { openUpdater } from "~/store/collections/updater/collection-updater-action";
+import { openCollectionUpdateDialog } from "~/store/collections/collection-update-actions";
 import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
-import { openMoveCollectionDialog } from '~/store/move-collection-dialog/move-collection-dialog';
+import { openMoveCollectionDialog } from '~/store/collections/collection-move-actions';
 import { openCollectionCopyDialog } from '~/store/collections/collection-copy-actions';
 
 export const collectionResourceActionSet: ContextMenuActionSet = [[
@@ -16,7 +16,7 @@ export const collectionResourceActionSet: ContextMenuActionSet = [[
         icon: RenameIcon,
         name: "Edit collection",
         execute: (dispatch, resource) => {
-            dispatch<any>(openUpdater(resource));
+            dispatch<any>(openCollectionUpdateDialog(resource));
         }
     },
     {
index 8ae60c8f5882c4c81404798a7d7a674a70772578..bc5d739ab342a5e501f54fa41e97d32889b04c9d 100644 (file)
@@ -5,29 +5,28 @@
 import { reset, initialize } from "redux-form";
 
 import { ContextMenuActionSet } from "../context-menu-action-set";
-import { projectActions, PROJECT_FORM_NAME } from "~/store/project/project-action";
 import { NewProjectIcon, RenameIcon, CopyIcon, MoveToIcon } from "~/components/icon/icon";
 import { ToggleFavoriteAction } from "../actions/favorite-action";
 import { toggleFavorite } from "~/store/favorites/favorites-actions";
 import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
-import { PROJECT_CREATE_DIALOG } from "../../dialog-create/dialog-project-create";
-import { openMoveProjectDialog } from '~/store/move-project-dialog/move-project-dialog';
+import { openMoveProjectDialog } from '~/store/projects/project-move-actions';
+import { PROJECT_CREATE_FORM_NAME, openProjectCreateDialog } from '~/store/projects/project-create-actions';
+import { openProjectUpdateDialog } from '~/store/projects/project-update-actions';
 
 export const projectActionSet: ContextMenuActionSet = [[
     {
         icon: NewProjectIcon,
         name: "New project",
         execute: (dispatch, resource) => {
-            dispatch(reset(PROJECT_CREATE_DIALOG));
-            dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: resource.uuid }));
+            dispatch(reset(PROJECT_CREATE_FORM_NAME));
+            dispatch<any>(openProjectCreateDialog(resource.uuid));
         }
     },
     {
         icon: RenameIcon,
         name: "Edit project",
         execute: (dispatch, resource) => {
-            dispatch(projectActions.OPEN_PROJECT_UPDATER({ uuid: resource.uuid }));
-            dispatch(initialize(PROJECT_FORM_NAME, { name: resource.name, description: resource.description }));
+            dispatch<any>(openProjectUpdateDialog(resource));
         }
     },
     {
index de3b954f4c1c819c5d9aab4a136cd0e03a377a6a..2c71abc41ed6b88cd0fa5094ccf807e38e148c13 100644 (file)
@@ -6,26 +6,25 @@ import { reset } from "redux-form";
 
 import { ContextMenuActionSet } from "../context-menu-action-set";
 import { projectActions } from "~/store/project/project-action";
-import { collectionCreateActions } from "~/store/collections/creator/collection-creator-action";
-import { PROJECT_CREATE_DIALOG } from "../../dialog-create/dialog-project-create";
-import { COLLECTION_CREATE_DIALOG } from "../../dialog-create/dialog-collection-create";
+import { COLLECTION_CREATE_FORM_NAME, openCollectionCreateDialog } from '~/store/collections/collection-create-actions';
 import { NewProjectIcon, CollectionIcon } from "~/components/icon/icon";
+import { PROJECT_CREATE_FORM_NAME, openProjectCreateDialog } from '~/store/projects/project-create-actions';
 
 export const rootProjectActionSet: ContextMenuActionSet =  [[
     {
         icon: NewProjectIcon,
         name: "New project",
         execute: (dispatch, resource) => {
-            dispatch(reset(PROJECT_CREATE_DIALOG));
-            dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: resource.uuid }));
+            dispatch(reset(PROJECT_CREATE_FORM_NAME));
+            dispatch<any>(openProjectCreateDialog(resource.uuid));
         }
     },
     {
         icon: CollectionIcon,
         name: "New Collection",
         execute: (dispatch, resource) => {
-            dispatch(reset(COLLECTION_CREATE_DIALOG));
-            dispatch(collectionCreateActions.OPEN_COLLECTION_CREATOR({ ownerUuid: resource.uuid }));
+            dispatch(reset(COLLECTION_CREATE_FORM_NAME));
+            dispatch<any>(openCollectionCreateDialog(resource.uuid));
         }
     }
 ]];
diff --git a/src/views-components/create-collection-dialog/create-collection-dialog.tsx b/src/views-components/create-collection-dialog/create-collection-dialog.tsx
deleted file mode 100644 (file)
index 94eb82f..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { connect } from "react-redux";
-import { Dispatch } from "redux";
-import { SubmissionError } from "redux-form";
-
-import { RootState } from "~/store/store";
-import { DialogCollectionCreate } from "../dialog-create/dialog-collection-create";
-import { collectionCreateActions, createCollection } from "~/store/collections/creator/collection-creator-action";
-import { snackbarActions } from "~/store/snackbar/snackbar-actions";
-import { UploadFile } from "~/store/collections/uploader/collection-uploader-actions";
-import { projectPanelActions } from "~/store/project-panel/project-panel-action";
-
-const mapStateToProps = (state: RootState) => ({
-    open: state.collections.creator.opened
-});
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
-    handleClose: () => {
-        dispatch(collectionCreateActions.CLOSE_COLLECTION_CREATOR());
-    },
-    onSubmit: (data: { name: string, description: string }, files: UploadFile[]) => {
-        return dispatch<any>(addCollection(data, files.map(f => f.file)))
-            .catch((e: any) => {
-                throw new SubmissionError({ name: e.errors.join("").includes("UniqueViolation") ? "Collection with this name already exists." : "" });
-            });
-    }
-});
-
-const addCollection = (data: { name: string, description: string }, files: File[]) =>
-    (dispatch: Dispatch) => {
-        return dispatch<any>(createCollection(data, files)).then(() => {
-            dispatch(snackbarActions.OPEN_SNACKBAR({
-                message: "Collection has been successfully created.",
-                hideDuration: 2000
-            }));
-            dispatch(projectPanelActions.REQUEST_ITEMS());
-        });
-    };
-
-export const CreateCollectionDialog = connect(mapStateToProps, mapDispatchToProps)(DialogCollectionCreate);
-
diff --git a/src/views-components/create-project-dialog/create-project-dialog.tsx b/src/views-components/create-project-dialog/create-project-dialog.tsx
deleted file mode 100644 (file)
index 43f56ed..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { connect } from "react-redux";
-import { Dispatch } from "redux";
-import { SubmissionError } from "redux-form";
-
-import { RootState } from "~/store/store";
-import { DialogProjectCreate } from "../dialog-create/dialog-project-create";
-import { projectActions, createProject, getProjectList } from "~/store/project/project-action";
-import { projectPanelActions } from "~/store/project-panel/project-panel-action";
-import { snackbarActions } from "~/store/snackbar/snackbar-actions";
-
-const mapStateToProps = (state: RootState) => ({
-    open: state.projects.creator.opened
-});
-
-const addProject = (data: { name: string, description: string }) =>
-    (dispatch: Dispatch, getState: () => RootState) => {
-        const { ownerUuid } = getState().projects.creator;
-        return dispatch<any>(createProject(data)).then(() => {
-            dispatch(snackbarActions.OPEN_SNACKBAR({
-                message: "Project has been successfully created.",
-                hideDuration: 2000
-            }));
-            dispatch(projectPanelActions.REQUEST_ITEMS());
-            dispatch<any>(getProjectList(ownerUuid));
-        });
-    };
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
-    handleClose: () => {
-        dispatch(projectActions.CLOSE_PROJECT_CREATOR());
-    },
-    onSubmit: (data: { name: string, description: string }) => {
-        return dispatch<any>(addProject(data))
-            .catch((e: any) => {
-                throw new SubmissionError({ name: e.errors.join("").includes("UniqueViolation") ? "Project with this name already exists." : "" });
-            });
-    }
-});
-
-export const CreateProjectDialog = connect(mapStateToProps, mapDispatchToProps)(DialogProjectCreate);
index af0e33f1b4260fab01a15bef29fef1985701ecdd..2fb007b8a6a6e102039f75e39912a2bfc67812b7 100644 (file)
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { reduxForm, Field } from 'redux-form';
-import { compose } from 'redux';
-import { TextField } from '~/components/text-field/text-field';
-import { Dialog, DialogActions, DialogContent, DialogTitle } from '@material-ui/core/';
-import { Button, StyleRulesCallback, WithStyles, withStyles, CircularProgress } from '@material-ui/core';
-
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '~/validators/validators';
-import { FileUpload } from "~/components/file-upload/file-upload";
-import { connect, DispatchProp } from "react-redux";
-import { RootState } from "~/store/store";
+import { InjectedFormProps } from 'redux-form';
+import { WithDialogProps } from '~/store/dialog/with-dialog';
+import { CollectionCreateFormDialogData } from '~/store/collections/collection-create-actions';
 import { collectionUploaderActions, UploadFile } from "~/store/collections/uploader/collection-uploader-actions";
-
-type CssRules = "button" | "lastButton" | "formContainer" | "createProgress" | "dialogActions";
-
-const styles: StyleRulesCallback<CssRules> = theme => ({
-    button: {
-        marginLeft: theme.spacing.unit
-    },
-    lastButton: {
-        marginLeft: theme.spacing.unit,
-        marginRight: "20px",
-    },
-    formContainer: {
-        display: "flex",
-        flexDirection: "column",
-    },
-    createProgress: {
-        position: "absolute",
-        minWidth: "20px",
-        right: "110px"
-    },
-    dialogActions: {
-        marginBottom: theme.spacing.unit * 3
-    }
-});
-
-interface DialogCollectionDataProps {
-    open: boolean;
-    handleSubmit: any;
-    submitting: boolean;
-    invalid: boolean;
-    pristine: boolean;
-    files: UploadFile[];
-}
-
-interface DialogCollectionActionProps {
-    handleClose: () => void;
-    onSubmit: (data: { name: string, description: string }, files: UploadFile[]) => void;
-}
-
-type DialogCollectionProps = DialogCollectionDataProps & DialogCollectionActionProps & DispatchProp & WithStyles<CssRules>;
-
-export const COLLECTION_CREATE_DIALOG = "collectionCreateDialog";
-
-export const DialogCollectionCreate = compose(
-    connect((state: RootState) => ({
-        files: state.collections.uploader
-    })),
-    reduxForm({ form: COLLECTION_CREATE_DIALOG }),
-    withStyles(styles))(
-    class DialogCollectionCreate extends React.Component<DialogCollectionProps> {
-            render() {
-                const { classes, open, handleClose, handleSubmit, onSubmit, submitting, invalid, pristine, files } = this.props;
-                const busy = submitting || files.reduce(
-                    (prev, curr) => prev + (curr.loaded > 0 && curr.loaded < curr.total ? 1 : 0), 0
-                ) > 0;
-                return (
-                    <Dialog
-                        open={open}
-                        onClose={handleClose}
-                        fullWidth={true}
-                        maxWidth='sm'
-                        disableBackdropClick={true}
-                        disableEscapeKeyDown={true}>
-                        <form onSubmit={handleSubmit((data: any) => onSubmit(data, files))}>
-                            <DialogTitle id="form-dialog-title">Create a collection</DialogTitle>
-                            <DialogContent className={classes.formContainer}>
-                                <Field name="name"
-                                    disabled={submitting}
-                                    component={TextField}
-                                    validate={COLLECTION_NAME_VALIDATION}
-                                    label="Collection Name" />
-                                <Field name="description"
-                                    disabled={submitting}
-                                    component={TextField}
-                                    validate={COLLECTION_DESCRIPTION_VALIDATION}
-                                    label="Description - optional" />
-                                <FileUpload
-                                    files={files}
-                                    disabled={busy}
-                                    onDrop={files => this.props.dispatch(collectionUploaderActions.SET_UPLOAD_FILES(files))} />
-                            </DialogContent>
-                            <DialogActions className={classes.dialogActions}>
-                                <Button onClick={handleClose} className={classes.button} color="primary"
-                                    disabled={busy}>CANCEL</Button>
-                                <Button type="submit"
-                                    className={classes.lastButton}
-                                    color="primary"
-                                    disabled={invalid || busy || pristine}
-                                    variant="contained">
-                                    CREATE A COLLECTION
-                            </Button>
-                                {busy && <CircularProgress size={20} className={classes.createProgress} />}
-                            </DialogActions>
-                        </form>
-                    </Dialog>
-                );
-            }
-        }
-    );
+import { FormDialog } from '~/components/form-dialog/form-dialog';
+import { CollectionNameField, CollectionDescriptionField } from '~/views-components/form-fields/collection-form-fields';
+import { FileUpload } from '~/components/file-upload/file-upload';
+
+// interface DialogCollectionDataProps {
+//     open: boolean;
+//     handleSubmit: any;
+//     submitting: boolean;
+//     invalid: boolean;
+//     pristine: boolean;
+//     files: UploadFile[];
+// }
+
+type DialogCollectionProps = WithDialogProps<{}> & InjectedFormProps<CollectionCreateFormDialogData>;
+
+export const DialogCollectionCreate = (props: DialogCollectionProps) =>
+    <FormDialog
+        dialogTitle='Create a collection'
+        formFields={CollectionAddFields}
+        submitLabel='Create a Collection'
+        {...props}
+    />;
+
+const CollectionAddFields = () => <span>
+    <CollectionNameField />
+    <CollectionDescriptionField />
+    {/* <FileUpload
+        files={this.props.files}
+        disabled={busy}
+        onDrop={files => this.props.dispatch(collectionUploaderActions.SET_UPLOAD_FILES(files))} /> */}
+</span>;
\ No newline at end of file
index e77114b369a2137d5cdb3584703c68e5163e45f3..8c1d3800a0e1b275d9a12a3d02bfe2cf87828b1d 100644 (file)
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { reduxForm, Field } from 'redux-form';
-import { compose } from 'redux';
-import { TextField } from '~/components/text-field/text-field';
-import { Dialog, DialogActions, DialogContent, DialogTitle } from '@material-ui/core/';
-import { Button, StyleRulesCallback, WithStyles, withStyles, CircularProgress } from '@material-ui/core';
+import { InjectedFormProps } from 'redux-form';
+import { WithDialogProps } from '~/store/dialog/with-dialog';
+import { ProjectCreateFormDialogData } from '~/store/projects/project-create-actions';
+import { FormDialog } from '~/components/form-dialog/form-dialog';
+import { ProjectNameField, ProjectDescriptionField } from '~/views-components/form-fields/project-form-fields';
 
-import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from '~/validators/validators';
+type DialogCollectionProps = WithDialogProps<{}> & InjectedFormProps<ProjectCreateFormDialogData>;
 
-type CssRules = "button" | "lastButton" | "formContainer" | "dialog" | "dialogTitle" | "createProgress" | "dialogActions";
+export const DialogProjectCreate = (props: DialogCollectionProps) =>
+    <FormDialog
+        dialogTitle='Create a project'
+        formFields={ProjectAddFields}
+        submitLabel='Create a Project'
+        {...props}
+    />;
 
-const styles: StyleRulesCallback<CssRules> = theme => ({
-    button: {
-        marginLeft: theme.spacing.unit
-    },
-    lastButton: {
-        marginLeft: theme.spacing.unit,
-        marginRight: "20px",
-    },
-    formContainer: {
-        display: "flex",
-        flexDirection: "column",
-        marginTop: "20px",
-    },
-    dialogTitle: {
-        paddingBottom: "0"
-    },
-    dialog: {
-        minWidth: "600px",
-        minHeight: "320px"
-    },
-    createProgress: {
-        position: "absolute",
-        minWidth: "20px",
-        right: "95px"
-    },
-    dialogActions: {
-        marginBottom: "24px"
-    }
-});
-interface DialogProjectProps {
-    open: boolean;
-    handleClose: () => void;
-    onSubmit: (data: { name: string, description: string }) => void;
-    handleSubmit: any;
-    submitting: boolean;
-    invalid: boolean;
-    pristine: boolean;
-}
-
-export const PROJECT_CREATE_DIALOG = "projectCreateDialog";
-
-export const DialogProjectCreate = compose(
-    reduxForm({ form: PROJECT_CREATE_DIALOG }),
-    withStyles(styles))(
-    class DialogProjectCreate extends React.Component<DialogProjectProps & WithStyles<CssRules>> {
-        render() {
-            const { classes, open, handleClose, handleSubmit, onSubmit, submitting, invalid, pristine } = this.props;
-
-            return (
-                <Dialog
-                    open={open}
-                    onClose={handleClose}
-                    disableBackdropClick={true}
-                    disableEscapeKeyDown={true}>
-                    <div className={classes.dialog}>
-                        <form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
-                            <DialogTitle id="form-dialog-title" className={classes.dialogTitle}>Create a
-                                project</DialogTitle>
-                            <DialogContent className={classes.formContainer}>
-                                <Field name="name"
-                                       component={TextField}
-                                       validate={PROJECT_NAME_VALIDATION}
-                                       label="Project Name"/>
-                                <Field name="description"
-                                       component={TextField}
-                                       validate={PROJECT_DESCRIPTION_VALIDATION}
-                                       label="Description - optional"/>
-                            </DialogContent>
-                            <DialogActions className={classes.dialogActions}>
-                                <Button onClick={handleClose} className={classes.button} color="primary"
-                                        disabled={submitting}>CANCEL</Button>
-                                <Button type="submit"
-                                        className={classes.lastButton}
-                                        color="primary"
-                                        disabled={invalid || submitting || pristine}
-                                        variant="contained">
-                                    CREATE A PROJECT
-                                </Button>
-                                {submitting && <CircularProgress size={20} className={classes.createProgress}/>}
-                            </DialogActions>
-                        </form>
-                    </div>
-                </Dialog>
-            );
-        }
-    }
-);
+const ProjectAddFields = () => <span>
+    <ProjectNameField />
+    <ProjectDescriptionField />
+</span>;
index 9f25c14b811d48a010a6bcdae7a27efa80ed1aec..ee6293abb8c0c64e5fe2431eba26837ea0d2be6a 100644 (file)
@@ -6,7 +6,7 @@ import { compose } from "redux";
 import { withDialog } from "~/store/dialog/with-dialog";
 import { reduxForm } from 'redux-form';
 import { COLLECTION_COPY_FORM_NAME, CollectionCopyFormDialogData, copyCollection } from '~/store/collections/collection-copy-actions';
-import { DialogCollectionCopy } from "~/views-components/dialog-collection-copy/dialog-collection-copy";
+import { DialogCollectionCopy } from "~/views-components/dialog-copy/dialog-collection-copy";
 
 export const CopyCollectionDialog = compose(
     withDialog(COLLECTION_COPY_FORM_NAME),
diff --git a/src/views-components/dialog-forms/create-collection-dialog.ts b/src/views-components/dialog-forms/create-collection-dialog.ts
new file mode 100644 (file)
index 0000000..d2699d8
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } from "redux";
+import { reduxForm } from 'redux-form';
+import { withDialog } from "~/store/dialog/with-dialog";
+import { addCollection, COLLECTION_CREATE_FORM_NAME, CollectionCreateFormDialogData } from '~/store/collections/collection-create-actions';
+import { UploadFile } from "~/store/collections/uploader/collection-uploader-actions";
+import { DialogCollectionCreate } from "~/views-components/dialog-create/dialog-collection-create";
+
+export const CreateCollectionDialog = compose(
+    withDialog(COLLECTION_CREATE_FORM_NAME),
+    reduxForm<CollectionCreateFormDialogData>({
+        form: COLLECTION_CREATE_FORM_NAME,
+        onSubmit: (data, dispatch) => {
+            dispatch(addCollection(data));
+        }
+    })
+)(DialogCollectionCreate);
+
+// onSubmit: (data: { name: string, description: string }, files: UploadFile[]) => void;
\ No newline at end of file
diff --git a/src/views-components/dialog-forms/create-project-dialog.ts b/src/views-components/dialog-forms/create-project-dialog.ts
new file mode 100644 (file)
index 0000000..2e87517
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } from "redux";
+import { reduxForm } from 'redux-form';
+import { withDialog } from "~/store/dialog/with-dialog";
+import { addProject, PROJECT_CREATE_FORM_NAME, ProjectCreateFormDialogData } from '~/store/projects/project-create-actions';
+import { DialogProjectCreate } from '~/views-components/dialog-create/dialog-project-create';
+
+export const CreateProjectDialog = compose(
+    withDialog(PROJECT_CREATE_FORM_NAME),
+    reduxForm<ProjectCreateFormDialogData>({
+        form: PROJECT_CREATE_FORM_NAME,
+        onSubmit: (data, dispatch) => {
+            dispatch(addProject(data));
+        }
+    })
+)(DialogProjectCreate);
\ No newline at end of file
similarity index 63%
rename from src/views-components/move-collection-dialog/move-collection-dialog.ts
rename to src/views-components/dialog-forms/move-collection-dialog.ts
index 783f0c78479d81e687a46e7e09a8e253dfba9015..38d6d0339288031485bcd51bbaf98932fc7bcdd7 100644 (file)
@@ -5,16 +5,16 @@
 import { compose } from "redux";
 import { withDialog } from "~/store/dialog/with-dialog";
 import { reduxForm } from 'redux-form';
-import { MoveToFormDialog } from '../move-to-dialog/move-to-dialog';
-import { MOVE_COLLECTION_DIALOG, moveCollection } from '~/store/move-collection-dialog/move-collection-dialog';
+import { DialogMoveTo } from '~/views-components/dialog-move/dialog-move-to';
+import { COLLECTION_MOVE_FORM_NAME, moveCollection } from '~/store/collections/collection-move-actions';
 import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 
 export const MoveCollectionDialog = compose(
-    withDialog(MOVE_COLLECTION_DIALOG),
+    withDialog(COLLECTION_MOVE_FORM_NAME),
     reduxForm<MoveToFormDialogData>({
-        form: MOVE_COLLECTION_DIALOG,
+        form: COLLECTION_MOVE_FORM_NAME,
         onSubmit: (data, dispatch) => {
             dispatch(moveCollection(data));
         }
     })
-)(MoveToFormDialog);
+)(DialogMoveTo);
similarity index 59%
rename from src/views-components/move-project-dialog/move-project-dialog.ts
rename to src/views-components/dialog-forms/move-project-dialog.ts
index 9ec67486f63597c2bf2c327d01cadaa6d7f34f2f..dd102b145f745d009cbfe2ee6cd7f90b918e525d 100644 (file)
@@ -5,18 +5,18 @@
 import { compose } from "redux";
 import { withDialog } from "~/store/dialog/with-dialog";
 import { reduxForm } from 'redux-form';
-import { MOVE_PROJECT_DIALOG } from '~/store/move-project-dialog/move-project-dialog';
-import { moveProject } from '~/store/move-project-dialog/move-project-dialog';
+import { PROJECT_MOVE_FORM_NAME } from '~/store/projects/project-move-actions';
+import { moveProject } from '~/store/projects/project-move-actions';
 import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
-import { MoveToFormDialog } from '../move-to-dialog/move-to-dialog';
+import { DialogMoveTo } from '~/views-components/dialog-move/dialog-move-to';
 
 export const MoveProjectDialog = compose(
-    withDialog(MOVE_PROJECT_DIALOG),
+    withDialog(PROJECT_MOVE_FORM_NAME),
     reduxForm<MoveToFormDialogData>({
-        form: MOVE_PROJECT_DIALOG,
+        form: PROJECT_MOVE_FORM_NAME,
         onSubmit: (data, dispatch) => {
             dispatch(moveProject(data));
         }
     })
-)(MoveToFormDialog);
+)(DialogMoveTo);
 
diff --git a/src/views-components/dialog-forms/update-collection-dialog.ts b/src/views-components/dialog-forms/update-collection-dialog.ts
new file mode 100644 (file)
index 0000000..2c4296d
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } 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 { editCollection, COLLECTION_UPDATE_FORM_NAME, CollectionUpdateFormDialogData } from '~/store/collections/collection-update-actions';
+
+export const UpdateCollectionDialog = compose(
+    withDialog(COLLECTION_UPDATE_FORM_NAME),
+    reduxForm<CollectionUpdateFormDialogData>({
+        form: COLLECTION_UPDATE_FORM_NAME,
+        onSubmit: (data, dispatch) => {
+            dispatch(editCollection(data));
+        }
+    })
+)(DialogCollectionUpdate);
\ No newline at end of file
diff --git a/src/views-components/dialog-forms/update-project-dialog.ts b/src/views-components/dialog-forms/update-project-dialog.ts
new file mode 100644 (file)
index 0000000..598d0b1
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } from "redux";
+import { reduxForm } from 'redux-form';
+import { withDialog } from "~/store/dialog/with-dialog";
+import { DialogProjectUpdate } from '~/views-components/dialog-update/dialog-project-update';
+import { editProject, PROJECT_UPDATE_FORM_NAME, ProjectUpdateFormDialogData } from '~/store/projects/project-update-actions';
+
+export const UpdateProjectDialog = compose(
+    withDialog(PROJECT_UPDATE_FORM_NAME),
+    reduxForm<ProjectUpdateFormDialogData>({
+        form: PROJECT_UPDATE_FORM_NAME,
+        onSubmit: (data, dispatch) => {
+            dispatch(editProject(data));
+        }
+    })
+)(DialogProjectUpdate);
\ No newline at end of file
similarity index 88%
rename from src/views-components/move-to-dialog/move-to-dialog.tsx
rename to src/views-components/dialog-move/dialog-move-to.tsx
index d8f89d39ac52d29e5fddd687f37dbe6448735d7c..425b9e462a5439b47f3eb82a26fbe4eefe5481e0 100644 (file)
@@ -10,7 +10,7 @@ import { ProjectTreePickerField } from '~/views-components/project-tree-picker/p
 import { MOVE_TO_VALIDATION } from '~/validators/validators';
 import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 
-export const MoveToFormDialog = (props: WithDialogProps<string> & InjectedFormProps<MoveToFormDialogData>) =>
+export const DialogMoveTo = (props: WithDialogProps<string> & InjectedFormProps<MoveToFormDialogData>) =>
     <FormDialog
         dialogTitle='Move to'
         formFields={MoveToDialogFields}
index 18c43f2d008a1cbd407410d2389e3de8532a9fd0..b98e0e840f71ae94b98de0dc99636703db22e13f 100644 (file)
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { reduxForm, Field } from 'redux-form';
-import { compose } from 'redux';
-import { ArvadosTheme } from '~/common/custom-theme';
-import { Dialog, DialogActions, DialogContent, DialogTitle, StyleRulesCallback, withStyles, WithStyles, Button, CircularProgress } from '@material-ui/core';
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '~/validators/validators';
-import { COLLECTION_FORM_NAME } from '~/store/collections/updater/collection-updater-action';
-import { TextField } from '~/components/text-field/text-field';
-
-type CssRules = 'content' | 'actions' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
-
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
-    content: {
-        display: 'flex',
-        flexDirection: 'column'
-    },
-    actions: {
-        margin: 0,
-        padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3 - theme.spacing.unit / 2}px 
-                ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`
-    },
-    buttonWrapper: {
-        position: 'relative'
-    },
-    saveButton: {
-        boxShadow: 'none'
-    },
-    circularProgress: {
-        position: 'absolute',
-        top: 0,
-        bottom: 0,
-        left: 0,
-        right: 0,
-        margin: 'auto'
-    }
-});
-
-interface DialogCollectionDataProps {
-    open: boolean;
-    handleSubmit: any;
-    submitting: boolean;
-    invalid: boolean;
-    pristine: boolean;
-}
-
-interface DialogCollectionAction {
-    handleClose: () => void;
-    onSubmit: (data: { name: string, description: string }) => void;
-}
-
-type DialogCollectionProps = DialogCollectionDataProps & DialogCollectionAction & WithStyles<CssRules>;
-
-export const DialogCollectionUpdate = compose(
-    reduxForm({ form: COLLECTION_FORM_NAME }),
-    withStyles(styles))(
-
-        class DialogCollectionUpdate extends React.Component<DialogCollectionProps> {
-
-            render() {
-                const { classes, open, handleClose, handleSubmit, onSubmit, submitting, invalid, pristine } = this.props;
-                return (
-                    <Dialog open={open}
-                        onClose={handleClose}
-                        fullWidth={true}
-                        maxWidth='sm'
-                        disableBackdropClick={true}
-                        disableEscapeKeyDown={true}>
-
-                        <form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
-                            <DialogTitle>Edit Collection</DialogTitle>
-                            <DialogContent className={classes.content}>
-                                <Field name='name'
-                                    disabled={submitting}
-                                    component={TextField}
-                                    validate={COLLECTION_NAME_VALIDATION}
-                                    label="Collection Name" />
-                                <Field name='description'
-                                    disabled={submitting}
-                                    component={TextField}
-                                    validate={COLLECTION_DESCRIPTION_VALIDATION}
-                                    label="Description - optional" />
-                            </DialogContent>
-                            <DialogActions className={classes.actions}>
-                                <Button onClick={handleClose} color="primary"
-                                    disabled={submitting}>CANCEL</Button>
-                                <div className={classes.buttonWrapper}>
-                                    <Button type="submit" className={classes.saveButton}
-                                        color="primary"
-                                        disabled={invalid || submitting || pristine}
-                                        variant="contained">
-                                        SAVE
-                                    </Button>
-                                    {submitting && <CircularProgress size={20} className={classes.circularProgress} />}
-                                </div>
-                            </DialogActions>
-                        </form>
-                    </Dialog>
-                );
-            }
-        }
-    );
+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';
+
+type DialogCollectionProps = WithDialogProps<{}> & InjectedFormProps<CollectionUpdateFormDialogData>;
+
+export const DialogCollectionUpdate = (props: DialogCollectionProps) =>
+    <FormDialog
+        dialogTitle='Edit Collection'
+        formFields={CollectionEditFields}
+        submitLabel='Save'
+        {...props}
+    />;
+
+const CollectionEditFields = () => <span>
+    <CollectionNameField />
+    <CollectionDescriptionField />
+</span>;
index b5e788c026ce1693fc447a5940f97e0d48591f76..49b97a6f9ab1f08f7b3d889db720821a8a43ef18 100644 (file)
@@ -3,99 +3,23 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { reduxForm, Field } from 'redux-form';
-import { compose } from 'redux';
-import { ArvadosTheme } from '~/common/custom-theme';
-import { StyleRulesCallback, WithStyles, withStyles, Dialog, DialogTitle, DialogContent, DialogActions, CircularProgress, Button } from '../../../node_modules/@material-ui/core';
-import { TextField } from '~/components/text-field/text-field';
-import { PROJECT_FORM_NAME } from '~/store/project/project-action';
-import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from '~/validators/validators';
-
-type CssRules = 'content' | 'actions' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
-
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
-    content: {
-        display: 'flex',
-        flexDirection: 'column'
-    },
-    actions: {
-        margin: 0,
-        padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3 - theme.spacing.unit / 2}px 
-                ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`
-    },
-    buttonWrapper: {
-        position: 'relative'
-    },
-    saveButton: {
-        boxShadow: 'none'
-    },
-    circularProgress: {
-        position: 'absolute',
-        top: 0,
-        bottom: 0,
-        left: 0,
-        right: 0,
-        margin: 'auto'
-    }
-});
-
-interface DialogProjectDataProps {
-    open: boolean;
-    handleSubmit: any;
-    submitting: boolean;
-    invalid: boolean;
-    pristine: boolean;
-}
-
-interface DialogProjectActionProps {
-    handleClose: () => void;
-    onSubmit: (data: { name: string, description: string }) => void;
-}
-
-type DialogProjectProps = DialogProjectDataProps & DialogProjectActionProps & WithStyles<CssRules>;
-
-export const DialogProjectUpdate = compose(
-    reduxForm({ form: PROJECT_FORM_NAME }),
-    withStyles(styles))(
-
-        class DialogProjectUpdate extends React.Component<DialogProjectProps> {
-            render() {
-                const { handleSubmit, handleClose, onSubmit, open, classes, submitting, invalid, pristine } = this.props;
-                return <Dialog open={open}
-                    onClose={handleClose}
-                    fullWidth={true}
-                    maxWidth='sm'
-                    disableBackdropClick={true}
-                    disableEscapeKeyDown={true}>
-                    <form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
-                        <DialogTitle>Edit Project</DialogTitle>
-                        <DialogContent className={classes.content}>
-                            <Field name='name' 
-                                disabled={submitting}
-                                component={TextField}
-                                validate={PROJECT_NAME_VALIDATION}
-                                label="Project Name" />
-                            <Field name='description' 
-                                disabled={submitting}
-                                component={TextField} 
-                                validate={PROJECT_DESCRIPTION_VALIDATION}
-                                label="Description - optional" />
-                        </DialogContent>
-                        <DialogActions className={classes.actions}>
-                            <Button onClick={handleClose} color="primary"
-                                disabled={submitting}>CANCEL</Button>
-                            <div className={classes.buttonWrapper}>
-                                <Button type="submit" className={classes.saveButton}
-                                    color="primary"
-                                    disabled={invalid || submitting || pristine}
-                                    variant="contained">
-                                    SAVE
-                                </Button>
-                                {submitting && <CircularProgress size={20} className={classes.circularProgress} />}
-                            </div>
-                        </DialogActions>
-                    </form>
-                </Dialog>;
-            }
-        }
-    );
+import { InjectedFormProps } from 'redux-form';
+import { WithDialogProps } from '~/store/dialog/with-dialog';
+import { ProjectUpdateFormDialogData } from '~/store/projects/project-update-actions';
+import { FormDialog } from '~/components/form-dialog/form-dialog';
+import { ProjectNameField, ProjectDescriptionField } from '~/views-components/form-fields/project-form-fields';
+
+type DialogProjectProps = WithDialogProps<{}> & InjectedFormProps<ProjectUpdateFormDialogData>;
+
+export const DialogProjectUpdate = (props: DialogProjectProps) =>
+    <FormDialog
+        dialogTitle='Edit Project'
+        formFields={ProjectEditFields}
+        submitLabel='Save'
+        {...props}
+    />;
+
+const ProjectEditFields = () => <span>
+    <ProjectNameField />
+    <ProjectDescriptionField />
+</span>;
similarity index 93%
rename from src/views-components/form-dialog/collection-form-dialog.tsx
rename to src/views-components/form-fields/collection-form-fields.tsx
index d5f1d8521aae89fe693ce44efb9e1248eca42413..10c807b6211bbacf0316abcf95d04b8407708f1e 100644 (file)
@@ -6,7 +6,7 @@ import * as React from "react";
 import { Field, WrappedFieldProps } from "redux-form";
 import { TextField } from "~/components/text-field/text-field";
 import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "~/validators/validators";
-import { ProjectTreePicker } from "../project-tree-picker/project-tree-picker";
+import { ProjectTreePicker } from "~/views-components/project-tree-picker/project-tree-picker";
 
 export const CollectionPartialCopyFields = () => <div style={{ display: 'flex' }}>
     <div>
diff --git a/src/views-components/form-fields/project-form-fields.tsx b/src/views-components/form-fields/project-form-fields.tsx
new file mode 100644 (file)
index 0000000..630877e
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Field } from "redux-form";
+import { TextField } from "~/components/text-field/text-field";
+import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from "~/validators/validators";
+
+export const ProjectNameField = () =>
+    <Field
+        name='name'
+        component={TextField}
+        validate={PROJECT_NAME_VALIDATION}
+        label="Project Name" />;
+
+export const ProjectDescriptionField = () =>
+    <Field
+        name='description'
+        component={TextField}
+        validate={PROJECT_DESCRIPTION_VALIDATION}
+        label="Description - optional" />;
\ No newline at end of file
diff --git a/src/views-components/update-collection-dialog/update-collection-dialog..tsx b/src/views-components/update-collection-dialog/update-collection-dialog..tsx
deleted file mode 100644 (file)
index 239df58..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { connect } from "react-redux";
-import { Dispatch } from "redux";
-import { SubmissionError } from "redux-form";
-import { RootState } from "~/store/store";
-import { snackbarActions } from "~/store/snackbar/snackbar-actions";
-import { collectionUpdaterActions, updateCollection } from "~/store/collections/updater/collection-updater-action";
-import { dataExplorerActions } from "~/store/data-explorer/data-explorer-action";
-import { PROJECT_PANEL_ID } from "~/views/project-panel/project-panel";
-import { DialogCollectionUpdate } from "../dialog-update/dialog-collection-update";
-
-const mapStateToProps = (state: RootState) => ({
-    open: state.collections.updater.opened
-});
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
-    handleClose: () => {
-        dispatch(collectionUpdaterActions.CLOSE_COLLECTION_UPDATER());
-    },
-    onSubmit: (data: { name: string, description: string }) => {
-        return dispatch<any>(editCollection(data))
-            .catch((e: any) => {
-                if(e.errors) {
-                    throw new SubmissionError({ name: e.errors.join("").includes("UniqueViolation") ? "Collection with this name already exists." : "" });
-                }
-            });
-    }
-});
-
-const editCollection = (data: { name: string, description: string }) =>
-    (dispatch: Dispatch) => {
-        return dispatch<any>(updateCollection(data)).then(() => {
-            dispatch(snackbarActions.OPEN_SNACKBAR({
-                message: "Collection has been successfully updated.",
-                hideDuration: 2000
-            }));
-            dispatch(dataExplorerActions.REQUEST_ITEMS({ id: PROJECT_PANEL_ID }));
-        });
-    };
-
-export const UpdateCollectionDialog = connect(mapStateToProps, mapDispatchToProps)(DialogCollectionUpdate);
diff --git a/src/views-components/update-project-dialog/update-project-dialog.tsx b/src/views-components/update-project-dialog/update-project-dialog.tsx
deleted file mode 100644 (file)
index 0ea23c8..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { connect } from "react-redux";
-import { Dispatch } from "redux";
-import { SubmissionError } from "redux-form";
-import { RootState } from "~/store/store";
-import { snackbarActions } from "~/store/snackbar/snackbar-actions";
-import { DialogProjectUpdate } from "../dialog-update/dialog-project-update";
-import { projectActions, updateProject } from "~/store/project/project-action";
-
-const mapStateToProps = (state: RootState) => ({
-    open: state.projects.updater.opened
-});
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
-    handleClose: () => {
-        dispatch(projectActions.CLOSE_PROJECT_UPDATER());
-    },
-    onSubmit: (data: { name: string, description: string }) => {
-        return dispatch<any>(editProject(data))
-            .catch((e: any) => {
-                if (e.errors) {
-                    throw new SubmissionError({ name: e.errors.join("").includes("UniqueViolation") ? "Project with this name already exists." : "" });
-                }
-            });
-    }
-});
-
-const editProject = (data: { name: string, description: string }) =>
-    (dispatch: Dispatch, getState: () => RootState) => {
-        const { uuid } = getState().projects.updater;
-        return dispatch<any>(updateProject(data)).then(() => {
-            dispatch(snackbarActions.OPEN_SNACKBAR({
-                message: "Project has been successfully updated.",
-                hideDuration: 2000
-            }));
-        });
-    };
-
-export const UpdateProjectDialog = connect(mapStateToProps, mapDispatchToProps)(DialogProjectUpdate);
index 7b9701ad4678fcd72b68678db2634ec3846520d9..4640323f0923b863fd3f5e24a3754a4b9d522982 100644 (file)
@@ -21,11 +21,8 @@ import { sidePanelActions } from '~/store/side-panel/side-panel-action';
 import { SidePanel, SidePanelItem } from '~/components/side-panel/side-panel';
 import { ItemMode, setProjectItem } from "~/store/navigation/navigation-action";
 import { projectActions } from "~/store/project/project-action";
-import { collectionCreateActions } from '~/store/collections/creator/collection-creator-action';
-import { ProjectPanel } from "~/views/project-panel/project-panel";
 import { DetailsPanel } from '~/views-components/details-panel/details-panel';
 import { ArvadosTheme } from '~/common/custom-theme';
-import { CreateProjectDialog } from "~/views-components/create-project-dialog/create-project-dialog";
 
 import { detailsPanelActions, loadDetails } from "~/store/details-panel/details-panel-action";
 import { contextMenuActions } from "~/store/context-menu/context-menu-actions";
@@ -37,23 +34,27 @@ import { FavoritePanel } from "../favorite-panel/favorite-panel";
 import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
 import { Snackbar } from '~/views-components/snackbar/snackbar';
 import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-action';
-import { CreateCollectionDialog } from '~/views-components/create-collection-dialog/create-collection-dialog';
 import { CollectionPanel } from '../collection-panel/collection-panel';
 import { loadCollection, loadCollectionTags } from '~/store/collection-panel/collection-panel-action';
 import { getCollectionUrl } from '~/models/collection';
-import { UpdateCollectionDialog } from '~/views-components/update-collection-dialog/update-collection-dialog.';
-import { UpdateProjectDialog } from '~/views-components/update-project-dialog/update-project-dialog';
+
+import { PROJECT_CREATE_FORM_NAME, openProjectCreateDialog } from '~/store/projects/project-create-actions';
+import { COLLECTION_CREATE_FORM_NAME, openCollectionCreateDialog } from '~/store/collections/collection-create-actions';
+import { CreateCollectionDialog } from '~/views-components/dialog-forms/create-collection-dialog';
+import { UpdateCollectionDialog } from '~/views-components/dialog-forms/update-collection-dialog';
+import { CreateProjectDialog } from '~/views-components/dialog-forms/create-project-dialog';
+import { UpdateProjectDialog } from '~/views-components/dialog-forms/update-project-dialog';
+
+import { ProjectPanel } from "~/views/project-panel/project-panel";
 import { AuthService } from "~/services/auth-service/auth-service";
 import { RenameFileDialog } from '~/views-components/rename-file-dialog/rename-file-dialog';
 import { FileRemoveDialog } from '~/views-components/file-remove-dialog/file-remove-dialog';
 import { MultipleFilesRemoveDialog } from '~/views-components/file-remove-dialog/multiple-files-remove-dialog';
 import { DialogCollectionCreateWithSelectedFile } from '~/views-components/create-collection-dialog-with-selected/create-collection-dialog-with-selected';
-import { COLLECTION_CREATE_DIALOG } from '~/views-components/dialog-create/dialog-collection-create';
-import { PROJECT_CREATE_DIALOG } from '~/views-components/dialog-create/dialog-project-create';
 import { UploadCollectionFilesDialog } from '~/views-components/upload-collection-files-dialog/upload-collection-files-dialog';
-import { CollectionPartialCopyDialog } from '../../views-components/collection-partial-copy-dialog/collection-partial-copy-dialog';
-import { MoveProjectDialog } from '~/views-components/move-project-dialog/move-project-dialog';
-import { MoveCollectionDialog } from '~/views-components/move-collection-dialog/move-collection-dialog';
+import { CollectionPartialCopyDialog } from '~/views-components/collection-partial-copy-dialog/collection-partial-copy-dialog';
+import { MoveProjectDialog } from '~/views-components/dialog-forms/move-project-dialog';
+import { MoveCollectionDialog } from '~/views-components/dialog-forms/move-collection-dialog';
 import { CopyCollectionDialog } from '~/views-components/dialog-forms/copy-collection-dialog';
 
 const DRAWER_WITDH = 240;
@@ -397,13 +398,13 @@ export const Workbench = withStyles(styles)(
             }
 
             handleProjectCreationDialogOpen = (itemUuid: string) => {
-                this.props.dispatch(reset(PROJECT_CREATE_DIALOG));
-                this.props.dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: itemUuid }));
+                this.props.dispatch(reset(PROJECT_CREATE_FORM_NAME));
+                this.props.dispatch<any>(openProjectCreateDialog(itemUuid));
             }
 
             handleCollectionCreationDialogOpen = (itemUuid: string) => {
-                this.props.dispatch(reset(COLLECTION_CREATE_DIALOG));
-                this.props.dispatch(collectionCreateActions.OPEN_COLLECTION_CREATOR({ ownerUuid: itemUuid }));
+                this.props.dispatch(reset(COLLECTION_CREATE_FORM_NAME));
+                this.props.dispatch<any>(openCollectionCreateDialog(itemUuid));
             }
 
             openContextMenu = (event: React.MouseEvent<HTMLElement>, resource: { name: string; uuid: string; description?: string; kind: ContextMenuKind; }) => {