18559: Add tri-state account status indicator and associated context menu actions
authorStephen Smith <stephen@curii.com>
Wed, 30 Mar 2022 02:44:06 +0000 (22:44 -0400)
committerStephen Smith <stephen@curii.com>
Wed, 30 Mar 2022 02:45:19 +0000 (22:45 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

12 files changed:
src/components/icon/icon.tsx
src/models/group.ts
src/services/user-service/user-service.ts
src/store/auth/auth-action.ts
src/store/user-profile/user-profile-actions.ts
src/store/users/user-panel-middleware-service.ts
src/views-components/context-menu/action-sets/user-action-set.ts
src/views-components/data-explorer/renderers.tsx
src/views-components/user-dialog/activate-dialog.tsx [new file with mode: 0644]
src/views-components/user-dialog/deactivate-dialog.tsx
src/views/user-panel/user-panel.tsx
src/views/workbench/workbench.tsx

index 20ce62c12996bb0f90a3d25d5327b1cfe73a050a..4d17dd2863f6491a02540f0076b88d965e25cee2 100644 (file)
@@ -66,8 +66,10 @@ import LinkOutlined from '@material-ui/icons/LinkOutlined';
 import RemoveRedEye from '@material-ui/icons/RemoveRedEye';
 import Computer from '@material-ui/icons/Computer';
 import CropFreeSharp from '@material-ui/icons/CropFreeSharp';
 import RemoveRedEye from '@material-ui/icons/RemoveRedEye';
 import Computer from '@material-ui/icons/Computer';
 import CropFreeSharp from '@material-ui/icons/CropFreeSharp';
-import Cancel from '@material-ui/icons/Cancel';
 import ExitToApp from '@material-ui/icons/ExitToApp';
 import ExitToApp from '@material-ui/icons/ExitToApp';
+import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
+import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
+import NotInterested from '@material-ui/icons/NotInterested';
 
 // Import FontAwesome icons
 import { library } from '@fortawesome/fontawesome-svg-core';
 
 // Import FontAwesome icons
 import { library } from '@fortawesome/fontawesome-svg-core';
@@ -175,5 +177,8 @@ export const CanReadIcon: IconType = (props) => <RemoveRedEye {...props} />;
 export const CanWriteIcon: IconType = (props) => <Edit {...props} />;
 export const CanManageIcon: IconType = (props) => <Computer {...props} />;
 export const AddUserIcon: IconType = (props) => <PersonAdd {...props} />;
 export const CanWriteIcon: IconType = (props) => <Edit {...props} />;
 export const CanManageIcon: IconType = (props) => <Computer {...props} />;
 export const AddUserIcon: IconType = (props) => <PersonAdd {...props} />;
-export const DeactivateUserIcon: IconType = (props) => <Cancel {...props} />;
+export const DeactivateUserIcon: IconType = (props) => <NotInterested {...props} />;
 export const LoginAsIcon: IconType = (props) => <ExitToApp {...props} />;
 export const LoginAsIcon: IconType = (props) => <ExitToApp {...props} />;
+export const ActiveIcon: IconType = (props) => <CheckCircleOutline {...props} />;
+export const SetupIcon: IconType = (props) => <RemoveCircleOutline {...props} />;
+export const InactiveIcon: IconType = (props) => <NotInterested {...props} />;
index 3f3656ccd5fba90bcfb6a037c73ce0420c4c343c..f6a72c538f9481392aabac0883077cbf2086a6b4 100644 (file)
@@ -25,14 +25,18 @@ export enum GroupClass {
     ROLE  = 'role',
 }
 
     ROLE  = 'role',
 }
 
-export const BUILTIN_GROUP_IDS = [
-    'fffffffffffffff',
-    'anonymouspublic',
-    '000000000000000',
-]
+export enum BuiltinGroups {
+    ALL = 'fffffffffffffff',
+    ANON = 'anonymouspublic',
+    SYSTEM = '000000000000000',
+}
+
+export const getBuiltinGroupUuid = (cluster: string, groupName: BuiltinGroups): string => {
+    return cluster ? `${cluster}-${ResourceObjectType.GROUP}-${groupName}` : "";
+};
 
 export const isBuiltinGroup = (uuid: string) => {
     const match = RESOURCE_UUID_REGEX.exec(uuid);
     const parts = match ? match[0].split('-') : [];
 
 export const isBuiltinGroup = (uuid: string) => {
     const match = RESOURCE_UUID_REGEX.exec(uuid);
     const parts = match ? match[0].split('-') : [];
-    return parts.length === 3 && parts[1] === ResourceObjectType.GROUP && BUILTIN_GROUP_IDS.includes(parts[2]);
+    return parts.length === 3 && parts[1] === ResourceObjectType.GROUP && Object.values<string>(BuiltinGroups).includes(parts[2]);
 };
 };
