19231: Add smaller page sizes (10 and 20 items) to load faster
[arvados-workbench2.git] / src / store / user-profile / user-profile-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4 import { RootState } from "store/store";
5 import { Dispatch } from 'redux';
6 import { initialize, reset } from "redux-form";
7 import { ServiceRepository } from "services/services";
8 import { bindDataExplorerActions } from "store/data-explorer/data-explorer-action";
9 import { propertiesActions } from 'store/properties/properties-actions';
10 import { getProperty } from 'store/properties/properties';
11 import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
12 import { deleteResources, updateResources } from "store/resources/resources-actions";
13 import { dialogActions } from "store/dialog/dialog-actions";
14 import { filterResources } from "store/resources/resources";
15 import { ResourceKind } from "models/resource";
16 import { LinkClass, LinkResource } from "models/link";
17 import { BuiltinGroups, getBuiltinGroupUuid } from "models/group";
18
19 export const USER_PROFILE_PANEL_ID = 'userProfilePanel';
20 export const USER_PROFILE_FORM = 'userProfileForm';
21 export const DEACTIVATE_DIALOG = 'deactivateDialog';
22 export const SETUP_DIALOG = 'setupDialog';
23 export const ACTIVATE_DIALOG = 'activateDialog';
24 export const IS_PROFILE_INACCESSIBLE = 'isProfileInaccessible';
25
26 export const UserProfileGroupsActions = bindDataExplorerActions(USER_PROFILE_PANEL_ID);
27
28 export const getCurrentUserProfilePanelUuid = getProperty<string>(USER_PROFILE_PANEL_ID);
29 export const getUserProfileIsInaccessible = getProperty<boolean>(IS_PROFILE_INACCESSIBLE);
30
31 export const loadUserProfilePanel = (userUuid?: string) =>
32   async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
33     // Reset isInacessible to ensure error screen is hidden
34     dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: false }));
35     // Get user uuid from route or use current user uuid
36     const uuid = userUuid || getState().auth.user?.uuid;
37     if (uuid) {
38       await dispatch(propertiesActions.SET_PROPERTY({ key: USER_PROFILE_PANEL_ID, value: uuid }));
39       try {
40         const user = await services.userService.get(uuid, false);
41         dispatch(initialize(USER_PROFILE_FORM, user));
42         dispatch(updateResources([user]));
43         dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
44       } catch (e) {
45         if (e.status === 404) {
46           await dispatch(propertiesActions.SET_PROPERTY({ key: IS_PROFILE_INACCESSIBLE, value: true }));
47           dispatch(reset(USER_PROFILE_FORM));
48         } else {
49           dispatch(snackbarActions.OPEN_SNACKBAR({
50             message: 'Could not load user profile',
51             kind: SnackbarKind.ERROR
52           }));
53         }
54       }
55     }
56   }
57
58 export const saveEditedUser = (resource: any) =>
59   async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
60       try {
61           const user = await services.userService.update(resource.uuid, resource);
62           dispatch(updateResources([user]));
63           dispatch(initialize(USER_PROFILE_FORM, user));
64           dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Profile has been updated.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
65       } catch (e) {
66           dispatch(snackbarActions.OPEN_SNACKBAR({
67               message: "Could not update profile",
68               kind: SnackbarKind.ERROR,
69           }));
70       }
71   };
72
73 export const openSetupDialog = (uuid: string) =>
74   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
75     dispatch(dialogActions.OPEN_DIALOG({
76       id: SETUP_DIALOG,
77       data: {
78         title: 'Setup user',
79         text: 'Are you sure you want to setup this user?',
80         confirmButtonLabel: 'Confirm',
81         uuid
82       }
83     }));
84   };
85
86 export const openActivateDialog = (uuid: string) =>
87   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
88     dispatch(dialogActions.OPEN_DIALOG({
89       id: ACTIVATE_DIALOG,
90       data: {
91         title: 'Activate user',
92         text: 'Are you sure you want to activate this user?',
93         confirmButtonLabel: 'Confirm',
94         uuid
95       }
96     }));
97   };
98
99 export const openDeactivateDialog = (uuid: string) =>
100   (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
101     dispatch(dialogActions.OPEN_DIALOG({
102       id: DEACTIVATE_DIALOG,
103       data: {
104         title: 'Deactivate user',
105         text: 'Are you sure you want to deactivate this user?',
106         confirmButtonLabel: 'Confirm',
107         uuid
108       }
109     }));
110   };
111
112 export const setup = (uuid: string) =>
113   async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
114     try {
115       const resources = await services.userService.setup(uuid);
116       dispatch(updateResources(resources.items));
117
118       // Refresh data explorer
119       dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
120
121       dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been setup", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
122     } catch (e) {
123       dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
124     } finally {
125       dispatch(dialogActions.CLOSE_DIALOG({ id: SETUP_DIALOG }));
126     }
127   };
128
129 export const activate = (uuid: string) =>
130   async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
131     try {
132       const user = await services.userService.activate(uuid);
133       dispatch(updateResources([user]));
134
135       // Refresh data explorer
136       dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
137
138       dispatch(snackbarActions.OPEN_SNACKBAR({ message: "User has been activated", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
139     } catch (e) {
140       dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
141     }
142   };
143
144 export const deactivate = (uuid: string) =>
145   async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
146     try {
147       const { resources, auth } = getState();
148       // Call unsetup
149       const user = await services.userService.unsetup(uuid);
150       dispatch(updateResources([user]));
151
152       // Find and remove all users membership
153       const allUsersGroupUuid = getBuiltinGroupUuid(auth.localCluster, BuiltinGroups.ALL);
154       const memberships = filterResources((resource: LinkResource) =>
155           resource.kind === ResourceKind.LINK &&
156           resource.linkClass === LinkClass.PERMISSION &&
157           resource.headUuid === allUsersGroupUuid &&
158           resource.tailUuid === uuid
159       )(resources);
160       // Remove all users membership locally
161       dispatch<any>(deleteResources(memberships.map(link => link.uuid)));
162
163       // Refresh data explorer
164       dispatch(UserProfileGroupsActions.REQUEST_ITEMS());
165
166       dispatch(snackbarActions.OPEN_SNACKBAR({
167         message: "User has been deactivated.",
168         hideDuration: 2000,
169         kind: SnackbarKind.SUCCESS
170       }));
171     } catch (e) {
172       dispatch(snackbarActions.OPEN_SNACKBAR({
173         message: "Could not deactivate user",
174         kind: SnackbarKind.ERROR,
175       }));
176     }
177   };