creating-user-changing-is-admin-is-active-and-redirect-to-projects
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Thu, 6 Dec 2018 17:00:04 +0000 (18:00 +0100)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Thu, 6 Dec 2018 17:00:04 +0000 (18:00 +0100)
Feature #14504

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

src/store/project-panel/project-panel-middleware-service.ts
src/store/users/user-panel-middleware-service.ts
src/store/users/users-actions.ts
src/store/workbench/workbench-actions.ts
src/views-components/context-menu/action-sets/user-action-set.ts
src/views-components/data-explorer/renderers.tsx
src/views-components/dialog-create/dialog-user-create.tsx
src/views-components/form-fields/user-form-fields.tsx
src/views/user-panel/user-panel.tsx

index 36672e99ac19003ede9f9ade9240d870de496d14..257fc04477e86ded783a5a164879af4949772c80 100644 (file)
@@ -7,32 +7,32 @@ import {
     dataExplorerToListParams,
     getDataExplorerColumnFilters,
     listResultsToDataExplorerItemsMeta
-} from '../data-explorer/data-explorer-middleware-service';
-import { ProjectPanelColumnNames, ProjectPanelFilter } from "~/views/project-panel/project-panel";
-import { RootState } from "../store";
+} from '~/store/data-explorer/data-explorer-middleware-service';
+import { ProjectPanelColumnNames } from "~/views/project-panel/project-panel";
+import { RootState } from "~/store/store";
 import { DataColumns } from "~/components/data-table/data-table";
 import { ServiceRepository } from "~/services/services";
 import { SortDirection } from "~/components/data-table/data-column";
 import { OrderBuilder, OrderDirection } from "~/services/api/order-builder";
 import { FilterBuilder, joinFilters } from "~/services/api/filter-builder";
 import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/groups-service/groups-service";
-import { updateFavorites } from "../favorites/favorites-actions";
-import { PROJECT_PANEL_CURRENT_UUID, IS_PROJECT_PANEL_TRASHED, projectPanelActions } from './project-panel-action';
+import { updateFavorites } from "~/store/favorites/favorites-actions";
+import { PROJECT_PANEL_CURRENT_UUID, IS_PROJECT_PANEL_TRASHED, projectPanelActions } from '~/store/project-panel/project-panel-action';
 import { Dispatch, MiddlewareAPI } from "redux";
 import { ProjectResource } from "~/models/project";
 import { updateResources } from "~/store/resources/resources-actions";
 import { getProperty } from "~/store/properties/properties";
-import { snackbarActions, SnackbarKind } from '../snackbar/snackbar-actions';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions.ts';
-import { DataExplorer, getDataExplorer } from '../data-explorer/data-explorer-reducer';
+import { DataExplorer, getDataExplorer } from '~/store/data-explorer/data-explorer-reducer';
 import { ListResults } from '~/services/common-service/common-resource-service';
-import { loadContainers } from '../processes/processes-actions';
+import { loadContainers } from '~/store/processes/processes-actions';
 import { ResourceKind } from '~/models/resource';
 import { getResource } from "~/store/resources/resources";
 import { CollectionResource } from "~/models/collection";
 import { resourcesDataActions } from "~/store/resources-data/resources-data-actions";
 import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