index 97cb3c7112f5fb5f583d5f392437f0c9b02d4abe..8581b26766715fe57ce1d50657c8e9131f90daa4 100644 (file)
@@ -18,7 +18,7 @@ export class UserService extends CommonResourceService<UserResource> {
     }
 
     activate(uuid: string) {
     }
 
     activate(uuid: string) {
-        return CommonResourceService.defaultResponse(
+        return CommonResourceService.defaultResponse<UserResource>(
             this.serverApi
                 .post(this.resourceType + `/${uuid}/activate`),
             this.actions
             this.serverApi
                 .post(this.resourceType + `/${uuid}/activate`),
             this.actions
index d58a8103d28c1309d422f5cd4556c92cfbea3840..7fc9df774358a73d3d9448e604f83bf8693242c0 100644 (file)
@@ -80,6 +80,10 @@ export const getConfig = (dispatch: Dispatch, getState: () => RootState, service
     return state.remoteHostsConfig[state.localCluster];
 };
 
     return state.remoteHostsConfig[state.localCluster];
 };
 
+export const getLocalCluster = (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): string => {
+    return getState().auth.localCluster;
+};
+
 export const saveApiToken = (token: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<any> => {
     let config: any;
     const tokenParts = token.split('/');
 export const saveApiToken = (token: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<any> => {
     let config: any;
     const tokenParts = token.split('/');
index 0fdeef9e9ebc9af37ee46c664c1b02ad55dd393e..4d3d34aaf24969bfe7371b7cfff8f9545c1341a6 100644 (file)
@@ -9,13 +9,18 @@ import { bindDataExplorerActions } from "store/data-explorer/data-explorer-actio
 import { propertiesActions } from 'store/properties/properties-actions';
 import { getProperty } from 'store/properties/properties';
 import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
 import { propertiesActions } from 'store/properties/properties-actions';
 import { getProperty } from 'store/properties/properties';
 import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
-import { updateResources } from "store/resources/resources-actions";
+import { deleteResources, updateResources } from "store/resources/resources-actions";
 import { dialogActions } from "store/dialog/dialog-actions";
 import { dialogActions } from "store/dialog/dialog-actions";
+import { filterResources } from "store/resources/resources";
+import { ResourceKind } from "models/resource";
+import { LinkClass, LinkResource } from "models/link";
+import { BuiltinGroups, getBuiltinGroupUuid } from "models/group";
 
 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 USER_PROFILE_PANEL_ID = 'userProfilePanel';
 export const USER_PROFILE_FORM = 'userProfileForm';
 export const DEACTIVATE_DIALOG = 'deactivateDialog';
 export const SETUP_DIALOG = 'setupDialog';
+export const ACTIVATE_DIALOG = 'activateDialog';
 export const IS_PROFILE_INACCESSIBLE = 'isProfileInaccessible';
 
 export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID);
 export const IS_PROFILE_INACCESSIBLE = 'isProfileInaccessible';
 
 export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID);
@@ -65,60 +70,97 @@ export const saveEditedUser = (resource: any) =>
       }
   };
 
       }
   };
 
-export const openDeactivateDialog = (uuid: string) =>
+export const openSetupDialog = (uuid: string) =>
   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
     dispatch(dialogActions.OPEN_DIALOG({
   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
     dispatch(dialogActions.OPEN_DIALOG({
-      id: DEACTIVATE_DIALOG,
+      id: SETUP_DIALOG,
       data: {
       data: {
-          title: 'Deactivate user',
-          text: 'Are you sure you want to deactivate this user?',
-          confirmButtonLabel: 'Deactvate',
-          uuid
+        title: 'Setup user',
+        text: 'Are you sure you want to setup this user?',
+        confirmButtonLabel: 'Confirm',
+        uuid
       }
       }
-  }));
-}
+    }));
+  };
 
 
-export const openSetupDialog = (uuid: string) =>
+export const openActivateDialog = (uuid: string) =>
   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
     dispatch(dialogActions.OPEN_DIALOG({
   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
     dispatch(dialogActions.OPEN_DIALOG({
-      id: SETUP_DIALOG,
+      id: ACTIVATE_DIALOG,
+      data: {
+        title: 'Activate user',
+        text: 'Are you sure you want to activate this user?',
+        confirmButtonLabel: 'Confirm',
+        uuid
+      }
+    }));
+  };
+
+export const openDeactivateDialog = (uuid: string) =>
+  (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+    dispatch(dialogActions.OPEN_DIALOG({
+      id: DEACTIVATE_DIALOG,
       data: {
       data: {
-          title: 'Setup user',
-          text: 'Are you sure you want to setup this user?',
-          confirmButtonLabel: 'Confirm',
-          uuid
+        title: 'Deactivate user',
+        text: 'Are you sure you want to deactivate this user?',
+        confirmButtonLabel: 'Confirm',
+        uuid
       }
       }
-  }));
-}
+    }));
+  };
 
 export const setup = (uuid: string) =>
 
 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 }));
