18559: Replace setup vm dialog with setup confirmation dialog
authorStephen Smith <stephen@curii.com>
Thu, 24 Mar 2022 19:07:27 +0000 (15:07 -0400)
committerStephen Smith <stephen@curii.com>
Thu, 24 Mar 2022 19:07:27 +0000 (15:07 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

src/store/user-profile/user-profile-actions.ts
src/store/users/users-actions.ts
src/views-components/dialog-forms/setup-shell-account-dialog.tsx [deleted file]
src/views-components/user-dialog/manage-dialog.tsx [deleted file]
src/views-components/user-dialog/setup-dialog.tsx [new file with mode: 0644]
src/views/user-profile-panel/user-profile-panel-root.tsx
src/views/user-profile-panel/user-profile-panel.tsx
src/views/workbench/workbench.tsx

index 0c6341f2d51cd35259d4d3566ee24b796901de4c..6042efa9984c1bcefdf28cbef94060ead45298b5 100644 (file)
@@ -15,6 +15,7 @@ import { dialogActions } from "store/dialog/dialog-actions";
 export const USER_PROFILE_PANEL_ID = 'userProfilePanel';
 export const USER_PROFILE_FORM = 'userProfileForm';
 export const DEACTIVATE_DIALOG = 'deactivateDialog';
+export const SETUP_DIALOG = 'setupDialog';
 
 export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID);
 
@@ -61,6 +62,34 @@ export const openDeactivateDialog = (uuid: string) =>
   }));
 }
 
+export const openSetupDialog = (uuid: string) =>
+  (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+    dispatch(dialogActions.OPEN_DIALOG({
+      id: SETUP_DIALOG,
+      data: {
+          title: 'Setup user',
+          text: 'Are you sure you want to setup this user?',
+          confirmButtonLabel: 'Confirm',
+          uuid
+      }
+  }));
+}
+
+
+export const setup = (uuid: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        try {
+            const resources = await services.userService.setup(uuid);
+            dispatch(updateResources(resources.items));
+            dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been setup", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+        } catch (e) {
+            dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
+        } finally {
+            dispatch(dialogActions.CLOSE_DIALOG({ id: SETUP_DIALOG }));
+        }
+
+    };
+
 export const unsetup = (uuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         try {
index 425a2d569d6e2b553e091134b3fa6f05af826d7b..b5d47d70caf07458cc82b10dad14f6be11e5bd4b 100644 (file)
@@ -8,14 +8,14 @@ import { RootState } from 'store/store';
 import { getUserUuid } from "common/getuser";
 import { ServiceRepository } from "services/services";
 import { dialogActions } from 'store/dialog/dialog-actions';
-import { startSubmit, reset, initialize, stopSubmit } from "redux-form";
+import { startSubmit, reset, stopSubmit } from "redux-form";
 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
 import { UserResource } from "models/user";
 import { getResource } from 'store/resources/resources';
 import { navigateTo, navigateToUsers, navigateToRootProject } from "store/navigation/navigation-action";
 import { authActions } from 'store/auth/auth-action';
 import { getTokenV2 } from "models/api-client-authorization";
-import { AddLoginFormData, VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD, VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD, VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD } from "store/virtual-machines/virtual-machines-actions";
+import { VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD, VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD } from "store/virtual-machines/virtual-machines-actions";
 import { PermissionLevel } from "models/permission";
 import { updateResources } from "store/resources/resources-actions";
 
@@ -23,7 +23,6 @@ export const USERS_PANEL_ID = 'usersPanel';
 export const USER_ATTRIBUTES_DIALOG = 'userAttributesDialog';
 export const USER_CREATE_FORM_NAME = 'userCreateFormName';
 export const USER_MANAGEMENT_DIALOG = 'userManageDialog';
-export const SETUP_SHELL_ACCOUNT_DIALOG = 'setupShellAccountDialog';
 
 export interface UserCreateFormDialogData {
     email: string;
@@ -47,15 +46,6 @@ export const openUserManagement = (uuid: string) =>
         dispatch(dialogActions.OPEN_DIALOG({ id: USER_MANAGEMENT_DIALOG, data }));
     };
 
-export const openSetupShellAccount = (uuid: string) =>
-    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const { resources } = getState();
-        const user = getResource<UserResource>(uuid)(resources);
-        const virtualMachines = await services.virtualMachineService.list();
-        dispatch(initialize(SETUP_SHELL_ACCOUNT_DIALOG, {[VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD]: user, [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: []}));
-        dispatch(dialogActions.OPEN_DIALOG({ id: SETUP_SHELL_ACCOUNT_DIALOG, data: virtualMachines }));
-    };
-
 export const loginAs = (uuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const { resources } = getState();
@@ -118,35 +108,6 @@ export const createUser = (data: UserCreateFormDialogData) =>
         }
     };
 
-export const setupUserVM = (setupData: AddLoginFormData) =>
-    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch(startSubmit(SETUP_SHELL_ACCOUNT_DIALOG));
-        try {
-            const userResource = await services.userService.get(setupData.user.uuid);
-
-            const resources = await services.userService.setup(setupData.user.uuid);
-            dispatch(updateResources(resources.items));
-
-            const permission = await services.permissionService.create({
-                headUuid: setupData.vmUuid,
-                tailUuid: userResource.uuid,
-                name: PermissionLevel.CAN_LOGIN,
-                properties: {
-                    username: userResource.username,
-                    groups: setupData.groups,
-                }
-            });
-            dispatch(updateResources([permission]));
-
-            dispatch(dialogActions.CLOSE_DIALOG({ id: SETUP_SHELL_ACCOUNT_DIALOG }));
-            dispatch(reset(SETUP_SHELL_ACCOUNT_DIALOG));
-            dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been added to VM.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
-        } catch (e) {
-            dispatch(stopSubmit(SETUP_SHELL_ACCOUNT_DIALOG));
-            dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
-        }
-    };
-
 export const openUserPanel = () =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const user = getState().auth.user;
