Connected users with Kratos

This commit is contained in:
Valentino 2021-12-21 14:55:46 +00:00 committed by Maarten de Waard
parent b0af0de05b
commit 8da937d0c5
22 changed files with 479 additions and 228 deletions

View file

@ -1,16 +1,41 @@
import { useDispatch, useSelector } from 'react-redux';
import { getUsers, fetchUsers } from '../redux';
import { getUsers, fetchUsers, fetchUserById, updateUserById, createUser, deleteUser } from '../redux';
import { getUserById, getUserModalLoading } from '../redux/selectors';
export function useUsers() {
const dispatch = useDispatch();
const users = useSelector(getUsers);
const user = useSelector(getUserById);
const userModalLoading = useSelector(getUserModalLoading);
function loadUsers() {
return dispatch(fetchUsers());
}
function loadUser(id: string) {
return dispatch(fetchUserById(id));
}
function editUserById(data: any) {
return dispatch(updateUserById(data));
}
function createNewUser(data: any) {
return dispatch(createUser(data));
}
function deleteUserById(id: string) {
return dispatch(deleteUser(id));
}
return {
users,
user,
loadUser,
loadUsers,
editUserById,
userModalLoading,
createNewUser,
deleteUserById,
};
}

View file

@ -1,6 +1,6 @@
export * from './types';
export { reducer, fetchCurrentUser, getCurrentUser } from './redux';
export { reducer } from './redux';
export { useUsers } from './hooks';

View file