-        }
+  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 activate = (uuid: string) =>
+  async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+    try {
+      const user = await services.userService.activate(uuid);
+      dispatch(updateResources([user]));
+      dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been activated", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+    } catch (e) {
+      dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
+    }
+  };
 
 
-export const unsetup = (uuid: string) =>
-    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        try {
-          const user = await services.userService.unsetup(uuid);
-          dispatch(updateResources([user]));
-          dispatch(snackbarActions.OPEN_SNACKBAR({
-              message: "User has been deactivated.",
-              hideDuration: 2000,
-              kind: SnackbarKind.SUCCESS
-          }));
-        } catch (e) {
-          dispatch(snackbarActions.OPEN_SNACKBAR({
-              message: "Could not deactivate user",
-              kind: SnackbarKind.ERROR,
-          }));
-        }
-    };
+export const deactivate = (uuid: string) =>
+  async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+    try {
+      const { resources, auth } = getState();
+      // Call unsetup
+      const user = await services.userService.unsetup(uuid);
+      dispatch(updateResources([user]));
+
+      // Find and remove all users membership
+      const allUsersGroupUuid = getBuiltinGroupUuid(auth.localCluster, BuiltinGroups.ALL);
+      const memberships = filterResources((resource: LinkResource) =>
+          resource.kind === ResourceKind.LINK &&
+          resource.linkClass === LinkClass.PERMISSION &&
+          resource.headUuid === allUsersGroupUuid &&
+          resource.tailUuid === uuid
+      )(resources);
+      // Remove all users membership locally
+      dispatch<any>(deleteResources(memberships.map(link => link.uuid)));
+
+      dispatch(snackbarActions.OPEN_SNACKBAR({
+        message: "User has been deactivated.",
+        hideDuration: 2000,
+        kind: SnackbarKind.SUCCESS
+      }));
+    } catch (e) {
+      dispatch(snackbarActions.OPEN_SNACKBAR({
+        message: "Could not deactivate user",
+        kind: SnackbarKind.ERROR,
+      }));
+    }
+  };
index 2a7423535abc487a00f8a52351f8e342c9cc591f..83af302c9af449cc7582f1d2a57808313ce43bb5 100644 (file)
@@ -17,6 +17,8 @@ import { userBindedActions } from 'store/users/users-actions';
 import { getSortColumn } from "store/data-explorer/data-explorer-reducer";
 import { UserResource } from 'models/user';
 import { UserPanelColumnNames } from 'views/user-panel/user-panel';
 import { getSortColumn } from "store/data-explorer/data-explorer-reducer";
 import { UserResource } from 'models/user';
 import { UserPanelColumnNames } from 'views/user-panel/user-panel';
+import { BuiltinGroups, getBuiltinGroupUuid } from 'models/group';
+import { LinkClass } from 'models/link';
 
 export class UserMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
 
 export class UserMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -36,6 +38,15 @@ export class UserMiddlewareService extends DataExplorerMiddlewareService {
                 api.dispatch(updateResources(responseLastName.items));
                 api.dispatch(setItems(responseLastName));
             }
                 api.dispatch(updateResources(responseLastName.items));
                 api.dispatch(setItems(responseLastName));
             }
