@truedat/auth 8.14.3 → 8.14.5

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.
Files changed (78) hide show
  1. package/package.json +4 -4
  2. package/src/groups/components/GroupCard.js +2 -2
  3. package/src/groups/components/GroupCards.js +129 -37
  4. package/src/groups/components/GroupForm.js +13 -7
  5. package/src/groups/components/GroupFormModal.js +8 -7
  6. package/src/groups/components/GroupInfoModal.js +6 -6
  7. package/src/groups/components/GroupMemberPicker.js +2 -4
  8. package/src/groups/components/GroupRouteModal.js +15 -10
  9. package/src/groups/components/__tests__/GroupCards.spec.js +94 -0
  10. package/src/groups/components/__tests__/GroupForm.spec.js +2 -2
  11. package/src/groups/components/__tests__/GroupModals.spec.js +42 -16
  12. package/src/groups/components/__tests__/__snapshots__/GroupCards.spec.js.snap +14 -0
  13. package/src/messages/en.js +6 -5
  14. package/src/messages/es.js +8 -7
  15. package/src/styles/Groups.less +96 -131
  16. package/src/styles/UserCard.less +416 -0
  17. package/src/styles/UserModals.less +831 -0
  18. package/src/styles/Users.less +313 -0
  19. package/src/users/components/AdminUserRoutes.js +7 -20
  20. package/src/users/components/EditUser.js +7 -34
  21. package/src/users/components/EditUserModal.js +150 -0
  22. package/src/users/components/NewUser.js +128 -17
  23. package/src/users/components/Password.js +11 -189
  24. package/src/users/components/UserAcls.js +117 -6
  25. package/src/users/components/UserActions.js +99 -34
  26. package/src/users/components/UserCard.js +171 -22
  27. package/src/users/components/UserCards.js +188 -35
  28. package/src/users/components/UserFormFields.js +206 -0
  29. package/src/users/components/UserGroupAclRow.js +8 -2
  30. package/src/users/components/UserGroupAcls.js +2 -2
  31. package/src/users/components/UserGroupLink.js +21 -0
  32. package/src/users/components/UserInfoModal.js +228 -0
  33. package/src/users/components/UserPasswordField.js +66 -0
  34. package/src/users/components/UserPasswordModal.js +210 -0
  35. package/src/users/components/UserRoutes.js +4 -13
  36. package/src/users/components/UserTabs.js +5 -2
  37. package/src/users/components/UserTypeSelector.js +109 -0
  38. package/src/users/components/UsersAndGroups.js +39 -5
  39. package/src/users/components/UsersTable.js +234 -0
  40. package/src/users/components/__tests__/EditUser.spec.js +84 -10
  41. package/src/users/components/__tests__/NewUser.spec.js +34 -4
  42. package/src/users/components/__tests__/Password.spec.js +53 -146
  43. package/src/users/components/__tests__/UserAcls.spec.js +62 -2
  44. package/src/users/components/__tests__/UserCard.spec.js +111 -5
  45. package/src/users/components/__tests__/UserCards.spec.js +149 -0
  46. package/src/users/components/__tests__/UserGroupAclRow.spec.js +19 -1
  47. package/src/users/components/__tests__/UserModals.spec.js +554 -0
  48. package/src/users/components/__tests__/UserRoutes.spec.js +8 -3
  49. package/src/users/components/__tests__/UserTypeSelector.spec.js +32 -0
  50. package/src/users/components/__tests__/UsersAndGroups.spec.js +17 -0
  51. package/src/users/components/__tests__/__snapshots__/UserCard.spec.js.snap +66 -37
  52. package/src/users/components/__tests__/__snapshots__/UserCards.spec.js.snap +375 -75
  53. package/src/users/components/__tests__/__snapshots__/UserGroupAclRow.spec.js.snap +7 -3
  54. package/src/users/components/__tests__/__snapshots__/UserRoutes.spec.js.snap +4 -13
  55. package/src/users/hooks/useSortedGroups.js +13 -0
  56. package/src/users/hooks/useUserModalOperation.js +55 -0
  57. package/src/users/reducers/__tests__/userRedirect.spec.js +14 -3
  58. package/src/users/reducers/index.js +1 -0
  59. package/src/users/reducers/userModalOperation.js +29 -0
  60. package/src/users/reducers/userRedirect.js +5 -4
  61. package/src/users/sagas/__tests__/updateUser.spec.js +19 -1
  62. package/src/users/sagas/createUser.js +12 -5
  63. package/src/users/sagas/deleteUser.js +12 -5
  64. package/src/users/sagas/updateUser.js +19 -8
  65. package/src/users/selectors/__tests__/filterUsers.spec.js +11 -0
  66. package/src/users/selectors/__tests__/getUserAcls.spec.js +1 -0
  67. package/src/users/selectors/__tests__/getUserGroupAcls.spec.js +2 -0
  68. package/src/users/selectors/filterUsers.js +9 -1
  69. package/src/users/selectors/getUserGroupAcls.js +1 -0
  70. package/src/users/components/User.js +0 -68
  71. package/src/users/components/UserPassword.js +0 -24
  72. package/src/users/components/__tests__/User.spec.js +0 -20
  73. package/src/users/components/__tests__/UserPassword.spec.js +0 -24
  74. package/src/users/components/__tests__/__snapshots__/EditUser.spec.js.snap +0 -281
  75. package/src/users/components/__tests__/__snapshots__/NewUser.spec.js.snap +0 -321
  76. package/src/users/components/__tests__/__snapshots__/Password.spec.js.snap +0 -128
  77. package/src/users/components/__tests__/__snapshots__/User.spec.js.snap +0 -129
  78. package/src/users/components/__tests__/__snapshots__/UserPassword.spec.js.snap +0 -124