-import { serializeResourceTypeFilters } from '../resource-type-filters/resource-type-filters';
+import { serializeResourceTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
 
 export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
index 590e160c9a984896727f9d73b976e8ab11609736..2f60209368ffc48afd266216e28f97398fc769ff 100644 (file)
@@ -44,7 +44,7 @@ export const getParams = (dataExplorer: DataExplorer) => ({
 
 export const getFilters = (dataExplorer: DataExplorer) => {
     const filters = new FilterBuilder()
-        .addILike("firstName", dataExplorer.searchValue)
+        .addILike("username", dataExplorer.searchValue)
         .getFilters();
     return filters;
 };
index ca6edd62ab3f7003138909db3216e13eb4f709f3..7494f9833e8fcc7be5791b1d3956f0e4f47da82f 100644 (file)
@@ -6,20 +6,13 @@ import { Dispatch } from "redux";
 import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
 import { RootState } from '~/store/store';
 import { ServiceRepository } from "~/services/services";
-import { navigateToUsers } from "~/store/navigation/navigation-action";
-import { unionize, ofType, UnionOf } from "~/common/unionize";
 import { dialogActions } from '~/store/dialog/dialog-actions';
 import { startSubmit, reset, stopSubmit } from "redux-form";
 import { getCommonResourceServiceError, CommonResourceServiceError } from "~/services/common-service/common-resource-service";
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
 import { UserResource } from "~/models/user";
 import { getResource } from '~/store/resources/resources';
-
-export const usersPanelActions = unionize({
-    SET_USERS: ofType<any>(),
-});
-
-export type UsersActions = UnionOf<typeof usersPanelActions>;
+import { navigateToProject } from "~/store/navigation/navigation-action";
 
 export const USERS_PANEL_ID = 'usersPanel';
 export const USER_ATTRIBUTES_DIALOG = 'userAttributesDialog';
@@ -45,10 +38,17 @@ export const openUserCreateDialog = () =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const userUuid = await services.authService.getUuid();
         const user = await services.userService.get(userUuid!);
+        const virtualMachines = await services.virtualMachineService.list();
         dispatch(reset(USER_CREATE_FORM_NAME));
-        dispatch(dialogActions.OPEN_DIALOG({ id: USER_CREATE_FORM_NAME, data: { user } }));
+        dispatch(dialogActions.OPEN_DIALOG({ id: USER_CREATE_FORM_NAME, data: { user, ...virtualMachines } }));
+    };
+
+export const openUserProjects = (uuid: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch<any>(navigateToProject(uuid));
     };
 
+
 export const createUser = (user: UserCreateFormDialogData) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         dispatch(startSubmit(USER_CREATE_FORM_NAME));
@@ -57,7 +57,7 @@ export const createUser = (user: UserCreateFormDialogData) =>
             dispatch(dialogActions.CLOSE_DIALOG({ id: USER_CREATE_FORM_NAME }));
             dispatch(reset(USER_CREATE_FORM_NAME));
             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been successfully created.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
-            dispatch<any>(loadUsersData());
+            dispatch<any>(loadUsersPanel());
             dispatch(userBindedActions.REQUEST_ITEMS());
             return newUser;
         } catch (e) {
@@ -69,17 +69,31 @@ export const createUser = (user: UserCreateFormDialogData) =>
         }
     };
 
-export const userBindedActions = bindDataExplorerActions(USERS_PANEL_ID);
+export const toggleIsActive = (uuid: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const { resources } = getState();
+        const data = getResource<UserResource>(uuid)(resources);
+        const isActive = data!.isActive;
+        const newActivity = await services.userService.update(uuid, { ...data, isActive: !isActive });
+        dispatch<any>(loadUsersPanel());
+        return newActivity;
+    };
 
-export const openUsersPanel = () =>
-    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch<any>(navigateToUsers);
+export const toggleIsAdmin = (uuid: string) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const { resources } = getState();
+        const data = getResource<UserResource>(uuid)(resources);
+        const isAdmin = data!.isAdmin;
+        const newActivity = await services.userService.update(uuid, { ...data, isAdmin: !isAdmin });
+        dispatch<any>(loadUsersPanel());
+        return newActivity;
     };
 
+export const userBindedActions = bindDataExplorerActions(USERS_PANEL_ID);
+
 export const loadUsersData = () =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const users = await services.userService.list();
-        dispatch(usersPanelActions.SET_USERS(users.items));
+        await services.userService.list();
     };
 
 export const loadUsersPanel = () =>