+            // Get "all users" group memberships
+            const allUsersGroupUuid = getBuiltinGroupUuid(state.auth.localCluster, BuiltinGroups.ALL);
+            const allUserMemberships = await this.services.permissionService.list({
+                filters: new FilterBuilder()
+                    .addEqual('head_uuid', allUsersGroupUuid)
+                    .addEqual('link_class', LinkClass.PERMISSION)
+                    .getFilters()
+            });
+            api.dispatch(updateResources(allUserMemberships.items));
         } catch {
             api.dispatch(couldNotFetchUsers());
         }
         } catch {
             api.dispatch(couldNotFetchUsers());
         }
index 18426a0d16221e735b782e8a2d3584e5118fea72..b01516e2dfed1be235a4027004e9040b09589941 100644 (file)
@@ -11,10 +11,11 @@ import {
     UserPanelIcon,
     LoginAsIcon,
     AdminMenuIcon,
     UserPanelIcon,
     LoginAsIcon,
     AdminMenuIcon,
+    ActiveIcon,
 } from "components/icon/icon";
 import { openAdvancedTabDialog } from 'store/advanced-tab/advanced-tab';
 import { loginAs, openUserAttributes, openUserProjects } from "store/users/users-actions";
 } from "components/icon/icon";
 import { openAdvancedTabDialog } from 'store/advanced-tab/advanced-tab';
 import { loginAs, openUserAttributes, openUserProjects } from "store/users/users-actions";
