@truedat/auth 8.14.0 → 8.14.2
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/groups/components/EditGroup.js +64 -20
- package/src/groups/components/Group.js +39 -41
- package/src/groups/components/GroupCard.js +51 -54
- package/src/groups/components/GroupCards.js +60 -25
- package/src/groups/components/GroupDeleteModal.js +98 -0
- package/src/groups/components/GroupDropdown.js +92 -0
- package/src/groups/components/GroupForm.js +165 -84
- package/src/groups/components/GroupFormModal.js +137 -0
- package/src/groups/components/GroupInfoModal.js +114 -0
- package/src/groups/components/GroupMemberPicker.js +364 -0
- package/src/groups/components/GroupRouteModal.js +46 -0
- package/src/groups/components/GroupUsers.js +31 -14
- package/src/groups/components/GroupUsersTable.js +136 -83
- package/src/groups/components/NewGroup.js +38 -10
- package/src/groups/components/__tests__/EditGroup.spec.js +105 -0
- package/src/groups/components/__tests__/Group.spec.js +29 -47
- package/src/groups/components/__tests__/GroupCard.spec.js +8 -20
- package/src/groups/components/__tests__/GroupDropdown.spec.js +27 -0
- package/src/groups/components/__tests__/GroupForm.spec.js +32 -5
- package/src/groups/components/__tests__/GroupMemberPicker.spec.js +378 -0
- package/src/groups/components/__tests__/GroupModals.spec.js +210 -0
- package/src/groups/components/__tests__/GroupUsers.spec.js +17 -2
- package/src/groups/components/__tests__/GroupUsersTable.spec.js +34 -2
- package/src/groups/components/__tests__/__snapshots__/Group.spec.js.snap +1 -0
- package/src/groups/components/__tests__/__snapshots__/GroupCard.spec.js.snap +32 -30
- package/src/groups/components/__tests__/__snapshots__/GroupCards.spec.js.snap +90 -77
- package/src/groups/components/__tests__/__snapshots__/GroupUsers.spec.js.snap +75 -11
- package/src/groups/components/__tests__/__snapshots__/GroupUsersTable.spec.js.snap +70 -13
- package/src/groups/components/index.js +0 -4
- package/src/groups/hooks/__tests__/useGroupOperation.spec.js +167 -0
- package/src/groups/hooks/__tests__/useGroups.spec.js +90 -0
- package/src/groups/hooks/useGroupOperation.js +83 -0
- package/src/groups/hooks/useGroups.js +64 -0
- package/src/groups/reducers/index.js +0 -4
- package/src/groups/routines.js +0 -7
- package/src/groups/sagas/index.js +2 -24
- package/src/hooks/useRoles.js +6 -3
- package/src/styles/Groups.less +1009 -0
- package/src/users/components/AdminUserRoutes.js +1 -14
- package/src/users/components/GroupUserCrumbs.js +7 -12
- package/src/users/components/UserRoutes.js +12 -35
- package/src/users/components/UsersAndGroups.js +3 -6
- package/src/users/components/__tests__/GroupUserCrumbs.spec.js +26 -30
- package/src/users/components/__tests__/UserRoutes.spec.js +4 -6
- package/src/users/components/__tests__/UsersAndGroups.spec.js +5 -7
- package/src/users/components/__tests__/__snapshots__/UserRoutes.spec.js.snap +9 -55
- package/src/groups/components/GroupLoader.js +0 -27
- package/src/groups/components/GroupsLoader.js +0 -30
- package/src/groups/components/__tests__/GroupLoader.spec.js +0 -71
- package/src/groups/components/__tests__/__snapshots__/GroupLoader.spec.js.snap +0 -11
- package/src/groups/reducers/__tests__/groupRedirect.spec.js +0 -51
- package/src/groups/reducers/__tests__/groupUsers.spec.js +0 -60
- package/src/groups/reducers/group.js +0 -56
- package/src/groups/reducers/groupRedirect.js +0 -20
- package/src/groups/reducers/groupUsers.js +0 -16
- package/src/groups/reducers/groups.js +0 -29
- package/src/groups/sagas/__tests__/deleteGroup.spec.js +0 -93
- package/src/groups/sagas/__tests__/deleteGroupUser.spec.js +0 -80
- package/src/groups/sagas/createGroup.js +0 -27
- package/src/groups/sagas/deleteGroup.js +0 -31
- package/src/groups/sagas/deleteGroupUser.js +0 -35
- package/src/groups/sagas/fetchGroup.js +0 -30
- package/src/groups/sagas/fetchGroups.js +0 -26
- package/src/groups/sagas/updateGroup.js +0 -28
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import { Button, Icon, Popup } from "semantic-ui-react";
|
|
5
|
+
import { useQuery } from "@apollo/client";
|
|
6
|
+
import { DOMAINS_QUERY } from "@truedat/core/api/queries";
|
|
7
|
+
import { useRoles } from "../../hooks/useRoles";
|
|
8
|
+
import { useFilteredGroupUsers } from "../hooks/useGroups";
|
|
9
|
+
import GroupDropdown from "./GroupDropdown";
|
|
10
|
+
|
|
11
|
+
const typeIcons = {
|
|
12
|
+
user: "user",
|
|
13
|
+
admin: "shield alternate",
|
|
14
|
+
service: "cogs",
|
|
15
|
+
agent: "microchip",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const GroupMemberPicker = ({
|
|
19
|
+
users,
|
|
20
|
+
options,
|
|
21
|
+
value,
|
|
22
|
+
onChange,
|
|
23
|
+
onBlur,
|
|
24
|
+
disabled,
|
|
25
|
+
showFilters = true,
|
|
26
|
+
}) => {
|
|
27
|
+
const { formatMessage } = useIntl();
|
|
28
|
+
const message = (id, defaultMessage) => formatMessage({ id, defaultMessage });
|
|
29
|
+
const selectedListRef = useRef(null);
|
|
30
|
+
const [addedIds, setAddedIds] = useState([]);
|
|
31
|
+
const addedTimerRef = useRef(null);
|
|
32
|
+
useEffect(() => () => clearTimeout(addedTimerRef.current), []);
|
|
33
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
34
|
+
const [type, setType] = useState("");
|
|
35
|
+
const [role, setRole] = useState("");
|
|
36
|
+
const [domains, setDomains] = useState([]);
|
|
37
|
+
const {
|
|
38
|
+
data: roles = [],
|
|
39
|
+
error: rolesError,
|
|
40
|
+
loading: rolesLoading,
|
|
41
|
+
retry: retryRoles,
|
|
42
|
+
} = useRoles(showFilters);
|
|
43
|
+
const {
|
|
44
|
+
data: domainData,
|
|
45
|
+
loading: domainsLoading,
|
|
46
|
+
error: domainsError,
|
|
47
|
+
refetch: retryDomains,
|
|
48
|
+
} = useQuery(DOMAINS_QUERY, {
|
|
49
|
+
variables: { action: "viewDomain" },
|
|
50
|
+
skip: !showFilters,
|
|
51
|
+
});
|
|
52
|
+
const domainOptions = useMemo(() => {
|
|
53
|
+
const domains = domainData?.domains || [];
|
|
54
|
+
const names = new Map(domains.map(({ id, name }) => [String(id), name]));
|
|
55
|
+
return domains
|
|
56
|
+
.map(({ id, name, parentId }) => ({
|
|
57
|
+
key: id,
|
|
58
|
+
value: String(id),
|
|
59
|
+
text: names.has(String(parentId))
|
|
60
|
+
? `${names.get(String(parentId))} / ${name}`
|
|
61
|
+
: name,
|
|
62
|
+
}))
|
|
63
|
+
.sort((a, b) => a.text.localeCompare(b.text));
|
|
64
|
+
}, [domainData]);
|
|
65
|
+
const {
|
|
66
|
+
users: result,
|
|
67
|
+
error: searchError,
|
|
68
|
+
loading: searchLoading,
|
|
69
|
+
retry: retrySearch,
|
|
70
|
+
} = useFilteredGroupUsers(showFilters, role, domains);
|
|
71
|
+
const loading = rolesLoading || searchLoading;
|
|
72
|
+
const failed = Boolean(rolesError || searchError);
|
|
73
|
+
const usersById = useMemo(
|
|
74
|
+
() => new Map(users.map((user) => [user.id, user])),
|
|
75
|
+
[users],
|
|
76
|
+
);
|
|
77
|
+
const filteredOptions = useMemo(() => {
|
|
78
|
+
const selected = new Set(value);
|
|
79
|
+
const resultIds =
|
|
80
|
+
showFilters && result && new Set(result.map(({ id }) => String(id)));
|
|
81
|
+
const matches = new Set(
|
|
82
|
+
users
|
|
83
|
+
.filter(
|
|
84
|
+
(user) =>
|
|
85
|
+
(!resultIds || resultIds.has(String(user.id))) &&
|
|
86
|
+
(!showFilters || !type || user.role === type),
|
|
87
|
+
)
|
|
88
|
+
.map(({ id }) => id),
|
|
89
|
+
);
|
|
90
|
+
return options.filter(
|
|
91
|
+
({ value: id }) => !selected.has(id) && matches.has(id),
|
|
92
|
+
);
|
|
93
|
+
}, [options, result, users, type, value, showFilters]);
|
|
94
|
+
const addUsers = (ids) => {
|
|
95
|
+
if (disabled || (showFilters && (loading || failed))) return;
|
|
96
|
+
const added = ids.filter((id) => !value.includes(id));
|
|
97
|
+
if (!added.length) return;
|
|
98
|
+
clearTimeout(addedTimerRef.current);
|
|
99
|
+
setAddedIds(added);
|
|
100
|
+
addedTimerRef.current = setTimeout(() => setAddedIds([]), 900);
|
|
101
|
+
onChange([...added, ...value]);
|
|
102
|
+
setSearchQuery("");
|
|
103
|
+
if (selectedListRef.current) selectedListRef.current.scrollTop = 0;
|
|
104
|
+
};
|
|
105
|
+
const canAddAll =
|
|
106
|
+
showFilters &&
|
|
107
|
+
Boolean(role || domains.length) &&
|
|
108
|
+
filteredOptions.length > 1;
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
className={`group-member-picker${showFilters ? "" : " group-member-picker--without-filters"}`}
|
|
112
|
+
>
|
|
113
|
+
{showFilters && (
|
|
114
|
+
<aside
|
|
115
|
+
className="group-member-filters"
|
|
116
|
+
aria-label={message("group.members.filters", "Filtros de miembros")}
|
|
117
|
+
>
|
|
118
|
+
<GroupDropdown
|
|
119
|
+
selection
|
|
120
|
+
placeholder={message("group.members.all", "Todos")}
|
|
121
|
+
label={message("group.members.type", "Tipo de usuario")}
|
|
122
|
+
aria-label={message("group.members.type", "Tipo de usuario")}
|
|
123
|
+
value={type}
|
|
124
|
+
disabled={disabled}
|
|
125
|
+
options={[
|
|
126
|
+
{
|
|
127
|
+
key: "all",
|
|
128
|
+
value: "",
|
|
129
|
+
text: message("group.members.all", "Todos"),
|
|
130
|
+
},
|
|
131
|
+
...["user", "admin", "service", "agent"].map((item) => ({
|
|
132
|
+
key: item,
|
|
133
|
+
value: item,
|
|
134
|
+
text: message(
|
|
135
|
+
item === "agent"
|
|
136
|
+
? "group.members.agent"
|
|
137
|
+
: `user.type.${item}`,
|
|
138
|
+
item === "agent" ? "Agente" : undefined,
|
|
139
|
+
),
|
|
140
|
+
})),
|
|
141
|
+
]}
|
|
142
|
+
onChange={(_event, { value }) => setType(value)}
|
|
143
|
+
/>
|
|
144
|
+
<GroupDropdown
|
|
145
|
+
selection
|
|
146
|
+
search
|
|
147
|
+
placeholder={message("group.members.all", "Todos")}
|
|
148
|
+
label={message("group.members.role", "Rol")}
|
|
149
|
+
aria-label={message("group.members.role", "Rol")}
|
|
150
|
+
value={role}
|
|
151
|
+
disabled={disabled}
|
|
152
|
+
options={[
|
|
153
|
+
{
|
|
154
|
+
key: "all",
|
|
155
|
+
value: "",
|
|
156
|
+
text: message("group.members.all", "Todos"),
|
|
157
|
+
},
|
|
158
|
+
...roles.map(({ id, name }) => ({
|
|
159
|
+
key: id,
|
|
160
|
+
value: String(id),
|
|
161
|
+
text: name,
|
|
162
|
+
})),
|
|
163
|
+
]}
|
|
164
|
+
onChange={(_event, { value }) => setRole(value)}
|
|
165
|
+
/>
|
|
166
|
+
<GroupDropdown
|
|
167
|
+
selection
|
|
168
|
+
search
|
|
169
|
+
multiple
|
|
170
|
+
label={message("group.members.domain", "Dominio")}
|
|
171
|
+
aria-label={message("group.members.domain", "Dominio")}
|
|
172
|
+
placeholder={message("domain.multiple.placeholder")}
|
|
173
|
+
value={domains}
|
|
174
|
+
options={domainOptions}
|
|
175
|
+
disabled={disabled || domainsLoading || Boolean(domainsError)}
|
|
176
|
+
loading={domainsLoading}
|
|
177
|
+
error={Boolean(domainsError)}
|
|
178
|
+
onChange={(_event, { value }) => setDomains(value)}
|
|
179
|
+
/>
|
|
180
|
+
{domainsError && (
|
|
181
|
+
<div role="alert">
|
|
182
|
+
<p>
|
|
183
|
+
{message(
|
|
184
|
+
"group.members.domainsError",
|
|
185
|
+
"No se han podido cargar los dominios.",
|
|
186
|
+
)}
|
|
187
|
+
</p>
|
|
188
|
+
<Button
|
|
189
|
+
type="button"
|
|
190
|
+
size="small"
|
|
191
|
+
disabled={disabled || domainsLoading}
|
|
192
|
+
onClick={() => retryDomains().catch(() => {})}
|
|
193
|
+
>
|
|
194
|
+
{message("group.members.retry", "Reintentar")}
|
|
195
|
+
</Button>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</aside>
|
|
199
|
+
)}
|
|
200
|
+
<div className="group-member-selection">
|
|
201
|
+
<div className="group-add-control">
|
|
202
|
+
<GroupDropdown
|
|
203
|
+
closeOnChange
|
|
204
|
+
selectOnBlur={false}
|
|
205
|
+
name="user_ids"
|
|
206
|
+
aria-label={message("group.members.add", "Añadir miembros")}
|
|
207
|
+
label={message("group.members.add", "Añadir miembros")}
|
|
208
|
+
search
|
|
209
|
+
selection
|
|
210
|
+
options={filteredOptions}
|
|
211
|
+
value=""
|
|
212
|
+
searchQuery={searchQuery}
|
|
213
|
+
onSearchChange={(_event, { searchQuery }) =>
|
|
214
|
+
setSearchQuery(searchQuery)
|
|
215
|
+
}
|
|
216
|
+
onBlur={onBlur}
|
|
217
|
+
disabled={disabled || (showFilters && (loading || failed))}
|
|
218
|
+
loading={showFilters && loading}
|
|
219
|
+
onChange={(_event, { value: id }) => addUsers([id])}
|
|
220
|
+
placeholder={message("group.form.users.placeholder")}
|
|
221
|
+
/>
|
|
222
|
+
{canAddAll && (
|
|
223
|
+
<Button
|
|
224
|
+
type="button"
|
|
225
|
+
size="mini"
|
|
226
|
+
className="group-add-all"
|
|
227
|
+
disabled={disabled || loading || failed}
|
|
228
|
+
onClick={() =>
|
|
229
|
+
addUsers(filteredOptions.map(({ value: id }) => id))
|
|
230
|
+
}
|
|
231
|
+
>
|
|
232
|
+
{message("group.members.addAll", "Añadir todos")}
|
|
233
|
+
</Button>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
<section
|
|
237
|
+
className="group-selected-members"
|
|
238
|
+
aria-label={message(
|
|
239
|
+
"group.members.selected",
|
|
240
|
+
"Miembros seleccionados",
|
|
241
|
+
)}
|
|
242
|
+
>
|
|
243
|
+
<h4>
|
|
244
|
+
{message("group.members.selected", "Miembros seleccionados")}{" "}
|
|
245
|
+
<span>({value.length})</span>
|
|
246
|
+
</h4>
|
|
247
|
+
{value.length ? (
|
|
248
|
+
<ul ref={selectedListRef}>
|
|
249
|
+
{value.map((id) => {
|
|
250
|
+
const user = usersById.get(id);
|
|
251
|
+
const name = user?.full_name || user?.user_name || String(id);
|
|
252
|
+
const parts = name.trim().split(/\s+/);
|
|
253
|
+
const initials =
|
|
254
|
+
(parts[0]?.[0] || "?") +
|
|
255
|
+
(parts.length > 1 ? parts[parts.length - 1][0] : "");
|
|
256
|
+
const typeLabel = user?.role
|
|
257
|
+
? message(
|
|
258
|
+
user.role === "agent"
|
|
259
|
+
? "group.members.agent"
|
|
260
|
+
: `user.type.${user.role}`,
|
|
261
|
+
user.role === "agent" ? "Agente" : undefined,
|
|
262
|
+
)
|
|
263
|
+
: null;
|
|
264
|
+
return (
|
|
265
|
+
<li
|
|
266
|
+
key={id}
|
|
267
|
+
className={
|
|
268
|
+
addedIds.includes(id) ? "group-member-added" : undefined
|
|
269
|
+
}
|
|
270
|
+
>
|
|
271
|
+
<span className="group-member-identity">
|
|
272
|
+
<span className="group-member-avatar" aria-hidden="true">
|
|
273
|
+
{initials.toUpperCase()}
|
|
274
|
+
</span>
|
|
275
|
+
<span className="group-member-details">
|
|
276
|
+
<span className="group-member-name">
|
|
277
|
+
<strong>{name}</strong>
|
|
278
|
+
{typeLabel && (
|
|
279
|
+
<Popup
|
|
280
|
+
inverted
|
|
281
|
+
size="mini"
|
|
282
|
+
content={typeLabel}
|
|
283
|
+
on={["hover", "focus"]}
|
|
284
|
+
position="top center"
|
|
285
|
+
trigger={
|
|
286
|
+
<span
|
|
287
|
+
className="group-member-type"
|
|
288
|
+
tabIndex={0}
|
|
289
|
+
aria-label={typeLabel}
|
|
290
|
+
>
|
|
291
|
+
<Icon
|
|
292
|
+
name={typeIcons[user.role] || "user"}
|
|
293
|
+
aria-hidden="true"
|
|
294
|
+
/>
|
|
295
|
+
</span>
|
|
296
|
+
}
|
|
297
|
+
/>
|
|
298
|
+
)}
|
|
299
|
+
</span>
|
|
300
|
+
{user?.full_name && user?.user_name && (
|
|
301
|
+
<small>{user.user_name}</small>
|
|
302
|
+
)}
|
|
303
|
+
</span>
|
|
304
|
+
</span>
|
|
305
|
+
<Button
|
|
306
|
+
type="button"
|
|
307
|
+
icon
|
|
308
|
+
basic
|
|
309
|
+
disabled={disabled}
|
|
310
|
+
aria-label={formatMessage(
|
|
311
|
+
{
|
|
312
|
+
id: "group.members.remove",
|
|
313
|
+
defaultMessage: "Eliminar a {name} del grupo",
|
|
314
|
+
},
|
|
315
|
+
{ name },
|
|
316
|
+
)}
|
|
317
|
+
onClick={() =>
|
|
318
|
+
onChange(value.filter((memberId) => memberId !== id))
|
|
319
|
+
}
|
|
320
|
+
>
|
|
321
|
+
<Icon name="times" />
|
|
322
|
+
</Button>
|
|
323
|
+
</li>
|
|
324
|
+
);
|
|
325
|
+
})}
|
|
326
|
+
</ul>
|
|
327
|
+
) : (
|
|
328
|
+
<p>
|
|
329
|
+
{message("group.members.empty", "No hay miembros seleccionados")}
|
|
330
|
+
</p>
|
|
331
|
+
)}
|
|
332
|
+
</section>
|
|
333
|
+
{showFilters && failed && (
|
|
334
|
+
<div role="alert">
|
|
335
|
+
<p>
|
|
336
|
+
{message(
|
|
337
|
+
"group.members.error",
|
|
338
|
+
"No se han podido cargar los usuarios.",
|
|
339
|
+
)}
|
|
340
|
+
</p>
|
|
341
|
+
<Button
|
|
342
|
+
type="button"
|
|
343
|
+
size="small"
|
|
344
|
+
disabled={disabled}
|
|
345
|
+
onClick={() => Promise.allSettled([retryRoles(), retrySearch()])}
|
|
346
|
+
>
|
|
347
|
+
{message("group.members.retry", "Reintentar")}
|
|
348
|
+
</Button>
|
|
349
|
+
</div>
|
|
350
|
+
)}
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
};
|
|
355
|
+
GroupMemberPicker.propTypes = {
|
|
356
|
+
users: PropTypes.array.isRequired,
|
|
357
|
+
options: PropTypes.array.isRequired,
|
|
358
|
+
value: PropTypes.array.isRequired,
|
|
359
|
+
onChange: PropTypes.func.isRequired,
|
|
360
|
+
onBlur: PropTypes.func,
|
|
361
|
+
disabled: PropTypes.bool,
|
|
362
|
+
showFilters: PropTypes.bool,
|
|
363
|
+
};
|
|
364
|
+
export default GroupMemberPicker;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import PropTypes from "prop-types";
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
|
+
import { useNavigate, useLocation, useParams } from "react-router";
|
|
4
|
+
import { GROUPS } from "@truedat/core/routes";
|
|
5
|
+
import GroupFormModal from "./GroupFormModal";
|
|
6
|
+
import GroupInfoModal from "./GroupInfoModal";
|
|
7
|
+
|
|
8
|
+
const GroupRouteModal = ({ mode }) => {
|
|
9
|
+
const navigate = useNavigate();
|
|
10
|
+
const { pathname } = useLocation();
|
|
11
|
+
const { id } = useParams();
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const opener = document.activeElement;
|
|
14
|
+
return () => {
|
|
15
|
+
requestAnimationFrame(() => {
|
|
16
|
+
if (document.querySelector(".group-modal")) return;
|
|
17
|
+
if (opener?.isConnected && opener !== document.body) opener.focus();
|
|
18
|
+
else
|
|
19
|
+
document
|
|
20
|
+
.querySelector(`a[href="${pathname.replace(/\/edit$/, "")}"]`)
|
|
21
|
+
?.focus();
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
}, [pathname]);
|
|
25
|
+
const onClose = useCallback(() => {
|
|
26
|
+
navigate(GROUPS);
|
|
27
|
+
requestAnimationFrame(() => {
|
|
28
|
+
document
|
|
29
|
+
.querySelector(`a[href="${pathname.replace(/\/edit$/, "")}"]`)
|
|
30
|
+
?.focus();
|
|
31
|
+
});
|
|
32
|
+
}, [navigate, pathname]);
|
|
33
|
+
return mode === "info" ? (
|
|
34
|
+
<GroupInfoModal key={id} onClose={onClose} />
|
|
35
|
+
) : (
|
|
36
|
+
<GroupFormModal
|
|
37
|
+
key={id || "new"}
|
|
38
|
+
editing={mode === "edit"}
|
|
39
|
+
onClose={onClose}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
GroupRouteModal.propTypes = {
|
|
44
|
+
mode: PropTypes.oneOf(["new", "edit", "info"]).isRequired,
|
|
45
|
+
};
|
|
46
|
+
export default GroupRouteModal;
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import
|
|
4
|
-
import { Link } from "react-router";
|
|
5
|
-
import { connect } from "react-redux";
|
|
3
|
+
import { Link, useParams } from "react-router";
|
|
6
4
|
import { Segment, Button, Divider } from "semantic-ui-react";
|
|
7
5
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
8
6
|
import SearchInput from "@truedat/core/components/SearchInput";
|
|
9
7
|
import { linkTo } from "@truedat/core/routes";
|
|
8
|
+
import { useGroup, useGroupUsers } from "../hooks/useGroups";
|
|
10
9
|
import GroupUsersTable from "./GroupUsersTable";
|
|
11
10
|
|
|
12
|
-
export const GroupUsers = (
|
|
11
|
+
export const GroupUsers = () => {
|
|
12
|
+
const { id } = useParams();
|
|
13
|
+
const {
|
|
14
|
+
group,
|
|
15
|
+
members: groupUsers,
|
|
16
|
+
loading: groupLoading,
|
|
17
|
+
error,
|
|
18
|
+
retry,
|
|
19
|
+
} = useGroup(id);
|
|
13
20
|
const [filter, setFilter] = useState("");
|
|
14
21
|
const { formatMessage } = useIntl();
|
|
22
|
+
const { users: directoryUsers } = useGroupUsers();
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
if (groupLoading) return <p role="status">Cargando grupo…</p>;
|
|
25
|
+
if (error || _.isEmpty(group)) {
|
|
26
|
+
return (
|
|
27
|
+
<div role="alert">
|
|
28
|
+
No se ha podido cargar el grupo.
|
|
29
|
+
<button type="button" onClick={() => retry().catch(() => {})}>
|
|
30
|
+
Reintentar
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return (
|
|
17
36
|
<Segment attached="bottom">
|
|
18
37
|
<Button
|
|
19
38
|
floated="right"
|
|
@@ -28,16 +47,14 @@ export const GroupUsers = ({ group, groupLoading }) => {
|
|
|
28
47
|
value={filter}
|
|
29
48
|
/>
|
|
30
49
|
<Divider hidden />
|
|
31
|
-
<GroupUsersTable
|
|
50
|
+
<GroupUsersTable
|
|
51
|
+
filter={filter}
|
|
52
|
+
group={group}
|
|
53
|
+
groupUsers={groupUsers}
|
|
54
|
+
users={directoryUsers}
|
|
55
|
+
/>
|
|
32
56
|
</Segment>
|
|
33
57
|
);
|
|
34
58
|
};
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
group: PropTypes.object,
|
|
38
|
-
groupLoading: PropTypes.bool,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const mapStateToProps = ({ group, groupLoading }) => ({ group, groupLoading });
|
|
42
|
-
|
|
43
|
-
export default connect(mapStateToProps)(GroupUsers);
|
|
60
|
+
export default GroupUsers;
|