diff --git a/src/views-components/dialog-forms/setup-shell-account-dialog.tsx b/src/views-components/dialog-forms/setup-shell-account-dialog.tsx
deleted file mode 100644 (file)
index 04eae12..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-import React from 'react';
-import { compose } from "redux";
-import { reduxForm, InjectedFormProps, Field } from 'redux-form';
-import { withDialog, WithDialogProps } from "store/dialog/with-dialog";
-import { FormDialog } from 'components/form-dialog/form-dialog';
-import { TextField } from 'components/text-field/text-field';
-import { VirtualMachinesResource } from 'models/virtual-machines';
-import { InputLabel } from '@material-ui/core';
-import { SETUP_SHELL_ACCOUNT_DIALOG, setupUserVM } from 'store/users/users-actions';
-import { UserResource } from 'models/user';
-import { VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD, AddLoginFormData } from 'store/virtual-machines/virtual-machines-actions';
-import { UserGroupsVirtualMachineField, RequiredUserVirtualMachineField } from 'views-components/form-fields/user-form-fields';
-
-export const SetupShellAccountDialog = compose(
-    withDialog(SETUP_SHELL_ACCOUNT_DIALOG),
-    reduxForm<AddLoginFormData>({
-        form: SETUP_SHELL_ACCOUNT_DIALOG,
-        onSubmit: (data, dispatch) => {
-            dispatch(setupUserVM(data));
-        }
-    })
-)(
-    (props: SetupShellAccountDialogComponentProps) =>
-        <FormDialog
-            dialogTitle='Setup shell account'
-            formFields={SetupShellAccountFormFields}
-            submitLabel='Submit'
-            {...props}
-        />
-);
-
-interface DataProps {
-    user: UserResource;
-    items: VirtualMachinesResource[];
-}
-
-const UserNameField = () =>
-    <span>
-        <InputLabel>VM Login</InputLabel>
-        <Field
-            name={`${VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD}.username`}
-            component={TextField as any}
-            disabled />
-    </span>;
-
-type SetupShellAccountDialogComponentProps = WithDialogProps<{}> & InjectedFormProps<AddLoginFormData>;
-
-const SetupShellAccountFormFields = (props: SetupShellAccountDialogComponentProps) =>
-    <>
-        <UserNameField />
-        <RequiredUserVirtualMachineField data={props.data as DataProps} />
-        <UserGroupsVirtualMachineField />
-    </>;
diff --git a/src/views-components/user-dialog/manage-dialog.tsx b/src/views-components/user-dialog/manage-dialog.tsx
deleted file mode 100644 (file)
index a62e1a2..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import React from "react";
-import { compose, Dispatch } from "redux";
-import { connect } from "react-redux";
-import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from "@material-ui/core";
-import { WithDialogProps } from "store/dialog/with-dialog";
-import { withDialog } from 'store/dialog/with-dialog';
-import { WithStyles, withStyles } from '@material-ui/core/styles';
-import { ArvadosTheme } from 'common/custom-theme';
-import { USER_MANAGEMENT_DIALOG } from "store/users/users-actions";
-import { openSetupShellAccount, loginAs } from 'store/users/users-actions';
-import { getUserDisplayName } from "models/user";
-
-type CssRules = 'spacing';
-
-const styles = withStyles<CssRules>((theme: ArvadosTheme) => ({
-    spacing: {
-        paddingBottom: theme.spacing.unit * 2,
-        paddingTop: theme.spacing.unit * 2,
-    }
-}));
-
-interface UserManageDataProps {
-    data: any;
-}
-
-interface UserManageActionProps {
-    openSetupShellAccount: (uuid: string) => void;
-    loginAs: (uuid: string) => void;
-}
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
-    openSetupShellAccount: (uuid: string) => dispatch<any>(openSetupShellAccount(uuid)),
-    loginAs: (uuid: string) => dispatch<any>(loginAs(uuid))
-});
-
-type UserManageProps = UserManageDataProps & UserManageActionProps & WithStyles<CssRules>;
-
-export const UserManageDialog = compose(
-    connect(null, mapDispatchToProps),
-    withDialog(USER_MANAGEMENT_DIALOG),
-    styles)(
-        (props: WithDialogProps<UserManageProps> & UserManageProps) =>
-            <Dialog open={props.open}
-                onClose={props.closeDialog}
-                fullWidth
-                maxWidth="md">
-                {props.data &&
-                    <span>
-                        <DialogTitle>{`Manage - ${getUserDisplayName(props.data)}`}</DialogTitle>
-                        <DialogContent>
-                            <Typography variant='body1' className={props.classes.spacing}>
-                                As an admin, you can log in as this user. When you’ve finished, you will need to log out and log in again with your own account.
-                    </Typography>
-                            <Button variant="contained" color="primary" onClick={() => props.loginAs(props.data.uuid)}>
-                                {`LOG IN AS ${getUserDisplayName(props.data)}`}
-                            </Button>
-                            <Typography variant='body1' className={props.classes.spacing}>
-                                As an admin, you can setup a shell account for this user. The login name is automatically generated from the user's e-mail address.
-                    </Typography>
-                            <Button variant="contained" color="primary" onClick={() => props.openSetupShellAccount(props.data.uuid)}>
-                                {`SETUP SHELL ACCOUNT FOR ${getUserDisplayName(props.data)}`}
-                            </Button>
-                        </DialogContent></span>}
-
-                <DialogActions>
-                    <Button
-                        variant='text'
-                        color='primary'
-                        onClick={props.closeDialog}>
-                        Close
-                </Button>
-                </DialogActions>
-            </Dialog>
-    );
diff --git a/src/views-components/user-dialog/setup-dialog.tsx b/src/views-components/user-dialog/setup-dialog.tsx
new file mode 100644 (file)
index 0000000..3a2fd35
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch, compose } from 'redux';
+import { connect } from "react-redux";
+import { ConfirmationDialog } from "components/confirmation-dialog/confirmation-dialog";
+import { withDialog, WithDialogProps } from "store/dialog/with-dialog";
+import { setup, SETUP_DIALOG } from 'store/user-profile/user-profile-actions';
+
+const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
+    onConfirm: () => {
+        props.closeDialog();
+        dispatch<any>(setup(props.data.uuid));
+    }
+});
+
+export const SetupDialog = compose(
+    withDialog(SETUP_DIALOG),
+    connect(null, mapDispatchToProps)
+)(ConfirmationDialog);
index 4572a35225fb6c0a829aeebcf74d7357f2fbe5d5..febe0ab9b96b8701f9f03a6b8c4541fb7652e675 100644 (file)
@@ -69,7 +69,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 });
 
 export interface UserProfilePanelRootActionProps {
-    openSetupShellAccount: (uuid: string) => void;
+    openSetupDialog: (uuid: string) => void;
     loginAs: (uuid: string) => void;
     openDeactivateDialog: (uuid: string) => void;
 }