-import { openSetupDialog, openDeactivateDialog } from "store/user-profile/user-profile-actions";
+import { openSetupDialog, openDeactivateDialog, openActivateDialog } from "store/user-profile/user-profile-actions";
 import { navigateToUserProfile } from "store/navigation/navigation-action";
 
 export const userActionSet: ContextMenuActionSet = [[{
 import { navigateToUserProfile } from "store/navigation/navigation-action";
 
 export const userActionSet: ContextMenuActionSet = [[{
@@ -37,12 +38,17 @@ export const userActionSet: ContextMenuActionSet = [[{
     }
 }, {
     name: "Account Settings",
     }
 }, {
     name: "Account Settings",
-    adminOnly: true,
     icon: UserPanelIcon,
     execute: (dispatch, { uuid }) => {
         dispatch<any>(navigateToUserProfile(uuid));
     }
     icon: UserPanelIcon,
     execute: (dispatch, { uuid }) => {
         dispatch<any>(navigateToUserProfile(uuid));
     }
-}, {
+},], [{
+    name: "Activate User",
+    icon: ActiveIcon,
+    execute: (dispatch, { uuid }) => {
+        dispatch<any>(openActivateDialog(uuid));
+    }
+},{
     name: "Setup User",
     adminOnly: true,
     icon: AdminMenuIcon,
     name: "Setup User",
     adminOnly: true,
     icon: AdminMenuIcon,
index 5068355b01c32bb21234662566458df8a6d2fbc2..70508628d2d83965ea48278688de3ec097a1c60b 100644 (file)
@@ -6,7 +6,21 @@ import React from 'react';
 import { Grid, Typography, withStyles, Tooltip, IconButton, Checkbox } from '@material-ui/core';
 import { FavoriteStar, PublicFavoriteStar } from '../favorite-star/favorite-star';
 import { Resource, ResourceKind, TrashableResource } from 'models/resource';
 import { Grid, Typography, withStyles, Tooltip, IconButton, Checkbox } from '@material-ui/core';
 import { FavoriteStar, PublicFavoriteStar } from '../favorite-star/favorite-star';
 import { Resource, ResourceKind, TrashableResource } from 'models/resource';
-import { ProjectIcon, FilterGroupIcon, CollectionIcon, ProcessIcon, DefaultIcon, ShareIcon, CollectionOldVersionIcon, WorkflowIcon, RemoveIcon, RenameIcon } from 'components/icon/icon';
+import {
+    ProjectIcon,
+    FilterGroupIcon,
+    CollectionIcon,
+    ProcessIcon,
+    DefaultIcon,
+    ShareIcon,
+    CollectionOldVersionIcon,
+    WorkflowIcon,
+    RemoveIcon,
+    RenameIcon,
+    ActiveIcon,
+    SetupIcon,
+    InactiveIcon,
+} from 'components/icon/icon';
 import { formatDate, formatFileSize, formatTime } from 'common/formatters';
 import { resourceLabel } from 'common/labels';
 import { connect, DispatchProp } from 'react-redux';
 import { formatDate, formatFileSize, formatTime } from 'common/formatters';
 import { resourceLabel } from 'common/labels';
 import { connect, DispatchProp } from 'react-redux';
@@ -28,7 +42,7 @@ import { withResourceData } from 'views-components/data-explorer/with-resources'
 import { CollectionResource } from 'models/collection';
 import { IllegalNamingWarning } from 'components/warning/warning';
 import { loadResource } from 'store/resources/resources-actions';
 import { CollectionResource } from 'models/collection';
 import { IllegalNamingWarning } from 'components/warning/warning';
 import { loadResource } from 'store/resources/resources-actions';
-import { GroupClass, GroupResource, isBuiltinGroup } from 'models/group';
+import { BuiltinGroups, getBuiltinGroupUuid, GroupClass, GroupResource, isBuiltinGroup } from 'models/group';
 import { openRemoveGroupMemberDialog } from 'store/group-details-panel/group-details-panel-actions';
 import { setMemberIsHidden } from 'store/group-details-panel/group-details-panel-actions';
 import { formatPermissionLevel } from 'views-components/sharing-dialog/permission-select';
 import { openRemoveGroupMemberDialog } from 'store/group-details-panel/group-details-panel-actions';
 import { setMemberIsHidden } from 'store/group-details-panel/group-details-panel-actions';
 import { formatPermissionLevel } from 'views-components/sharing-dialog/permission-select';
@@ -219,6 +233,55 @@ export const ResourceIsActive = connect(
     }, { toggleIsActive }
 )(renderIsActive);
 
     }, { toggleIsActive }
 )(renderIsActive);
 
+enum UserAccountStatus {
+    ACTIVE = 'Active',
+    INACTIVE = 'Inactive',
+    SETUP = 'Setup',
+    UNKNOWN = 'UNKNOWN'
+}
+
+const renderAccountStatus = (props: {status: UserAccountStatus}) =>
+    <Grid container alignItems="center" wrap="nowrap" spacing={8}>
+        <Grid item>
+            {(() => {
+                switch(props.status) {
+                    case UserAccountStatus.ACTIVE:
+                        return <ActiveIcon style={{color: '#4caf50'}} />;
+                    case UserAccountStatus.SETUP:
+                        return <SetupIcon style={{color: '#2196f3'}} />;
+                    case UserAccountStatus.INACTIVE:
+                        return <InactiveIcon style={{color: '#9e9e9e'}} />;
+                    default:
+                        return <InactiveIcon />;
+                }
+            })()}
+        </Grid>
+        <Grid item>
+            <Typography noWrap>
+                {props.status}
+            </Typography>
+        </Grid>
+    </Grid>;
+
+export const UserResourceAccountStatus = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const user = getResource<UserResource>(props.uuid)(state.resources);
+        // Get membership links for all users group
+        const allUsersGroupUuid = getBuiltinGroupUuid(state.auth.localCluster, BuiltinGroups.ALL);
+        const permissions = filterResources((resource: LinkResource) =>
+            resource.kind === ResourceKind.LINK &&
+            resource.linkClass === LinkClass.PERMISSION &&
+            resource.headUuid === allUsersGroupUuid &&
+            resource.tailUuid === props.uuid
+        )(state.resources);
+
+        if (user) {
+            return user.isActive ? {status: UserAccountStatus.ACTIVE} : permissions.length > 0 ? {status: UserAccountStatus.SETUP} : {status: UserAccountStatus.INACTIVE};
+        } else {
+            return {status: UserAccountStatus.UNKNOWN};
+        }
+    })(renderAccountStatus);
+
 export const ResourceLinkTailIsActive = connect(
     (state: RootState, props: { uuid: string, disabled?: boolean }) => {
         const link = getResource<LinkResource>(props.uuid)(state.resources);
 export const ResourceLinkTailIsActive = connect(
     (state: RootState, props: { uuid: string, disabled?: boolean }) => {
         const link = getResource<LinkResource>(props.uuid)(state.resources);
diff --git a/src/views-components/user-dialog/activate-dialog.tsx b/src/views-components/user-dialog/activate-dialog.tsx
new file mode 100644 (file)
index 0000000..79e8330
--- /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 { activate, ACTIVATE_DIALOG } from 'store/user-profile/user-profile-actions';
+
+const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
+    onConfirm: () => {
+        props.closeDialog();
+        dispatch<any>(activate(props.data.uuid));
+    }
+});
+
+export const ActivateDialog = compose(
+    withDialog(ACTIVATE_DIALOG),
+    connect(null, mapDispatchToProps)
+)(ConfirmationDialog);
index 6babf367eb6cc7c142d38f3a46a5b5dc85cfeb87..8aefa9299dc7cf98a456f3ab4cb32d271325e8fa 100644 (file)
@@ -6,12 +6,12 @@ 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 { connect } from "react-redux";
 import { ConfirmationDialog } from "components/confirmation-dialog/confirmation-dialog";
 import { withDialog, WithDialogProps } from "store/dialog/with-dialog";
-import { unsetup, DEACTIVATE_DIALOG } from 'store/user-profile/user-profile-actions';
+import { deactivate, DEACTIVATE_DIALOG } from 'store/user-profile/user-profile-actions';
 
 const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
     onConfirm: () => {
         props.closeDialog();
 
 const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
     onConfirm: () => {
         props.closeDialog();
-        dispatch<any>(unsetup(props.data.uuid));
+        dispatch<any>(deactivate(props.data.uuid));
     }
 });
 
     }
 });
 