@ -1,64 +1,132 @@
import { createApiAction, createCrudApiActions } from 'src/services/api';
import { transformUserForApi } from '../transformations';
import { User } from '../types';
import { CurrentUserUpdateAPI } from './types';
import { Dispatch } from 'redux';
import { showToast, ToastType } from 'src/common/util/show-toast';
import { performApiCall } from 'src/services/api';
import { transformRequestUser, transformResponseUser } from '../transformations';
export enum UserActionTypes {
CHANGE_CURRENT_USER_START = 'users/fetch_current_user_start',
CHANGE_CURRENT_USER_FAILURE = 'users/fetch_current_user_failure',
FETCH_CURRENT_USER_SUCCESS = 'users/fetch_current_user_success',
UPDATE_CURRENT_USER_SUCCESS = 'users/update_current_user_success',
CHANGE_USERS_START = 'users/change_users_start',
CHANGE_USERS_FAILURE = 'users/change_users_failure',
FETCH_USERS_SUCCESS = 'users/fetch_users_success',
ADD_ACCOUNT_USER_SUCCESS = 'users/add_account_user_success',
UPDATE_ACCOUNT_USER_SUCCESS = 'users/update_account_user_success',
DELETE_ACCOUNT_USER_SUCCESS = 'users/delete_account_user_success',
CHANGE_USER_ROLES_START = 'users/change_user_roles_start',
CHANGE_USER_ROLES_FAILURE = 'users/change_user_roles_failure',
FETCH_USER_ROLES_SUCCESS = 'users/fetch_user_roles_success',
ADD_USER_ROLE_SUCCESS = 'users/add_user_role_success',
UPDATE_USER_ROLE_SUCCESS = 'users/update_user_role_success',
DELETE_USER_ROLE_SUCCESS = 'users/delete_user_role_success',
FETCH_USERS = 'users/fetch_users',
FETCH_USER = 'users/fetch_user',
UPDATE_USER = 'users/update_user',
CREATE_USER = 'users/create_user',
DELETE_USER = 'users/delete_user',
SET_USER_MODAL_LOADING = 'users/user_modal_loading',
}
export const fetchCurrentUser = () =>
createApiAction(
{
path: '/users/me',
export const fetchUsers = () => async (dispatch: Dispatch<any>) => {
try {
const { data } = await performApiCall({
path: '/users',
method: 'GET',
},
[
UserActionTypes.CHANGE_CURRENT_USER_START,
UserActionTypes.FETCH_CURRENT_USER_SUCCESS,
UserActionTypes.CHANGE_CURRENT_USER_FAILURE,
],
);
});
export const updateCurrentUser = (data: CurrentUserUpdateAPI) =>
createApiAction(
{
path: '/users/me',
method: 'PATCH',
body: data,
},
[
UserActionTypes.CHANGE_CURRENT_USER_START,
UserActionTypes.UPDATE_CURRENT_USER_SUCCESS,
UserActionTypes.CHANGE_CURRENT_USER_FAILURE,
],
);
dispatch({
type: UserActionTypes.FETCH_USERS,
payload: data.map(transformResponseUser),
});
} catch (err) {
console.error(err);
}
};
export const [fetchUsers, addUser, updateUser, deleteUser] = createCrudApiActions<User>(
'/users',
UserActionTypes.CHANGE_USERS_START,
UserActionTypes.CHANGE_USERS_FAILURE,
UserActionTypes.FETCH_USERS_SUCCESS,
UserActionTypes.ADD_ACCOUNT_USER_SUCCESS,
UserActionTypes.UPDATE_ACCOUNT_USER_SUCCESS,
UserActionTypes.DELETE_ACCOUNT_USER_SUCCESS,
transformUserForApi,
);
export const setUserModalLoading = (isLoading: boolean) => (dispatch: Dispatch<any>) => {
dispatch({
type: UserActionTypes.SET_USER_MODAL_LOADING,
payload: isLoading,
});
};
export const fetchUserById = (id: string) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));
try {
const { data } = await performApiCall({
path: `/users/${id}`,
method: 'GET',
});
dispatch({
type: UserActionTypes.FETCH_USER,
payload: transformResponseUser(data),
});
} catch (err) {
console.error(err);
}
dispatch(setUserModalLoading(false));
};
export const updateUserById = (user: any) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));
try {
const { data } = await performApiCall({
path: `/users/${user.id}`,
method: 'PUT',
body: transformRequestUser(user),
});
dispatch({
type: UserActionTypes.UPDATE_USER,
payload: transformResponseUser(data),
});
showToast('User updated sucessfully.', ToastType.Success);
dispatch(fetchUsers());
} catch (err) {
console.error(err);
}
dispatch(setUserModalLoading(false));
};
export const createUser = (user: any) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));
try {
const { data } = await performApiCall({
path: '/users',
method: 'POST',
body: transformRequestUser(user),
});
dispatch({
type: UserActionTypes.CREATE_USER,
payload: transformResponseUser(data),
});
showToast('User created sucessfully.', ToastType.Success);
dispatch(fetchUsers());
} catch (err: any) {
dispatch(setUserModalLoading(false));
showToast(`${err}`, ToastType.Error);
throw err;
}
dispatch(setUserModalLoading(false));
};
export const deleteUser = (id: string) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));
try {
await performApiCall({
path: `/users/${id}`,
method: 'DELETE',
});
dispatch({
type: UserActionTypes.DELETE_USER,
payload: {},
});
showToast('User deleted sucessfully.', ToastType.Success);
dispatch(fetchUsers());
} catch (err) {
console.error(err);
}
dispatch(setUserModalLoading(false));
};

View file

@ -1,4 +1,4 @@
export * from './actions';
export { default as reducer } from './reducers';
export { getCurrentUser, getUsers } from './selectors';
export { getUsers } from './selectors';
export * from './types';

View file