@@ -0,0 +1,313 @@
1
+ .users-role-accordion.ui.styled.accordion {
2
+ clear: both;
3
+ border: 0;
4
+ border-radius: 0;
5
+ box-shadow: none;
6
+ margin-top: 0;
7
+ background: transparent;
8
+ }
9
+
10
+ .users-role-accordion.ui.styled.accordion .title.users-role-title {
11
+ display: flex;
12
+ align-items: center;
13
+ gap: 0.75rem;
14
+ margin-bottom: 0.28rem;
15
+ border: 0;
16
+ border-bottom: 1px solid #d1d5db;
17
+ border-radius: 0;
18
+ background: transparent;
19
+ padding: 0.2rem 0.52rem 0.2rem 0.7rem;
20
+ box-shadow: none;
21
+ }
22
+
23
+ .users-role-toggle {
24
+ display: block;
25
+ flex: 1;
26
+ min-width: 0;
27
+ width: 100%;
28
+ margin: 0;
29
+ padding: 0;
30
+ border: 0;
31
+ background: transparent;
32
+ color: inherit;
33
+ font: inherit;
34
+ text-align: left;
35
+ cursor: pointer;
36
+
37
+ &:focus-visible {
38
+ outline: 2px solid #1d4ed8;
39
+ outline-offset: 2px;
40
+ border-radius: 2px;
41
+ }
42
+ }
43
+
44
+ .users-role-title-content {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: flex-start;
48
+ gap: 0.3rem;
49
+ }
50
+
51
+ .users-role-title-header.ui.header {
52
+ margin: 0;
53
+ display: flex;
54
+ align-items: center;
55
+ color: #0f172a;
56
+ min-height: 0.92rem;
57
+ }
58
+
59
+ .users-role-title-header.ui.header .content {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ min-height: 0.9rem;
63
+ font-size: 1.34rem;
64
+ font-weight: 800;
65
+ letter-spacing: 0.01em;
66
+ line-height: 1;
67
+ }
68
+
69
+ .users-role-accordion.ui.styled.accordion
70
+ .users-role-title-header.ui.header
71
+ .content {
72
+ padding: 0.625rem 0 !important;
73
+ }
74
+
75
+ .users-role-count.ui.circular.label {
76
+ min-width: 1.34rem;
77
+ height: 1.34rem;
78
+ display: inline-flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ margin: 0;
82
+ font-size: 0.68rem;
83
+ font-weight: 800;
84
+ background: #0f172a;
85
+ color: #ffffff;
86
+ }
87
+
88
+ .users-role-chevron.icon {
89
+ margin: 0;
90
+ color: #334155;
91
+ font-size: 1.15rem;
92
+ line-height: 1;
93
+ }
94
+
95
+ .users-role-accordion.ui.styled.accordion .content.users-role-content {
96
+ margin: 0 0 0.55rem;
97
+ padding: 0 0.14rem 0.12rem;
98
+ border: 0;
99
+ background: transparent;
100
+ }
101
+
102
+ .users-role-card-group.ui.cards {
103
+ display: grid;
104
+ grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
105
+ gap: 1.75em 1rem;
106
+ margin: 0;
107
+ padding-block: 0.875em;
108
+
109
+ &::before,
110
+ &::after {
111
+ display: none;
112
+ }
113
+
114
+ > .ui.card.td-card-base.td-card--user {
115
+ width: 100%;
116
+ max-width: none;
117
+ min-width: 0;
118
+ margin: 0;
119
+ }
120
+ }
121
+
122
+ @media (min-width: 1600px) {
123
+ .users-role-card-group.ui.cards {
124
+ grid-template-columns: repeat(5, minmax(0, 1fr));
125
+ }
126
+ }
127
+
128
+ .users-role-title.users-role-title--admin {
129
+ border-bottom-color: #93c5fd;
130
+ }
131
+
132
+ .users-role-title.users-role-title--user {
133
+ border-bottom-color: #86efac;
134
+ }
135
+
136
+ .users-role-title.users-role-title--agent {
137
+ border-bottom-color: #c4b5fd;
138
+ }
139
+
140
+ .users-role-title.users-role-title--service {
141
+ border-bottom-color: #fdba74;
142
+ }
143
+
144
+ @media (max-width: 767px) {
145
+ .users-role-accordion.ui.styled.accordion .title.users-role-title {
146
+ padding: 0.18rem 0.42rem;
147
+ }
148
+
149
+ .users-role-title-header.ui.header .content {
150
+ font-size: 1.16rem;
151
+ }
152
+ }
153
+
154
+ .users-toolbar {
155
+ display: flex;
156
+ flex-wrap: wrap;
157
+ align-items: center;
158
+ gap: 0.75rem;
159
+ margin-bottom: 0.5rem;
160
+
161
+ > .ui.input {
162
+ flex: 1 1 12rem;
163
+ min-width: 0;
164
+
165
+ > input {
166
+ min-width: 0;
167
+ width: 0;
168
+ flex: 1 1 0;
169
+ }
170
+ }
171
+
172
+ > .ui.button,
173
+ .users-toolbar-actions .ui.button {
174
+ margin: 0;
175
+ }
176
+ }
177
+
178
+ .users-filters {
179
+ display: flex;
180
+ flex-wrap: wrap;
181
+ align-items: center;
182
+ gap: 0.75rem;
183
+ margin-bottom: 0.5rem;
184
+ }
185
+
186
+ .users-toolbar-actions {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 0.75rem;
190
+ }
191
+
192
+ .ui.buttons.users-view-buttons {
193
+ flex-shrink: 0;
194
+
195
+ > .ui.button {
196
+ margin: 0;
197
+ border-radius: 0;
198
+
199
+ &:first-child {
200
+ border-radius: 4px 0 0 4px;
201
+ }
202
+
203
+ &:last-child {
204
+ border-radius: 0 4px 4px 0;
205
+ }
206
+
207
+ &:focus-visible {
208
+ position: relative;
209
+ z-index: 1;
210
+ outline: 2px solid @primaryColor;
211
+ outline-offset: 2px;
212
+ }
213
+ }
214
+ }
215
+
216
+ .users-toolbar-actions {
217
+ margin-left: auto;
218
+ flex-wrap: wrap;
219
+ }
220
+
221
+ .users-results {
222
+ display: flex;
223
+ justify-content: flex-end;
224
+ margin-bottom: 0.25rem;
225
+
226
+ > .ui.label {
227
+ margin: 0;
228
+ font-weight: 400;
229
+ }
230
+ }
231
+
232
+ .users-role-title > .users-results {
233
+ flex-shrink: 0;
234
+ margin: 0;
235
+ }
236
+
237
+ .users-table-scroll {
238
+ max-width: 100%;
239
+ overflow-x: auto;
240
+ }
241
+
242
+ .ui.table.users-table {
243
+ min-width: 850px;
244
+ box-shadow: none;
245
+
246
+ td {
247
+ overflow-wrap: anywhere;
248
+ max-width: 22rem;
249
+ }
250
+ .users-table-actions {
251
+ white-space: nowrap;
252
+ .ui.button {
253
+ margin: 0 0 0 0.5rem;
254
+ }
255
+ }
256
+ .users-table-type .icon {
257
+ margin: 0;
258
+ }
259
+ .users-sort-hidden {
260
+ visibility: hidden;
261
+ }
262
+ .users-table-name {
263
+ font-weight: 700;
264
+ }
265
+ .users-table-username {
266
+ display: block;
267
+ color: #6b7280;
268
+ font-size: 0.875rem;
269
+ }
270
+ }
271
+
272
+ .users-table-sort {
273
+ display: inline-flex;
274
+ align-items: center;
275
+ gap: 0.35rem;
276
+ padding: 0;
277
+ border: 0;
278
+ background: transparent;
279
+ color: inherit;
280
+ font: inherit;
281
+ cursor: pointer;
282
+ }
283
+
284
+ .users-table-scroll:focus-visible,
285
+ .users-table-sort:focus-visible,
286
+ .users-table a:focus-visible,
287
+ .users-table button:focus-visible {
288
+ outline: 2px solid currentColor;
289
+ outline-offset: 2px;
290
+ }
291
+
292
+ .users-toolbar .ui.input input::placeholder {
293
+ color: #6b7280;
294
+ font-weight: 400;
295
+ opacity: 1;
296
+ }
297
+
298
+ @media (max-width: 767px) {
299
+ .users-filters {
300
+ position: relative;
301
+
302
+ > .ui.dropdown {
303
+ position: static;
304
+
305
+ > .menu {
306
+ left: 0;
307
+ right: auto;
308
+ min-width: min(16rem, 100%);
309
+ max-width: 100%;
310
+ }
311
+ }
312
+ }
313
+ }
@@ -1,42 +1,29 @@
1
- import React from "react";
2
1
  import { Route, Routes } from "react-router";