index 169b32abcdc22039d7d2b007e6c6c054bc5c3c40..589353cd880ad91557f80969e6fe1233e818fc2f 100644 (file)
@@ -15,9 +15,9 @@ import {
     UserResourceFullName,
     ResourceUuid,
     ResourceEmail,
     UserResourceFullName,
     ResourceUuid,
     ResourceEmail,
-    ResourceIsActive,
     ResourceIsAdmin,
     ResourceIsAdmin,
-    ResourceUsername
+    ResourceUsername,
+    UserResourceAccountStatus,
 } from "views-components/data-explorer/renderers";
 import { navigateToUserProfile } from "store/navigation/navigation-action";
 import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view';
 } from "views-components/data-explorer/renderers";
 import { navigateToUserProfile } from "store/navigation/navigation-action";
 import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view';
@@ -46,7 +46,7 @@ export enum UserPanelColumnNames {
     NAME = "Name",
     UUID = "Uuid",
     EMAIL = "Email",
     NAME = "Name",
     UUID = "Uuid",
     EMAIL = "Email",
-    ACTIVE = "Active",
+    STATUS = "Account Status",
     ADMIN = "Admin",
     REDIRECT_TO_USER = "Redirect to user",
     USERNAME = "Username"
     ADMIN = "Admin",
     REDIRECT_TO_USER = "Redirect to user",
     USERNAME = "Username"
@@ -78,11 +78,11 @@ export const userPanelColumns: DataColumns<string> = [
         render: uuid => <ResourceEmail uuid={uuid} />
     },
     {
         render: uuid => <ResourceEmail uuid={uuid} />
     },
     {
-        name: UserPanelColumnNames.ACTIVE,
+        name: UserPanelColumnNames.STATUS,
         selected: true,
         configurable: true,
         filters: createTree(),
         selected: true,
         configurable: true,
         filters: createTree(),
-        render: uuid => <ResourceIsActive uuid={uuid} />
+        render: uuid => <UserResourceAccountStatus uuid={uuid} />
     },
     {
         name: UserPanelColumnNames.ADMIN,
     },
     {
         name: UserPanelColumnNames.ADMIN,
index 11e038f535c47ebd6721d13866a598bfdd249f2d..0d1a89503f1b52e2de3315f8e2882e2ddc5461fe 100644 (file)
@@ -81,6 +81,7 @@ import { UserAttributesDialog } from 'views-components/user-dialog/attributes-di
 import { CreateUserDialog } from 'views-components/dialog-forms/create-user-dialog';
 import { HelpApiClientAuthorizationDialog } from 'views-components/api-client-authorizations-dialog/help-dialog';
 import { DeactivateDialog } from 'views-components/user-dialog/deactivate-dialog';
 import { CreateUserDialog } from 'views-components/dialog-forms/create-user-dialog';
 import { HelpApiClientAuthorizationDialog } from 'views-components/api-client-authorizations-dialog/help-dialog';
 import { DeactivateDialog } from 'views-components/user-dialog/deactivate-dialog';
+import { ActivateDialog } from 'views-components/user-dialog/activate-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 { 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';
@@ -268,6 +269,7 @@ export const WorkbenchPanel =
             <UpdateProjectDialog />
             <UserAttributesDialog />
             <DeactivateDialog />
             <UpdateProjectDialog />
             <UserAttributesDialog />
             <DeactivateDialog />
+            <ActivateDialog />
             <SetupDialog />
             <VirtualMachineAttributesDialog />
             <FedLogin />
             <SetupDialog />
             <VirtualMachineAttributesDialog />
             <FedLogin />