Connect project dialog to the store
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 9 Jul 2018 15:53:23 +0000 (17:53 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 9 Jul 2018 15:53:23 +0000 (17:53 +0200)
Feature #13694

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

src/store/project/project-action.ts
src/store/project/project-reducer.test.ts
src/store/project/project-reducer.ts
src/views-components/create-project-dialog/create-project-dialog.tsx [new file with mode: 0644]
src/views/project-panel/project-panel.tsx
src/views/workbench/workbench.tsx

index a02c9e75461565687884b9ff960f7e5b76ca1b6c..184922e3ecde97900cb0e694a4b193639c02e1bb 100644 (file)
@@ -11,7 +11,8 @@ import FilterBuilder from "../../common/api/filter-builder";
 import { ThunkAction } from "../../../node_modules/redux-thunk";
 
 const actions = unionize({
-    OPEN_PROJECT_CREATOR: ofType<{}>(),
+    OPEN_PROJECT_CREATOR: ofType<{ownerUuid: string}>(),
+    CLOSE_PROJECT_CREATOR: ofType<{}>(),
     CREATE_PROJECT: ofType<Partial<ProjectResource>>(),
     CREATE_PROJECT_SUCCESS: ofType<ProjectResource>(),
     CREATE_PROJECT_ERROR: ofType<string>(),
index 724623e4c9f362c9dd60163446915fe6fb46ecf7..0862142d48b264e5bd84706d5e420dff06d07d9d 100644 (file)
@@ -77,7 +77,7 @@ describe('project-reducer', () => {
                 status: 1
             }],
             currentItemId: "1",
-            creator: { opened: false, pending: false },
+            creator: { opened: false, pending: false, ownerUuid: "" },
         };
         const project = {
             items: [{
@@ -120,7 +120,7 @@ describe('project-reducer', () => {
                 status: 1
             }],
             currentItemId: "1",
-            creator: { opened: false, pending: false }
+            creator: { opened: false, pending: false, ownerUuid: "" }
         };
         const project = {
             items: [{
@@ -140,7 +140,7 @@ describe('project-reducer', () => {
                 toggled: true
             }],
             currentItemId: "1",
-            creator: { opened: false, pending: false },
+            creator: { opened: false, pending: false, ownerUuid: "" },
         };
 
         const state = projectsReducer(initialState, actions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(initialState.items[0].id));
@@ -167,7 +167,7 @@ describe('project-reducer', () => {
                 toggled: false,
             }],
             currentItemId: "1",
-            creator: { opened: false, pending: false }
+            creator: { opened: false, pending: false, ownerUuid: "" }
         };
         const project = {
             items: [{
@@ -187,7 +187,7 @@ describe('project-reducer', () => {
                 toggled: true
             }],
             currentItemId: "1",
-            creator: { opened: false, pending: false },
+            creator: { opened: false, pending: false, ownerUuid: "" },
         };
 
         const state = projectsReducer(initialState, actions.TOGGLE_PROJECT_TREE_ITEM_OPEN(initialState.items[0].id));
index bb7d3d07e3720cfba20aa939122582d54be5860f..2c1ab81eb50bb4cc3f138eb4cc98badbee5275da 100644 (file)
@@ -11,12 +11,15 @@ import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
 export type ProjectState = {
     items: Array<TreeItem<Project>>,
     currentItemId: string,
-    creator: {
-        opened: boolean,
-        pending: boolean
-    }
+    creator: ProjectCreator
 };
 
+interface ProjectCreator {
+    opened: boolean;
+    pending: boolean;
+    ownerUuid: string;
+}
+
 export function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
     let item;
     for (const t of tree) {
@@ -89,22 +92,32 @@ function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[],
     return items;
 }
 
+const updateCreator = (state: ProjectState, creator: Partial<ProjectCreator>) => ({
+    ...state,
+    creator: {
+        ...state.creator,
+        ...creator
+    }
+});
+
 const initialState: ProjectState = {
     items: [],
     currentItemId: "",
     creator: {
         opened: false,
-        pending: false
+        pending: false,
+        ownerUuid: ""
     }
 };
 
 
 const projectsReducer = (state: ProjectState = initialState, action: ProjectAction) => {
     return actions.match(action, {
-        OPEN_PROJECT_CREATOR: () => ({ ...state, creator: { opened: true, pending: false } }),
-        CREATE_PROJECT: () => ({ ...state, creator: { opened: false, pending: true } }),
-        CREATE_PROJECT_SUCCESS: () => ({ ...state, creator: { opened: false, pending: false } }),
-        CREATE_PROJECT_ERROR: () => ({ ...state, creator: { opened: false, pending: false } }),
+        OPEN_PROJECT_CREATOR: ({ ownerUuid }) => updateCreator(state, { ownerUuid, opened: true, pending: false }),
+        CLOSE_PROJECT_CREATOR: () => updateCreator(state, { opened: false }),
+        CREATE_PROJECT: () => updateCreator(state, { pending: true }),
+        CREATE_PROJECT_SUCCESS: () => updateCreator(state, { ownerUuid: "", pending: false }),
+        CREATE_PROJECT_ERROR: () => updateCreator(state, { ownerUuid: "", pending: false }),
         REMOVE_PROJECT: () => state,
         PROJECTS_REQUEST: itemId => {
             const items = _.cloneDeep(state.items);
@@ -129,6 +142,7 @@ const projectsReducer = (state: ProjectState = initialState, action: ProjectActi
                 item.open = !item.open;
             }
             return {
+                ...state,
                 items,
                 currentItemId: itemId
             };
@@ -142,6 +156,7 @@ const projectsReducer = (state: ProjectState = initialState, action: ProjectActi
                 item.active = true;
             }
             return {
+                ...state,
                 items,
                 currentItemId: itemId
             };
@@ -150,6 +165,7 @@ const projectsReducer = (state: ProjectState = initialState, action: ProjectActi
             const items = _.cloneDeep(state.items);
             resetTreeActivity(items);
             return {
+                ...state,
                 items,
                 currentItemId: ""
             };
diff --git a/src/views-components/create-project-dialog/create-project-dialog.tsx b/src/views-components/create-project-dialog/create-project-dialog.tsx
new file mode 100644 (file)
index 0000000..d97eebc
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { connect } from "react-redux";
+import { Dispatch } from "../../../node_modules/redux";
+import { RootState } from "../../store/store";
+import DialogProjectCreate from "../../components/dialog-create/dialog-project-create";
+import actions from "../../store/project/project-action";
+
+const mapStateToProps = (state: RootState) => ({
+    open: state.projects.creator.opened
+});
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+    handleClose: () => {
+        dispatch(actions.CLOSE_PROJECT_CREATOR());
+    }
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(DialogProjectCreate);
index f1b82357ec524de9abe55b76add0dde639350bc6..d4e5e0a84e01daccd3f0a4e96b988fc12c1d4109 100644 (file)
@@ -27,7 +27,7 @@ type ProjectPanelProps = {
     onItemClick: (item: ProjectPanelItem) => void,
     onItemRouteChange: (itemId: string) => void,
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: ProjectPanelItem) => void;
-    onDialogOpen: () => void;
+    onDialogOpen: (ownerUuid: string) => void;
 }
     & DispatchProp
     & WithStyles<CssRules>
@@ -43,7 +43,7 @@ class ProjectPanel extends React.Component<ProjectPanelProps> {
                 <Button color="primary" variant="raised" className={this.props.classes.button}>
                     Run a process
                 </Button>
-                <Button color="primary" onClick={this.props.onDialogOpen} variant="raised" className={this.props.classes.button}>
+                <Button color="primary" onClick={() => this.props.onDialogOpen(this.props.currentItemId)} variant="raised" className={this.props.classes.button}>
                     New project
                 </Button>
             </div>
index 9c1336c0d074884edf590ae550b4606718fe16ac..303d055aacab47319de4c4ce5e9aeb3e3429a667 100644 (file)
@@ -29,7 +29,9 @@ import DetailsPanel from '../../views-components/details-panel/details-panel';
 import { ArvadosTheme } from '../../common/custom-theme';
 import ContextMenu, { ContextMenuAction } from '../../components/context-menu/context-menu';
 import { mockAnchorFromMouseEvent } from '../../components/popover/helpers';
-import DialogProjectCreate from '../../components/dialog-create/dialog-project-create';
+import CreateProjectDialog from "../../views-components/create-project-dialog/create-project-dialog";
+import { authService } from '../../services/services';
+
 
 const drawerWidth = 240;
 const appBarHeight = 100;
@@ -95,8 +97,8 @@ interface NavMenuItem extends MainAppBarMenuItem {
 interface WorkbenchState {
     contextMenu: {
         anchorEl?: HTMLElement;
+        itemUuid?: string;
     };
-    isCreationDialogOpen: boolean;
     anchorEl: any;
     searchText: string;
     menuItems: {
@@ -110,7 +112,8 @@ interface WorkbenchState {
 class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
     state = {
         contextMenu: {
-            anchorEl: undefined
+            anchorEl: undefined,
+            itemUuid: undefined
         },
         isCreationDialogOpen: false,
         anchorEl: null,
@@ -155,8 +158,8 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
         onDetailsPanelToggle: () => {
             this.setState(prev => ({ isDetailsPanelOpened: !prev.isDetailsPanelOpened }));
         },
-        onContextMenu: (event: React.MouseEvent<HTMLElement>, breadcrumb: Breadcrumb) => {
-            this.openContextMenu(event, breadcrumb);
+        onContextMenu: (event: React.MouseEvent<HTMLElement>, breadcrumb: NavBreadcrumb) => {
+            this.openContextMenu(event, breadcrumb.itemId);
         }
     };
 
@@ -169,27 +172,32 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
         this.props.dispatch(projectActions.RESET_PROJECT_TREE_ACTIVITY(itemId));
     }
 
-    handleCreationDialogOpen = () => {
+    handleCreationDialogOpen = (itemUuid: string) => {
         this.closeContextMenu();
-        this.setState({ isCreationDialogOpen: true });
+        this.props.dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: itemUuid }));
     }
 
-    handleCreationDialogClose = () => {
-        this.setState({ isCreationDialogOpen: false });
-    }
 
-    openContextMenu = (event: React.MouseEvent<HTMLElement>, item: any) => {
+    openContextMenu = (event: React.MouseEvent<HTMLElement>, itemUuid: string) => {
         event.preventDefault();
-        this.setState({ contextMenu: { anchorEl: mockAnchorFromMouseEvent(event) } });
-        console.log(item);
+        this.setState({
+            contextMenu: {
+                anchorEl: mockAnchorFromMouseEvent(event),
+                itemUuid
+            }
+        });
     }
 
     closeContextMenu = () => {
         this.setState({ contextMenu: {} });
     }
 
-    openCreateDialog = (item: ContextMenuAction) =>
-        item.openCreateDialog ? this.handleCreationDialogOpen() : void 0
+    openCreateDialog = (item: ContextMenuAction) => {
+        const { itemUuid } = this.state.contextMenu;
+        if (item.openCreateDialog && itemUuid) {
+            this.handleCreationDialogOpen(itemUuid);
+        }
+    }
 
     render() {
         const path = getTreePath(this.props.projects, this.props.currentProjectId);
@@ -221,12 +229,12 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
                             toggleOpen={this.toggleSidePanelOpen}
                             toggleActive={this.toggleSidePanelActive}
                             sidePanelItems={this.props.sidePanelItems}
-                            onContextMenu={this.openContextMenu}>
+                            onContextMenu={(event) => this.openContextMenu(event, authService.getUuid() || "")}>
                             <ProjectTree
                                 projects={this.props.projects}
                                 toggleOpen={itemId => this.props.dispatch<any>(setProjectItem(itemId, ItemMode.OPEN))}
                                 toggleActive={itemId => this.props.dispatch<any>(setProjectItem(itemId, ItemMode.ACTIVE))}
-                                onContextMenu={this.openContextMenu} />
+                                onContextMenu={(event, item) => this.openContextMenu(event, item.data.uuid)} />
                         </SidePanel>
                     </Drawer>}
                 <main className={classes.contentWrapper}>
@@ -244,7 +252,7 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
                     actions={contextMenuActions}
                     onActionClick={this.openCreateDialog}
                     onClose={this.closeContextMenu} />
-                <DialogProjectCreate open={this.state.isCreationDialogOpen} handleClose={this.handleCreationDialogClose} />
+                <CreateProjectDialog />
             </div>
         );
     }
@@ -252,7 +260,7 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
     renderProjectPanel = (props: RouteComponentProps<{ id: string }>) => <ProjectPanel
         onItemRouteChange={itemId => this.props.dispatch<any>(setProjectItem(itemId, ItemMode.ACTIVE))}
         onItemClick={item => this.props.dispatch<any>(setProjectItem(item.uuid, ItemMode.ACTIVE))}
-        onContextMenu={this.openContextMenu}
+        onContextMenu={(event, item) => this.openContextMenu(event, item.uuid)}
         onDialogOpen={this.handleCreationDialogOpen}
         {...props} />
 }