3
2
  import { Divider, Segment } from "semantic-ui-react";
4
- import Unauthorized from "@truedat/core/components/Unauthorized";
5
3
  import { useActiveRoute, useAuthorized } from "@truedat/core/hooks";
6
- import { ProtectedRoute, Loader } from "@truedat/core/router";
4
+ import { ProtectedRoute } from "@truedat/core/router";
7
5
  import {
8
6
  GROUP,
9
7
  GROUP_CREATE,
10
8
  GROUP_EDIT,
11
9
  GROUPS,
12
- USER,
13
- USER_EDIT,
14
10
  USERS,
15
- USERS_NEW,
16
- USER_EDIT_PASSWORD,
17
11
  } from "@truedat/core/routes";
18
12
  import Group from "../../groups/components/Group";
19
13
  import GroupUsers from "../../groups/components/GroupUsers";
20
14
  import NewGroup from "../../groups/components/NewGroup";
21
15
  import EditGroup from "../../groups/components/EditGroup";
22
- import EditUser from "./EditUser";
23
16
  import GroupUserCrumbs from "./GroupUserCrumbs";
24
- import NewUser from "./NewUser";
25
- import User from "./User";
26
- import UserLoader from "./UserLoader";
27
17
  import UsersAndGroups from "./UsersAndGroups";