@@ -305,7 +305,7 @@ export const UserProfilePanelRoot = withStyles(styles)(
                                     <Grid item sm={'auto'} xs={12}>
                                         <Button variant="contained"
                                             color="primary"
-                                            onClick={() => {this.props.openSetupShellAccount(this.props.initialValues.uuid)}}
+                                            onClick={() => {this.props.openSetupDialog(this.props.initialValues.uuid)}}
                                             disabled={false}>
                                             Setup Account
                                         </Button>
index 2bafd9faf9b45f57cea8cef8934e4f37265aebc1..e23b8bcea5af56bee2e7d8b7c271d0608cf30d4b 100644 (file)
@@ -8,11 +8,11 @@ import { reduxForm, isPristine, isValid } from 'redux-form';
 import { connect } from 'react-redux';
 import { saveEditedUser } from 'store/user-profile/user-profile-actions';
 import { UserProfilePanelRoot, UserProfilePanelRootDataProps } from 'views/user-profile-panel/user-profile-panel-root';
-import { openDeactivateDialog, USER_PROFILE_FORM } from "store/user-profile/user-profile-actions";
+import { openSetupDialog, openDeactivateDialog, USER_PROFILE_FORM } from "store/user-profile/user-profile-actions";
 import { matchUserProfileRoute } from 'routes/routes';
 import { UserResource } from 'models/user';
 import { getResource } from 'store/resources/resources';
-import { openSetupShellAccount, loginAs } from 'store/users/users-actions';
+import { loginAs } from 'store/users/users-actions';
 
 const mapStateToProps = (state: RootState): UserProfilePanelRootDataProps => {
   const pathname = state.router.location ? state.router.location.pathname : '';
@@ -32,7 +32,7 @@ const mapStateToProps = (state: RootState): UserProfilePanelRootDataProps => {
 }};
 
 const mapDispatchToProps = (dispatch: Dispatch) => ({
-    openSetupShellAccount: (uuid: string) => dispatch<any>(openSetupShellAccount(uuid)),
+    openSetupDialog: (uuid: string) => dispatch<any>(openSetupDialog(uuid)),
     loginAs: (uuid: string) => dispatch<any>(loginAs(uuid)),
     openDeactivateDialog: (uuid: string) => dispatch<any>(openDeactivateDialog(uuid)),
 });
index 1202529cbcd2039c3f6330cd778dd4a39493fbbb..11e038f535c47ebd6721d13866a598bfdd249f2d 100644 (file)
@@ -80,9 +80,8 @@ import { UserPanel } from 'views/user-panel/user-panel';
 import { UserAttributesDialog } from 'views-components/user-dialog/attributes-dialog';
 import { CreateUserDialog } from 'views-components/dialog-forms/create-user-dialog';
 import { HelpApiClientAuthorizationDialog } from 'views-components/api-client-authorizations-dialog/help-dialog';
-import { UserManageDialog } from 'views-components/user-dialog/manage-dialog';
 import { DeactivateDialog } from 'views-components/user-dialog/deactivate-dialog';
-import { SetupShellAccountDialog } from 'views-components/dialog-forms/setup-shell-account-dialog';
+import { SetupDialog } from 'views-components/user-dialog/setup-dialog';
 import { GroupsPanel } from 'views/groups-panel/groups-panel';
 import { RemoveGroupDialog } from 'views-components/groups-dialog/remove-dialog';
 import { GroupAttributesDialog } from 'views-components/groups-dialog/attributes-dialog';
@@ -261,7 +260,6 @@ export const WorkbenchPanel =
             <RepositoryAttributesDialog />
             <RepositoriesSampleGitDialog />
             <RichTextEditorDialog />
-            <SetupShellAccountDialog />
             <SharingDialog />
             <NotFoundDialog />
             <Snackbar />
@@ -269,8 +267,8 @@ export const WorkbenchPanel =
             <UpdateProcessDialog />
             <UpdateProjectDialog />
             <UserAttributesDialog />
-            <UserManageDialog />
             <DeactivateDialog />
+            <SetupDialog />
             <VirtualMachineAttributesDialog />
             <FedLogin />
             <WebDavS3InfoDialog />