@ -1,61 +1,39 @@
import { combineReducers } from 'redux';
import { createApiReducer, chainReducers, INITIAL_API_STATE, createCrudApiReducer } from 'src/services/api';
import { AuthActionTypes } from 'src/services/auth/redux/actions';
import { transformUser } from '../transformations';
import { User } from '../types';
import { UserActionTypes } from './actions';
const signInReducer = createApiReducer(
[AuthActionTypes.SIGN_IN_START, AuthActionTypes.SIGN_IN_SUCCESS, AuthActionTypes.SIGN_IN_FAILURE],
transformUser,
(data) => data,
);
const initialUsersState: any = {
users: [],
user: {},
userModalLoading: false,
};
const fetchCurrentUserReducer = createApiReducer(
[
UserActionTypes.CHANGE_CURRENT_USER_START,
UserActionTypes.FETCH_CURRENT_USER_SUCCESS,
UserActionTypes.CHANGE_CURRENT_USER_FAILURE,
],
transformUser,
(data) => data,
);
const usersReducer = (state: any = initialUsersState, action: any) => {
switch (action.type) {
case UserActionTypes.FETCH_USERS:
return {
...state,
users: action.payload,
};
case UserActionTypes.SET_USER_MODAL_LOADING:
return {
...state,
userModalLoading: action.payload,
};
case UserActionTypes.FETCH_USER:
case UserActionTypes.UPDATE_USER:
case UserActionTypes.CREATE_USER:
return {
...state,
isModalVisible: false,
user: action.payload,
};
case UserActionTypes.DELETE_USER:
return {
...state,
user: {},
};
default:
return state;
}
};
const updateCurrentUserReducer = createApiReducer(
[
UserActionTypes.CHANGE_CURRENT_USER_START,
UserActionTypes.UPDATE_CURRENT_USER_SUCCESS,
UserActionTypes.CHANGE_CURRENT_USER_FAILURE,
],
transformUser,
(data) => data,
);
const signOutReducer = createApiReducer(
['', AuthActionTypes.SIGN_OUT, ''],
() => INITIAL_API_STATE,
() => INITIAL_API_STATE,
);
const usersReducer = createCrudApiReducer<User>(
UserActionTypes.CHANGE_USERS_START,
UserActionTypes.CHANGE_USERS_FAILURE,
UserActionTypes.FETCH_USERS_SUCCESS,
UserActionTypes.ADD_ACCOUNT_USER_SUCCESS,
UserActionTypes.UPDATE_ACCOUNT_USER_SUCCESS,
UserActionTypes.DELETE_ACCOUNT_USER_SUCCESS,
transformUser,
);
export default combineReducers({
currentUser: chainReducers(
INITIAL_API_STATE,
signInReducer,
fetchCurrentUserReducer,
updateCurrentUserReducer,
signOutReducer,
),
users: usersReducer,
});
export default usersReducer;

View file

@ -1,4 +1,5 @@
import { State } from 'src/redux';
export const getCurrentUser = (state: State) => state.users.currentUser;
export const getUsers = (state: State) => state.users.users;
export const getUserById = (state: State) => state.users.user;
export const getUserModalLoading = (state: State) => state.users.userModalLoading;

View file

@ -1,4 +1,4 @@
import { ApiState, ApiStatus } from 'src/services/api/redux';
import { ApiStatus } from 'src/services/api/redux';
import { User } from '../types';
@ -8,10 +8,13 @@ export interface CurrentUserState extends User {
export interface UsersState {
currentUser: CurrentUserState;
users: ApiState<User>;
users: User[];
user: User;
userModalLoading: boolean;
}
export interface CurrentUserUpdateAPI {
id: number;
phoneNumber?: string;
email?: string;
language?: string;

View file

@ -1,14 +1,17 @@
import _ from 'lodash';
import { FormUser, User, UserApiRequest } from './types';
import { User } from './types';
export const transformUserForApi = (user: FormUser): UserApiRequest => ({
id: user.id,
email: user.email,
name: user.name,
status: user.status,
last_login: user.last_login,
});
export const transformResponseUser = (response: any): User => {
const userResponse = _.get(response, 'user', response);
return {
id: userResponse.id,
email: userResponse.traits.email,
name: userResponse.traits.name ?? null,
status: userResponse.state,
};
};
export const transformUser = (response: any): User => {
const userResponse = _.get(response, 'user', response);
@ -17,7 +20,13 @@ export const transformUser = (response: any): User => {
id: userResponse.id,
email: userResponse.email,
name: userResponse.name,
last_login: userResponse.last_login,
status: userResponse.status,
};
};
export const transformRequestUser = (data: any) => {
return {
email: data.email,
name: data.name,
};
};

View file

@ -1,9 +1,8 @@
export interface User {
id: number | null;
email: string;
name: string;
last_login: string;
status: string;
email: string | null;
name: string | null;
status: string | null;
}
export interface FormUser extends User {
@ -21,6 +20,5 @@ export interface UserApiRequest {
id: number | null;
email: string;
name: string;
last_login: string;
status: string;
}