28
18
  import UsersLoader from "./UsersLoader";
29
- import UserPassword from "./UserPassword";
30
19
 
31
20
  const UserRoutes = () => (
32
21
  <Routes>
33
22
  <Route index element={<UsersAndGroups />} />
34
- <Route path="new" element={<NewUser />} />
35
- <Route path=":id" element={<Loader loaders={<UserLoader />} />}>
36
- <Route index element={<User />} />
37
- <Route path="edit" element={<EditUser />} />
38
- <Route path="password" element={<UserPassword />} />
39
- </Route>
23
+ <Route path={"new"} element={<UsersAndGroups />} />
24
+ <Route path={":id"} element={<UsersAndGroups />} />
25
+ <Route path={":id/edit"} element={<UsersAndGroups />} />
26
+ <Route path={":id/password"} element={<UsersAndGroups />} />
40
27
  </Routes>
41
28
  );
42
29
 
@@ -71,13 +58,13 @@ const GroupRoutes = () => (
71
58
 
72
59
  export const AdminUserRoutes = () => {
73
60
  const authorized = useAuthorized();
74
- const usersActive = useActiveRoute(USERS);
61
+ const usersActive = useActiveRoute(`${USERS}/*`);
75
62
  return (
76
63
  <>
77
64
  {authorized && usersActive && <UsersLoader />}
78
65
  <Routes>
79
66
  <Route
80
- path={USERS}
67
+ path={`${USERS}/*`}
81
68
  element={
82
69
  <ProtectedRoute>
83
70
  <UserRoutes />
@@ -1,37 +1,10 @@
1
- import _ from "lodash/fp";
2
- import PropTypes from "prop-types";
3
- import { Header, Container, Segment } from "semantic-ui-react";
4
- import { connect } from "react-redux";
5
- import { useIntl } from "react-intl";
6
- import { updateUser } from "../routines";
7
- import UserBreadcrumbs from "./UserBreadcrumbs";
8
- import UserForm from "./UserForm";
1
+ import { useNavigate } from "react-router";
2
+ import { USERS } from "@truedat/core/routes";
3
+ import EditUserModal from "./EditUserModal";
9
4
 
10
- const EditUser = ({ updateUser, user }) => {
11
- const { formatMessage } = useIntl();
12
- return _.isEmpty(user) ? null : (
13
- <>
14
- <UserBreadcrumbs name={formatMessage({ id: "users.actions.edit" })} />
15
- <Container text as={Segment}>
16
- <Header
17
- as="h2"
18
- icon="user"
19
- content={formatMessage({ id: "users.actions.edit" })}
20
- />
21
- <UserForm
22
- onSubmit={(props) => updateUser({ ...props, id: user?.id })}
23
- user={user}
24
- />
25
- </Container>
26
- </>
27
- );
5
+ const EditUser = () => {
6
+ const navigate = useNavigate();
7
+ return <EditUserModal onClose={() => navigate(USERS)} />;
28
8
  };
29
9
 
30
- EditUser.propTypes = {
31
- user: PropTypes.object,
32
- updateUser: PropTypes.func,
33
- };
34
-
35
- const mapStateToProps = ({ user }) => ({ user });
36
-
37
- export default connect(mapStateToProps, { updateUser })(EditUser);
10
+ export default EditUser;
@@ -0,0 +1,150 @@
1
+ import { useEffect, useState } from "react";
2
+ import PropTypes from "prop-types";
3
+ import { connect } from "react-redux";
4
+ import { Form } from "semantic-ui-react";
5
+ import { useIntl } from "react-intl";
6
+ import { useForm } from "react-hook-form";
7
+ import TrueModal from "@truedat/core/components/TrueModal";
8
+ import { updateUser } from "../routines";
9
+ import useUserModalOperation from "../hooks/useUserModalOperation";
10
+ import useSortedGroups from "../hooks/useSortedGroups";
11
+ import UserFormFields from "./UserFormFields";
12
+ import { DeleteUserModal } from "./UserActions";
13
+
14
+ const getDefaultValues = (user = {}) => ({
15
+ full_name: user.full_name || "",
16
+ email: user.email || "",
17
+ user_name: user.user_name || "",
18
+ external_id: user.external_id || "",
19
+ role: user.role || "user",
20
+ groups: user.groups || [],
21
+ });
22
+
23
+ export const EditUserModal = ({ user, groups, onClose }) => {
24
+ const { formatMessage } = useIntl();
25
+ const [deleteModalOpen, setDeleteModalOpen] = useState(false);
26
+ const {
27
+ submit,
28
+ isSubmitting,
29
+ onClose: closeModal,
30
+ error,
31
+ } = useUserModalOperation(updateUser, onClose);
32
+
33
+ const {
34
+ handleSubmit,
35
+ control,
36
+ reset,
37
+ formState: { errors, isDirty, isValid },
38
+ } = useForm({
39
+ mode: "all",
40
+ defaultValues: getDefaultValues(user),
41
+ });
42
+
43
+ useEffect(() => {
44
+ if (user?.id) {
45
+ reset(getDefaultValues(user));
46
+ }
47
+ }, [
48
+ reset,
49
+ user?.id,
50
+ user?.full_name,
51
+ user?.email,
52
+ user?.user_name,
53
+ user?.external_id,
54
+ user?.role,
55
+ user?.groups,
56
+ ]);
57
+
58
+ if (!user?.id) return null;
59
+
60
+ if (deleteModalOpen) {
61
+ return (
62
+ <DeleteUserModal
63
+ id={user.id}
64
+ name={user.full_name || user.user_name}
65
+ onClose={() => setDeleteModalOpen(false)}
66
+ onDeleted={onClose}
67
+ />
68
+ );
69
+ }
70
+
71
+ const onSubmitForm = (data) => {
72
+ submit({ ...data, id: user.id });
73
+ };
74
+
75
+ return (
76
+ <TrueModal
77
+ open
78
+ title={formatMessage({ id: "users.actions.edit" })}
79
+ icon="user"
80
+ subtitle={user.full_name || user.user_name}
81
+ actions={[
82
+ {
83
+ key: "delete",
84
+ content: formatMessage({
85
+ id: "user.actions.delete.confirmation.header",
86
+ defaultMessage: "Delete user",
87
+ }),
88
+ icon: "trash alternate",
89
+ basic: true,
90
+ negative: true,
91
+ className: "um-delete-user",
92
+ onClick: () => setDeleteModalOpen(true),
93
+ disabled: isSubmitting,
94
+ },
95
+ {
96
+ key: "cancel",
97
+ content: formatMessage({ id: "actions.close" }),
98
+ secondary: true,
99
+ onClick: closeModal,
100
+ disabled: isSubmitting,
101
+ },
102
+ {
103
+ key: "save",
104
+ content: formatMessage({ id: "actions.edit" }),
105
+ primary: true,
106
+ type: "submit",
107
+ form: "edit-user-form",
108
+ disabled: !isDirty || !isValid || isSubmitting,
109
+ loading: isSubmitting,
110
+ },
111
+ ]}
112
+ onClose={closeModal}
113
+ aria-busy={isSubmitting}
114
+ closeOnEscape={!isSubmitting}
115
+ closeOnDimmerClick={!isSubmitting}
116
+ banner={error ? <span role="alert">{error}</span> : null}
117
+ bannerType="danger"
118
+ size="small"
119
+ className="user-modal user-modal--edit"
120
+ >
121
+ <Form
122
+ id="edit-user-form"
123
+ noValidate
124
+ onSubmit={handleSubmit(onSubmitForm)}
125
+ >
126
+ <UserFormFields
127
+ control={control}
128
+ errors={errors}
129
+ isSubmitting={isSubmitting}
130
+ groups={groups}
131
+ />
132
+ </Form>
133
+ </TrueModal>
134
+ );
135
+ };
136
+
137
+ EditUserModal.propTypes = {
138
+ user: PropTypes.object,
139
+ groups: PropTypes.array,
140
+ onClose: PropTypes.func.isRequired,
141
+ };
142
+
143
+ const EditUserModalWithGroups = (props) => {
144
+ const groups = useSortedGroups();
145
+ return <EditUserModal {...props} groups={groups} />;
146
+ };
147
+
148
+ const mapStateToProps = ({ user }) => ({ user });
149
+
150
+ export default connect(mapStateToProps)(EditUserModalWithGroups);