index 0d857d0988c1d13a00c78095148fea08945177ce..e7fbd1463b2158997f46af493368853e01608225 100644 (file)
@@ -130,7 +130,10 @@ export const loadProject = (uuid: string) =>
             const userUuid = services.authService.getUuid();
             dispatch(setIsProjectPanelTrashed(false));
             if (userUuid) {
-                if (userUuid !== uuid) {
+                if (extractUuidKind(uuid) === ResourceKind.USER && userUuid!==uuid) {
+                    // Load another users home projects
+                    dispatch(finishLoadingProject(uuid));
+                } else if (userUuid !== uuid) {
                     const match = await loadGroupContentsResource({ uuid, userUuid, services });
                     match({
                         OWNED: async project => {
index 68098cff1d50b6f5823850a08c0470cec4ac5f8c..7b0884e668b02ef9f3c5f30bf4c2b2eab3607873 100644 (file)
@@ -5,7 +5,7 @@
 import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
 import { AdvancedIcon, ProjectIcon, AttributesIcon, UserPanelIcon } from "~/components/icon/icon";
 import { openAdvancedTabDialog } from '~/store/advanced-tab/advanced-tab';
-import { openUserAttributes } from "~/store/users/users-actions";
+import { openUserAttributes, openUserProjects } from "~/store/users/users-actions";
 
 export const userActionSet: ContextMenuActionSet = [[{
     name: "Attributes",
@@ -17,7 +17,7 @@ export const userActionSet: ContextMenuActionSet = [[{
     name: "Project",
     icon: ProjectIcon,
     execute: (dispatch, { uuid }) => {
-        dispatch<any>(openAdvancedTabDialog(uuid));
+        dispatch<any>(openUserProjects(uuid));
     }
 }, {
     name: "Advanced",
index 20b2f9ec11ef08497c1f44b255b91a25651cc0ef..16ea7a995ec451b1fba9fdd5f0e9e23b60d0bfb1 100644 (file)
@@ -22,6 +22,7 @@ import { getUuidPrefix, openRunProcess } from '~/store/workflow-panel/workflow-p
 import { getResourceData } from "~/store/resources-data/resources-data";
 import { openSharingDialog } from '~/store/sharing-dialog/sharing-dialog-actions';
 import { UserResource } from '~/models/user';
+import { toggleIsActive, toggleIsAdmin } from '~/store/users/users-actions';
 
 const renderName = (item: { name: string; uuid: string, kind: string }) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -151,29 +152,31 @@ export const ResourceEmail = connect(
         return resource || { email: '' };
     })(renderEmail);
 
-const renderIsActive = (item: { isActive: boolean }) =>
+const renderIsActive = (props: { uuid: string, isActive: boolean, toggleIsActive: (uuid: string) => void }) =>
     <Checkbox
-        disableRipple
         color="primary"
-        checked={item.isActive} />;
+        checked={props.isActive}
+        onClick={() => props.toggleIsActive(props.uuid)} />;
 
 export const ResourceIsActive = connect(
     (state: RootState, props: { uuid: string }) => {
         const resource = getResource<UserResource>(props.uuid)(state.resources);
         return resource || { isActive: false };
-    })(renderIsActive);
+    }, { toggleIsActive }
+)(renderIsActive);
 
-const renderIsAdmin = (item: { isAdmin: boolean }) =>
+const renderIsAdmin = (props: { uuid: string, isAdmin: boolean, toggleIsAdmin: (uuid: string) => void }) =>
     <Checkbox
-        disableRipple
         color="primary"
-        checked={item.isAdmin} />;
+        checked={props.isAdmin}
+        onClick={() => props.toggleIsAdmin(props.uuid)} />;
 
 export const ResourceIsAdmin = connect(
     (state: RootState, props: { uuid: string }) => {
         const resource = getResource<UserResource>(props.uuid)(state.resources);
         return resource || { isAdmin: false };
-    })(renderIsAdmin);
+    }, { toggleIsAdmin }
+)(renderIsAdmin);
 
 const renderUsername = (item: { username: string }) =>
     <Typography noWrap>{item.username}</Typography>;
index bf135e8ce4a7b5fb028434c6fadbc78ebe188f30..80904397ec6b10ce386b3334b219232ff1ae8b19 100644 (file)
@@ -8,7 +8,7 @@ import { WithDialogProps } from '~/store/dialog/with-dialog';
 import { FormDialog } from '~/components/form-dialog/form-dialog';
 import { UserFirstNameField, UserLastNameField, UserEmailField, UserIdentityUrlField, UserVirtualMachineField, UserGroupsVirtualMachineField } from '~/views-components/form-fields/user-form-fields';
 
-type DialogUserProps = WithDialogProps<{}> & InjectedFormProps<any>;
+export type DialogUserProps = WithDialogProps<{}> & InjectedFormProps<any>;
 
 export const UserRepositoryCreate = (props: DialogUserProps) =>
     <FormDialog
@@ -18,11 +18,11 @@ export const UserRepositoryCreate = (props: DialogUserProps) =>
         {...props}
     />;
 
-const UserAddFields = () => <span>
+const UserAddFields = (props: DialogUserProps) => <span>
     <UserFirstNameField />
     <UserLastNameField />
     <UserEmailField />
     <UserIdentityUrlField />
-    <UserVirtualMachineField />
+    <UserVirtualMachineField data={props.data}/>
     <UserGroupsVirtualMachineField />
 </span>;
index 6dd635b5a8077f60631fc901ad3f26c2780cad8b..486d90f70932c9f7d7083f0caa3db6a146be9482 100644 (file)
@@ -7,6 +7,8 @@ import { Field } from "redux-form";
 import { TextField } from "~/components/text-field/text-field";
 import { USER_EMAIL_VALIDATION, USER_LENGTH_VALIDATION } from "~/validators/validators";
 import { NativeSelectField } from "~/components/select-field/select-field";
+import { InputLabel } from "@material-ui/core";
+import { VirtualMachinesResource } from "~/models/virtual-machines";
 
 export const UserFirstNameField = () =>
     <Field
@@ -39,16 +41,24 @@ export const UserIdentityUrlField = () =>
         validate={USER_LENGTH_VALIDATION}
         label="Identity URL Prefix" />;
 
-export const UserVirtualMachineField = () =>
-    <Field
-        name='virtualMachine'
-        component={NativeSelectField}
-        validate={USER_LENGTH_VALIDATION}
-        items={['shell']} />;
+export const UserVirtualMachineField = ({ data }: any) =>
+    <div style={{ marginBottom: '21px' }}>
+        <InputLabel>Virtual Machine</InputLabel>
+        <Field
+            name='virtualMachine'
+            component={NativeSelectField}
+            validate={USER_LENGTH_VALIDATION}
+            items={getVirtualMachinesList(data.items)} />
+    </div>;
 
 export const UserGroupsVirtualMachineField = () =>
     <Field
-        name='virtualMachine'
+        name='groups'
         component={TextField}
         validate={USER_LENGTH_VALIDATION}
-        label="Groups for virtual machine (comma separated list)" />;
\ No newline at end of file
+        label="Groups for virtual machine (comma separated list)" />;
+
+const getVirtualMachinesList = (virtualMachines: VirtualMachinesResource[]) => {
+    const mappedVirtualMachines = virtualMachines.map(it => ({ key: it.hostname, value: it.hostname }));
+    return mappedVirtualMachines;
+};
index db44d508295f1c34bbd91fbe58f9e7a7287b6951..f28cca3bf4242514fa0065135c11fe65a0ee9681 100644 (file)
@@ -159,7 +159,7 @@ export const UserPanel = compose(
                 return <Paper>
                     <Tabs value={value} onChange={this.handleChange} fullWidth>
                         <Tab label="USERS" />
-                        <Tab label="ACTIVITY" />
+                        <Tab label="ACTIVITY" disabled />
                     </Tabs>
                     {value === 0 &&
                         <span>