@truedat/auth 8.14.2 → 8.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/messages/en.js +6 -5
- package/src/messages/es.js +8 -7
- package/src/styles/UserCard.less +582 -0
- package/src/styles/UserModals.less +826 -0
- package/src/styles/Users.less +280 -0
- package/src/users/components/AdminUserRoutes.js +7 -20
- package/src/users/components/EditUser.js +7 -34
- package/src/users/components/EditUserModal.js +150 -0
- package/src/users/components/NewUser.js +128 -17
- package/src/users/components/Password.js +11 -189
- package/src/users/components/UserAcls.js +56 -6
- package/src/users/components/UserActions.js +99 -34
- package/src/users/components/UserCard.js +168 -22
- package/src/users/components/UserCards.js +273 -35
- package/src/users/components/UserFormFields.js +206 -0
- package/src/users/components/UserGroupAclRow.js +8 -2
- package/src/users/components/UserGroupAcls.js +2 -2
- package/src/users/components/UserGroupLink.js +21 -0
- package/src/users/components/UserInfoModal.js +268 -0
- package/src/users/components/UserPasswordField.js +66 -0
- package/src/users/components/UserPasswordModal.js +210 -0
- package/src/users/components/UserRoutes.js +4 -13
- package/src/users/components/UserTabs.js +5 -2
- package/src/users/components/UserTypeSelector.js +109 -0
- package/src/users/components/UsersAndGroups.js +39 -5
- package/src/users/components/UsersTable.js +234 -0
- package/src/users/components/__tests__/EditUser.spec.js +84 -10
- package/src/users/components/__tests__/NewUser.spec.js +34 -4
- package/src/users/components/__tests__/Password.spec.js +53 -146
- package/src/users/components/__tests__/UserAcls.spec.js +23 -2
- package/src/users/components/__tests__/UserCard.spec.js +111 -5
- package/src/users/components/__tests__/UserCards.spec.js +194 -0
- package/src/users/components/__tests__/UserGroupAclRow.spec.js +19 -1
- package/src/users/components/__tests__/UserModals.spec.js +507 -0
- package/src/users/components/__tests__/UserRoutes.spec.js +8 -3
- package/src/users/components/__tests__/UserTypeSelector.spec.js +32 -0
- package/src/users/components/__tests__/UsersAndGroups.spec.js +17 -0
- package/src/users/components/__tests__/__snapshots__/UserCard.spec.js.snap +64 -37
- package/src/users/components/__tests__/__snapshots__/UserCards.spec.js.snap +424 -75
- package/src/users/components/__tests__/__snapshots__/UserGroupAclRow.spec.js.snap +7 -3
- package/src/users/components/__tests__/__snapshots__/UserRoutes.spec.js.snap +4 -13
- package/src/users/hooks/useSortedGroups.js +13 -0
- package/src/users/hooks/useUserModalOperation.js +55 -0
- package/src/users/reducers/__tests__/userRedirect.spec.js +14 -3
- package/src/users/reducers/index.js +1 -0
- package/src/users/reducers/userModalOperation.js +29 -0
- package/src/users/reducers/userRedirect.js +5 -4
- package/src/users/sagas/__tests__/updateUser.spec.js +19 -1
- package/src/users/sagas/createUser.js +12 -5
- package/src/users/sagas/deleteUser.js +12 -5
- package/src/users/sagas/updateUser.js +19 -8
- package/src/users/selectors/__tests__/filterUsers.spec.js +11 -0
- package/src/users/selectors/__tests__/getUserAcls.spec.js +1 -0
- package/src/users/selectors/__tests__/getUserGroupAcls.spec.js +2 -0
- package/src/users/selectors/filterUsers.js +9 -1
- package/src/users/selectors/getUserGroupAcls.js +1 -0
- package/src/users/components/User.js +0 -68
- package/src/users/components/UserPassword.js +0 -24
- package/src/users/components/__tests__/User.spec.js +0 -20
- package/src/users/components/__tests__/UserPassword.spec.js +0 -24
- package/src/users/components/__tests__/__snapshots__/EditUser.spec.js.snap +0 -281
- package/src/users/components/__tests__/__snapshots__/NewUser.spec.js.snap +0 -321
- package/src/users/components/__tests__/__snapshots__/Password.spec.js.snap +0 -128
- package/src/users/components/__tests__/__snapshots__/User.spec.js.snap +0 -129
- package/src/users/components/__tests__/__snapshots__/UserPassword.spec.js.snap +0 -124
|
@@ -71,10 +71,7 @@ exports[`<UserRoutes /> renders correctly with users/:id route 1`] = `
|
|
|
71
71
|
UsersLoader
|
|
72
72
|
</div>
|
|
73
73
|
<div>
|
|
74
|
-
|
|
75
|
-
</div>
|
|
76
|
-
<div>
|
|
77
|
-
User
|
|
74
|
+
UsersAndGroups
|
|
78
75
|
</div>
|
|
79
76
|
</div>
|
|
80
77
|
`;
|
|
@@ -85,10 +82,7 @@ exports[`<UserRoutes /> renders correctly with users/:id/edit route 1`] = `
|
|
|
85
82
|
UsersLoader
|
|
86
83
|
</div>
|
|
87
84
|
<div>
|
|
88
|
-
|
|
89
|
-
</div>
|
|
90
|
-
<div>
|
|
91
|
-
EditUser
|
|
85
|
+
UsersAndGroups
|
|
92
86
|
</div>
|
|
93
87
|
</div>
|
|
94
88
|
`;
|
|
@@ -99,10 +93,7 @@ exports[`<UserRoutes /> renders correctly with users/:id/password route 1`] = `
|
|
|
99
93
|
UsersLoader
|
|
100
94
|
</div>
|
|
101
95
|
<div>
|
|
102
|
-
|
|
103
|
-
</div>
|
|
104
|
-
<div>
|
|
105
|
-
UserPassword
|
|
96
|
+
UsersAndGroups
|
|
106
97
|
</div>
|
|
107
98
|
</div>
|
|
108
99
|
`;
|
|
@@ -113,7 +104,7 @@ exports[`<UserRoutes /> renders correctly with users/new route 1`] = `
|
|
|
113
104
|
UsersLoader
|
|
114
105
|
</div>
|
|
115
106
|
<div>
|
|
116
|
-
|
|
107
|
+
UsersAndGroups
|
|
117
108
|
</div>
|
|
118
109
|
</div>
|
|
119
110
|
`;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { useGroups } from "../../groups/hooks/useGroups";
|
|
4
|
+
|
|
5
|
+
const caseInsensitiveName = ({ name }) => _.lowerCase(name);
|
|
6
|
+
|
|
7
|
+
// Group options for the user forms, sorted as the former Redux `groups` state was.
|
|
8
|
+
export const useSortedGroups = () => {
|
|
9
|
+
const { groups } = useGroups();
|
|
10
|
+
return useMemo(() => _.sortBy(caseInsensitiveName)(groups), [groups]);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default useSortedGroups;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useEffect, useId, useRef } from "react";
|
|
2
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import { clearUserModalOperation } from "../reducers/userModalOperation";
|
|
5
|
+
|
|
6
|
+
export default function useUserModalOperation(
|
|
7
|
+
routine,
|
|
8
|
+
onClose,
|
|
9
|
+
onSuccess = onClose,
|
|
10
|
+
) {
|
|
11
|
+
const id = useId();
|
|
12
|
+
const dispatch = useDispatch();
|
|
13
|
+
const { formatMessage } = useIntl();
|
|
14
|
+
const request = useRef(0);
|
|
15
|
+
const pending = useRef(false);
|
|
16
|
+
const operation = useSelector((state) => state.userModalOperation?.[id]);
|
|
17
|
+
const isSubmitting = operation?.status === "pending";
|
|
18
|
+
|
|
19
|
+
useEffect(() => () => dispatch(clearUserModalOperation(id)), [dispatch, id]);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (operation?.requestId !== request.current || !pending.current) return;
|
|
23
|
+
if (operation.status === "error") pending.current = false;
|
|
24
|
+
if (operation.status === "success") {
|
|
25
|
+
pending.current = false;
|
|
26
|
+
onSuccess();
|
|
27
|
+
}
|
|
28
|
+
}, [operation, onSuccess]);
|
|
29
|
+
|
|
30
|
+
const submit = (payload) => {
|
|
31
|
+
if (pending.current) return;
|
|
32
|
+
pending.current = true;
|
|
33
|
+
request.current += 1;
|
|
34
|
+
dispatch({
|
|
35
|
+
...routine(payload),
|
|
36
|
+
meta: { userModalId: id, requestId: request.current },
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
submit,
|
|
42
|
+
isSubmitting,
|
|
43
|
+
onClose: () => {
|
|
44
|
+
if (!pending.current) onClose();
|
|
45
|
+
},
|
|
46
|
+
error:
|
|
47
|
+
operation?.status === "error"
|
|
48
|
+
? formatMessage({
|
|
49
|
+
id: "user.modal.operation.error",
|
|
50
|
+
defaultMessage:
|
|
51
|
+
"The operation could not be completed. Please try again.",
|
|
52
|
+
})
|
|
53
|
+
: null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { linkTo } from "@truedat/core/routes";
|
|
2
1
|
import { clearRedirect } from "@truedat/core/routines";
|
|
3
2
|
import { createUser, deleteUser, updateUser } from "../../routines";
|
|
4
3
|
import { userRedirect } from "..";
|
|
@@ -27,12 +26,24 @@ describe("reducers: userRedirect", () => {
|
|
|
27
26
|
|
|
28
27
|
it("should handle the updateUser.SUCCESS action", () => {
|
|
29
28
|
const payload = { data: { id: "1" } };
|
|
30
|
-
const url = linkTo.USER({ id: "1" });
|
|
31
29
|
expect(userRedirect("foo", { type: updateUser.SUCCESS, payload })).toBe(
|
|
32
|
-
|
|
30
|
+
"/users/1",
|
|
33
31
|
);
|
|
34
32
|
});
|
|
35
33
|
|
|
34
|
+
it.each([createUser.SUCCESS, updateUser.SUCCESS])(
|
|
35
|
+
"preserves the redirect for modal operation %s",
|
|
36
|
+
(type) => {
|
|
37
|
+
expect(
|
|
38
|
+
userRedirect("foo", {
|
|
39
|
+
type,
|
|
40
|
+
payload: { data: { id: "1" } },
|
|
41
|
+
meta: { userModalId: "modal", requestId: 1 },
|
|
42
|
+
}),
|
|
43
|
+
).toBe("foo");
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
36
47
|
it("should ignore unknown actions", () => {
|
|
37
48
|
expect(userRedirect("foo", { type: "bar", payload })).toEqual("foo");
|
|
38
49
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import {
|
|
3
|
+
createUser,
|
|
4
|
+
updateUser,
|
|
5
|
+
updatePassword,
|
|
6
|
+
deleteUser,
|
|
7
|
+
} from "../routines";
|
|
8
|
+
|
|
9
|
+
export const clearUserModalOperation = (id) => ({
|
|
10
|
+
type: "CLEAR_USER_MODAL_OPERATION",
|
|
11
|
+
payload: id,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const userModalOperation = (state = {}, { type, meta, payload }) => {
|
|
15
|
+
if (type === "CLEAR_USER_MODAL_OPERATION") return _.omit([payload])(state);
|
|
16
|
+
if (!meta?.userModalId) return state;
|
|
17
|
+
const { userModalId, requestId } = meta;
|
|
18
|
+
const routines = [createUser, updateUser, updatePassword, deleteUser];
|
|
19
|
+
if (routines.some((routine) => routine.TRIGGER === type)) {
|
|
20
|
+
return { ...state, [userModalId]: { requestId, status: "pending" } };
|
|
21
|
+
}
|
|
22
|
+
if (state[userModalId]?.requestId !== requestId) return state;
|
|
23
|
+
const status = routines.some((routine) => routine.SUCCESS === type)
|
|
24
|
+
? "success"
|
|
25
|
+
: routines.some((routine) => routine.FAILURE === type)
|
|
26
|
+
? "error"
|
|
27
|
+
: null;
|
|
28
|
+
return status ? { ...state, [userModalId]: { requestId, status } } : state;
|
|
29
|
+
};
|
|
@@ -5,15 +5,16 @@ import { createUser, deleteUser, updateUser } from "../routines";
|
|
|
5
5
|
|
|
6
6
|
const initialState = "";
|
|
7
7
|
|
|
8
|
-
export const userRedirect = (state = initialState, { type, payload }) => {
|
|
8
|
+
export const userRedirect = (state = initialState, { type, payload, meta }) => {
|
|
9
9
|
switch (type) {
|
|
10
10
|
case clearRedirect.TRIGGER:
|
|
11
11
|
return initialState;
|
|
12
12
|
case createUser.SUCCESS:
|
|
13
|
-
return USERS;
|
|
13
|
+
return meta?.userModalId ? state : USERS;
|
|
14
14
|
case updateUser.SUCCESS:
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
return meta?.userModalId
|
|
16
|
+
? state
|
|
17
|
+
: linkTo.USER({ ..._.get("data")(payload) });
|
|
17
18
|
case deleteUser.SUCCESS:
|
|
18
19
|
return state;
|
|
19
20
|
default:
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
updatePasswordSaga,
|
|
8
8
|
} from "../updateUser";
|
|
9
9
|
import { updateUser, updatePassword } from "../../routines";
|
|
10
|
-
import { API_USER } from "../../api";
|
|
10
|
+
import { API_USER, API_USER_CHANGE_PASSWORD } from "../../api";
|
|
11
11
|
|
|
12
12
|
describe("sagas: updateUserRequestSaga", () => {
|
|
13
13
|
it("should invoke updateUserSaga on updateUser.TRIGGER", () => {
|
|
@@ -94,3 +94,21 @@ describe("sagas: updateUserSaga", () => {
|
|
|
94
94
|
}).not.toThrow();
|
|
95
95
|
});
|
|
96
96
|
});
|
|
97
|
+
|
|
98
|
+
it("changes the current password without requesting the administrative user list", () => {
|
|
99
|
+
const payload = {
|
|
100
|
+
user: { old_password: "old-example", new_password: "new-example" },
|
|
101
|
+
};
|
|
102
|
+
const meta = { userModalId: "self-password", requestId: 1 };
|
|
103
|
+
testSaga(updatePasswordSaga, { payload, meta })
|
|
104
|
+
.next()
|
|
105
|
+
.put(updatePassword.request())
|
|
106
|
+
.next()
|
|
107
|
+
.call(apiJsonPut, API_USER_CHANGE_PASSWORD, payload, JSON_OPTS)
|
|
108
|
+
.next({ data: {} })
|
|
109
|
+
.put({ ...updatePassword.success({}), meta })
|
|
110
|
+
.next()
|
|
111
|
+
.put(updatePassword.fulfill())
|
|
112
|
+
.next()
|
|
113
|
+
.isDone();
|
|
114
|
+
});
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { call, put, takeLatest } from "redux-saga/effects";
|
|
2
2
|
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
-
import { createUser } from "../routines";
|
|
3
|
+
import { createUser, fetchUsers } from "../routines";
|
|
4
4
|
import { API_USERS } from "../api";
|
|
5
5
|
|
|
6
|
-
export function* createUserSaga({ payload: user }) {
|
|
6
|
+
export function* createUserSaga({ payload: user, meta }) {
|
|
7
7
|
try {
|
|
8
8
|
const url = API_USERS;
|
|
9
9
|
const requestData = { user };
|
|
10
10
|
yield put(createUser.request());
|
|
11
11
|
const { data } = yield call(apiJsonPost, url, requestData, JSON_OPTS);
|
|
12
|
-
yield put(createUser.success(data));
|
|
12
|
+
yield put({ ...createUser.success(data), ...(meta ? { meta } : {}) });
|
|
13
|
+
if (meta?.userModalId) yield put(fetchUsers());
|
|
13
14
|
} catch (error) {
|
|
14
15
|
if (error.response) {
|
|
15
16
|
const { status, data } = error.response;
|
|
16
|
-
yield put(
|
|
17
|
+
yield put({
|
|
18
|
+
...createUser.failure({ status, data }),
|
|
19
|
+
...(meta ? { meta } : {}),
|
|
20
|
+
});
|
|
17
21
|
} else {
|
|
18
|
-
yield put(
|
|
22
|
+
yield put({
|
|
23
|
+
...createUser.failure(error.message),
|
|
24
|
+
...(meta ? { meta } : {}),
|
|
25
|
+
});
|
|
19
26
|
}
|
|
20
27
|
} finally {
|
|
21
28
|
yield put(createUser.fulfill());
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import { compile } from "path-to-regexp";
|
|
2
2
|
import { call, put, takeLatest } from "redux-saga/effects";
|
|
3
3
|
import { apiJsonDelete, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
-
import { deleteUser } from "../routines";
|
|
4
|
+
import { deleteUser, fetchUsers } from "../routines";
|
|
5
5
|
import { API_USER } from "../api";
|
|
6
6
|
|
|
7
7
|
const toApiPath = compile(API_USER);
|
|
8
8
|
|
|
9
|
-
export function* deleteUserSaga({ payload }) {
|
|
9
|
+
export function* deleteUserSaga({ payload, meta }) {
|
|
10
10
|
try {
|
|
11
11
|
const user = payload;
|
|
12
12
|
const url = toApiPath({ id: `${user.id}` });
|
|
13
13
|
yield put(deleteUser.request());
|
|
14
14
|
const { data } = yield call(apiJsonDelete, url, JSON_OPTS);
|
|
15
|
-
yield put(deleteUser.success(data));
|
|
15
|
+
yield put({ ...deleteUser.success(data), ...(meta ? { meta } : {}) });
|
|
16
|
+
if (meta?.userModalId) yield put(fetchUsers());
|
|
16
17
|
} catch (error) {
|
|
17
18
|
if (error.response) {
|
|
18
19
|
const { status, data } = error.response;
|
|
19
|
-
yield put(
|
|
20
|
+
yield put({
|
|
21
|
+
...deleteUser.failure({ status, data }),
|
|
22
|
+
...(meta ? { meta } : {}),
|
|
23
|
+
});
|
|
20
24
|
} else {
|
|
21
|
-
yield put(
|
|
25
|
+
yield put({
|
|
26
|
+
...deleteUser.failure(error.message),
|
|
27
|
+
...(meta ? { meta } : {}),
|
|
28
|
+
});
|
|
22
29
|
}
|
|
23
30
|
} finally {
|
|
24
31
|
yield put(deleteUser.fulfill());
|
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
import { compile } from "path-to-regexp";
|
|
2
2
|
import { all, call, put, takeLatest } from "redux-saga/effects";
|
|
3
3
|
import { apiJsonPut, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
-
import { updateUser, updatePassword } from "../routines";
|
|
4
|
+
import { updateUser, updatePassword, fetchUsers } from "../routines";
|
|
5
5
|
import { API_USER, API_USER_CHANGE_PASSWORD } from "../api";
|
|
6
6
|
|
|
7
|
-
export function* updateUserSaga({ payload: user }) {
|
|
7
|
+
export function* updateUserSaga({ payload: user, meta }) {
|
|
8
8
|
try {
|
|
9
9
|
const url = compile(API_USER)({ id: `${user.id}` });
|
|
10
10
|
const requestData = { user };
|
|
11
11
|
yield put(updateUser.request());
|
|
12
12
|
const { data } = yield call(apiJsonPut, url, requestData, JSON_OPTS);
|
|
13
|
-
yield put(updateUser.success(data));
|
|
13
|
+
yield put({ ...updateUser.success(data), ...(meta ? { meta } : {}) });
|
|
14
|
+
if (meta?.userModalId) yield put(fetchUsers());
|
|
14
15
|
} catch (error) {
|
|
15
16
|
if (error.response) {
|
|
16
17
|
const { status, data } = error.response;
|
|
17
|
-
yield put(
|
|
18
|
+
yield put({
|
|
19
|
+
...updateUser.failure({ status, data }),
|
|
20
|
+
...(meta ? { meta } : {}),
|
|
21
|
+
});
|
|
18
22
|
} else {
|
|
19
|
-
yield put(
|
|
23
|
+
yield put({
|
|
24
|
+
...updateUser.failure(error.message),
|
|
25
|
+
...(meta ? { meta } : {}),
|
|
26
|
+
});
|
|
20
27
|
}
|
|
21
28
|
} finally {
|
|
22
29
|
yield put(updateUser.fulfill());
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
export function* updatePasswordSaga({ payload }) {
|
|
33
|
+
export function* updatePasswordSaga({ payload, meta }) {
|
|
27
34
|
try {
|
|
28
35
|
const url = API_USER_CHANGE_PASSWORD;
|
|
29
36
|
yield put(updatePassword.request());
|
|
30
37
|
const { data } = yield call(apiJsonPut, url, payload, JSON_OPTS);
|
|
31
|
-
yield put(updatePassword.success(data));
|
|
38
|
+
yield put({ ...updatePassword.success(data), ...(meta ? { meta } : {}) });
|
|
39
|
+
if (meta?.userModalId && payload.user.id) yield put(fetchUsers());
|
|
32
40
|
} catch (error) {
|
|
33
|
-
yield put(
|
|
41
|
+
yield put({
|
|
42
|
+
...updatePassword.failure(error.message),
|
|
43
|
+
...(meta ? { meta } : {}),
|
|
44
|
+
});
|
|
34
45
|
} finally {
|
|
35
46
|
yield put(updatePassword.fulfill());
|
|
36
47
|
}
|
|
@@ -42,3 +42,14 @@ describe("selectors: filterUsers", () => {
|
|
|
42
42
|
expect(filteredUsers).toEqual(expect.arrayContaining([miguel]));
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
it.each([" ext-1 ", "calidad", "data"])(
|
|
47
|
+
"searches external IDs and group names/aliases: %s",
|
|
48
|
+
(query) => {
|
|
49
|
+
const user = {
|
|
50
|
+
external_id: "EXT-1",
|
|
51
|
+
groups: [{ name: "data", alias: "Calidád" }],
|
|
52
|
+
};
|
|
53
|
+
expect(filterUsers(query)([user, { user_name: "other" }])).toEqual([user]);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
@@ -28,11 +28,13 @@ describe("selectors: getUserGroupAcls", () => {
|
|
|
28
28
|
resource: aclWithAlias.resource,
|
|
29
29
|
role: aclWithAlias.role.name,
|
|
30
30
|
group: aclWithAlias.group.alias,
|
|
31
|
+
groupId: aclWithAlias.group.id,
|
|
31
32
|
},
|
|
32
33
|
{
|
|
33
34
|
resource: aclWithoutAlias.resource,
|
|
34
35
|
role: aclWithoutAlias.role.name,
|
|
35
36
|
group: aclWithoutAlias.group.name,
|
|
37
|
+
groupId: aclWithoutAlias.group.id,
|
|
36
38
|
},
|
|
37
39
|
]);
|
|
38
40
|
});
|
|
@@ -5,7 +5,15 @@ export const filterUsers = (usersFilter) =>
|
|
|
5
5
|
lowerDeburrTrim(usersFilter)
|
|
6
6
|
? _.filter(
|
|
7
7
|
_.flow(
|
|
8
|
-
|
|
8
|
+
(user) => [
|
|
9
|
+
user.user_name,
|
|
10
|
+
user.full_name,
|
|
11
|
+
user.email,
|
|
12
|
+
user.external_id,
|
|
13
|
+
...(user.groups || []).flatMap((group) =>
|
|
14
|
+
typeof group === "string" ? [group] : [group.name, group.alias],
|
|
15
|
+
),
|
|
16
|
+
],
|
|
9
17
|
_.map(lowerDeburrTrim),
|
|
10
18
|
_.some(_.includes(lowerDeburrTrim(usersFilter))),
|
|
11
19
|
),
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
|
-
import { Divider, Header, Grid, Icon, Segment } from "semantic-ui-react";
|
|
4
|
-
import { connect } from "react-redux";
|
|
5
|
-
import { FormattedMessage, useIntl } from "react-intl";
|
|
6
|
-
import UserActions from "./UserActions";
|
|
7
|
-
import UserAcls from "./UserAcls";
|
|
8
|
-
import UserBreadcrumbs from "./UserBreadcrumbs";
|
|
9
|
-
import UserGroupAcls from "./UserGroupAcls";
|
|
10
|
-
|
|
11
|
-
export const User = ({ user }) => {
|
|
12
|
-
const { formatMessage } = useIntl();
|
|
13
|
-
return _.isEmpty(user) ? null : (
|
|
14
|
-
<>
|
|
15
|
-
<UserBreadcrumbs name={user.user_name} />
|
|
16
|
-
<Segment>
|
|
17
|
-
<Grid>
|
|
18
|
-
<Grid.Column width={8}>
|
|
19
|
-
<Header floated="left" as="h2">
|
|
20
|
-
<Icon
|
|
21
|
-
circular
|
|
22
|
-
name={formatMessage({
|
|
23
|
-
id: "users.header.icon",
|
|
24
|
-
defaultMessage: "users",
|
|
25
|
-
})}
|
|
26
|
-
/>
|
|
27
|
-
<Header.Content>
|
|
28
|
-
{user.user_name}
|
|
29
|
-
<Header.Subheader>
|
|
30
|
-
<FormattedMessage id={`user.type.${user?.role}`} />
|
|
31
|
-
</Header.Subheader>
|
|
32
|
-
</Header.Content>
|
|
33
|
-
</Header>
|
|
34
|
-
</Grid.Column>
|
|
35
|
-
<Grid.Column width={8} textAlign="right">
|
|
36
|
-
<UserActions />
|
|
37
|
-
</Grid.Column>
|
|
38
|
-
<Grid.Column width={8}>
|
|
39
|
-
{user.external_id == null ? (
|
|
40
|
-
user.external_id
|
|
41
|
-
) : (
|
|
42
|
-
<>
|
|
43
|
-
{user.external_id}
|
|
44
|
-
<br />
|
|
45
|
-
</>
|
|
46
|
-
)}
|
|
47
|
-
{user.full_name}
|
|
48
|
-
<br />
|
|
49
|
-
{user.email}
|
|
50
|
-
<br />
|
|
51
|
-
{_.join(" ")(user.groups)}
|
|
52
|
-
<Divider hidden />
|
|
53
|
-
<UserAcls />
|
|
54
|
-
<UserGroupAcls />
|
|
55
|
-
</Grid.Column>
|
|
56
|
-
</Grid>
|
|
57
|
-
</Segment>
|
|
58
|
-
</>
|
|
59
|
-
);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
User.propTypes = {
|
|
63
|
-
user: PropTypes.object,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const mapStateToProps = ({ user }) => ({ user });
|
|
67
|
-
|
|
68
|
-
export default connect(mapStateToProps)(User);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import { connect } from "react-redux";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
import { useIntl } from "react-intl";
|
|
5
|
-
import UserBreadcrumbs from "./UserBreadcrumbs";
|
|
6
|
-
import Password from "./Password";
|
|
7
|
-
|
|
8
|
-
export const UserPassword = ({ user }) => {
|
|
9
|
-
const { formatMessage } = useIntl();
|
|
10
|
-
return (
|
|
11
|
-
<>
|
|
12
|
-
<UserBreadcrumbs name={formatMessage({ id: "user.password.change" })} />
|
|
13
|
-
<Password id={_.get("id")(user)} userName={_.get("user_name")(user)} />
|
|
14
|
-
</>
|
|
15
|
-
);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
UserPassword.propTypes = {
|
|
19
|
-
user: PropTypes.object,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const mapStateToProps = ({ user }) => ({ user });
|
|
23
|
-
|
|
24
|
-
export default connect(mapStateToProps)(UserPassword);
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { render } from "@truedat/test/render";
|
|
2
|
-
import User from "../User";
|
|
3
|
-
|
|
4
|
-
describe("<User />", () => {
|
|
5
|
-
const user = {
|
|
6
|
-
id: "1",
|
|
7
|
-
user_name: "joe",
|
|
8
|
-
role: "admin",
|
|
9
|
-
full_name: "Joe Bloggs",
|
|
10
|
-
email: "joe@bloggs.net",
|
|
11
|
-
};
|
|
12
|
-
const renderOpts = {
|
|
13
|
-
state: { user },
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
it("matches the latest snapshot", () => {
|
|
17
|
-
const { container } = render(<User />, renderOpts);
|
|
18
|
-
expect(container).toMatchSnapshot();
|
|
19
|
-
});
|
|
20
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { render, waitForLoad } from "@truedat/test/render";
|
|
2
|
-
import { UserPassword } from "../UserPassword";
|
|
3
|
-
|
|
4
|
-
const renderOpts = {
|
|
5
|
-
state: {
|
|
6
|
-
authentication: {
|
|
7
|
-
user_name: "foo",
|
|
8
|
-
},
|
|
9
|
-
},
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
describe("<UserPassword />", () => {
|
|
13
|
-
const user = {
|
|
14
|
-
id: 1,
|
|
15
|
-
user_name: "joe",
|
|
16
|
-
};
|
|
17
|
-
const props = { user };
|
|
18
|
-
|
|
19
|
-
it("matches the latest snapshot", async () => {
|
|
20
|
-
const rendered = render(<UserPassword {...props} />, renderOpts);
|
|
21
|
-
await waitForLoad(rendered);
|
|
22
|
-
expect(rendered.container).toMatchSnapshot();
|
|
23
|
-
});
|
|
24
|
-
});
|