@truedat/auth 8.14.1 → 8.14.3
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,167 @@
|
|
|
1
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
2
|
+
import { SWRConfig } from "swr";
|
|
3
|
+
import {
|
|
4
|
+
apiJson,
|
|
5
|
+
apiJsonPost,
|
|
6
|
+
apiJsonPut,
|
|
7
|
+
apiJsonDelete,
|
|
8
|
+
} from "@truedat/core/services/api";
|
|
9
|
+
import { useGroupOperation } from "../useGroupOperation";
|
|
10
|
+
import { useGroup, useGroups } from "../useGroups";
|
|
11
|
+
|
|
12
|
+
jest.mock("@truedat/core/services/api", () => ({
|
|
13
|
+
apiJson: jest.fn(),
|
|
14
|
+
apiJsonPost: jest.fn(),
|
|
15
|
+
apiJsonPut: jest.fn(),
|
|
16
|
+
apiJsonDelete: jest.fn(),
|
|
17
|
+
JSON_OPTS: {},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const setup = (action, onSuccess = jest.fn()) => {
|
|
21
|
+
const cache = new Map();
|
|
22
|
+
const wrapper = ({ children }) => (
|
|
23
|
+
<SWRConfig
|
|
24
|
+
value={{
|
|
25
|
+
provider: () => cache,
|
|
26
|
+
dedupingInterval: 0,
|
|
27
|
+
shouldRetryOnError: false,
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</SWRConfig>
|
|
32
|
+
);
|
|
33
|
+
return {
|
|
34
|
+
...renderHook(() => useGroupOperation(action, 1, onSuccess), { wrapper }),
|
|
35
|
+
cache,
|
|
36
|
+
onSuccess,
|
|
37
|
+
wrapper,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
beforeEach(() => jest.resetAllMocks());
|
|
42
|
+
|
|
43
|
+
describe("useGroupOperation", () => {
|
|
44
|
+
it("blocks duplicate submissions", async () => {
|
|
45
|
+
const deferred = {};
|
|
46
|
+
apiJsonPost.mockReturnValue(
|
|
47
|
+
new Promise((resolve) => {
|
|
48
|
+
deferred.resolve = resolve;
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
const { result } = setup("create");
|
|
52
|
+
act(() => {
|
|
53
|
+
result.current.submit({ name: "Team" });
|
|
54
|
+
result.current.submit({ name: "Team" });
|
|
55
|
+
});
|
|
56
|
+
expect(apiJsonPost).toHaveBeenCalledTimes(1);
|
|
57
|
+
await act(async () => deferred.resolve({ data: { data: { id: 1 } } }));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("exposes write errors without invoking success", async () => {
|
|
61
|
+
apiJsonPost.mockRejectedValue(new Error("offline"));
|
|
62
|
+
const { result, onSuccess } = setup("create");
|
|
63
|
+
await act(async () => result.current.submit({ name: "Team" }));
|
|
64
|
+
expect(result.current.failed).toBe(true);
|
|
65
|
+
expect(onSuccess).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("allows retry after a failed write", async () => {
|
|
69
|
+
apiJsonPost
|
|
70
|
+
.mockRejectedValueOnce(new Error("offline"))
|
|
71
|
+
.mockResolvedValueOnce({ data: { data: { id: 1 } } });
|
|
72
|
+
const { result, onSuccess } = setup("create");
|
|
73
|
+
await act(async () => result.current.submit({ name: "Team" }));
|
|
74
|
+
await act(async () => result.current.submit({ name: "Team" }));
|
|
75
|
+
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("keeps pending and errors independent between mutation instances", async () => {
|
|
79
|
+
apiJsonPost.mockRejectedValue(new Error("offline"));
|
|
80
|
+
const { result, wrapper } = setup("create");
|
|
81
|
+
const second = renderHook(
|
|
82
|
+
() => useGroupOperation("create", null, jest.fn()),
|
|
83
|
+
{ wrapper },
|
|
84
|
+
);
|
|
85
|
+
await act(async () => result.current.submit({ name: "Team" }));
|
|
86
|
+
expect(second.result.current.failed).toBe(false);
|
|
87
|
+
expect(second.result.current.pending).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("removes a member using freshly fetched membership", async () => {
|
|
91
|
+
apiJson.mockResolvedValue({
|
|
92
|
+
data: { data: { id: 1, name: "Team", users: [{ id: 2 }, { id: 3 }] } },
|
|
93
|
+
});
|
|
94
|
+
apiJsonPut.mockResolvedValue({
|
|
95
|
+
data: { data: { id: 1, users: [{ id: 3 }] } },
|
|
96
|
+
});
|
|
97
|
+
const { result } = setup("removeMember");
|
|
98
|
+
await act(async () => result.current.submit({ id: "2" }));
|
|
99
|
+
expect(apiJsonPut).toHaveBeenCalledWith(
|
|
100
|
+
"/api/groups/1",
|
|
101
|
+
{ group: { id: 1, name: "Team", user_ids: [3] } },
|
|
102
|
+
{},
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("evicts deleted group detail", async () => {
|
|
107
|
+
apiJsonDelete.mockResolvedValue({});
|
|
108
|
+
const { result, cache } = setup("delete");
|
|
109
|
+
cache.set("/api/groups/1", { data: { data: { data: { id: 1 } } } });
|
|
110
|
+
await act(async () => result.current.submit());
|
|
111
|
+
expect(cache.get("/api/groups/1").data).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("refreshes active list and detail caches after updating", async () => {
|
|
115
|
+
const group = { id: 1, name: "Team", users: [] };
|
|
116
|
+
apiJson.mockImplementation((url) =>
|
|
117
|
+
Promise.resolve({
|
|
118
|
+
data: { data: url === "/api/groups" ? [group] : group },
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
apiJsonPut.mockImplementation(async () => {
|
|
122
|
+
group.name = "Updated";
|
|
123
|
+
return { data: { data: group } };
|
|
124
|
+
});
|
|
125
|
+
const { result, wrapper } = setup("update");
|
|
126
|
+
const readers = renderHook(
|
|
127
|
+
() => ({ list: useGroups(), detail: useGroup(1) }),
|
|
128
|
+
{ wrapper },
|
|
129
|
+
);
|
|
130
|
+
await waitFor(() =>
|
|
131
|
+
expect(readers.result.current.list.loading).toBe(false),
|
|
132
|
+
);
|
|
133
|
+
await act(async () => result.current.submit({ id: 1, name: "Updated" }));
|
|
134
|
+
expect(readers.result.current.list.groups[0].name).toBe("Updated");
|
|
135
|
+
expect(readers.result.current.detail.group.name).toBe("Updated");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("reports write success even when list refresh fails", async () => {
|
|
139
|
+
apiJson
|
|
140
|
+
.mockResolvedValueOnce({ data: { data: [] } })
|
|
141
|
+
.mockRejectedValue(new Error("refresh failed"));
|
|
142
|
+
apiJsonPost.mockResolvedValue({ data: { data: { id: 1 } } });
|
|
143
|
+
const { result, wrapper, onSuccess } = setup("create");
|
|
144
|
+
const list = renderHook(() => useGroups(), { wrapper });
|
|
145
|
+
await waitFor(() => expect(list.result.current.loading).toBe(false));
|
|
146
|
+
await act(async () => result.current.submit({ name: "Team" }));
|
|
147
|
+
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
148
|
+
expect(result.current.failed).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("finishes the write while the background list refresh is still pending", async () => {
|
|
152
|
+
const refresh = {};
|
|
153
|
+
apiJson.mockReturnValue(
|
|
154
|
+
new Promise((resolve) => {
|
|
155
|
+
refresh.resolve = resolve;
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
apiJsonPost.mockResolvedValue({ data: { data: { id: 1 } } });
|
|
159
|
+
const { result, onSuccess } = setup("create");
|
|
160
|
+
await act(async () => result.current.submit({ name: "Team" }));
|
|
161
|
+
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
162
|
+
expect(result.current.pending).toBe(false);
|
|
163
|
+
await act(async () =>
|
|
164
|
+
refresh.resolve({ data: { data: [{ id: 1, name: "Team" }] } }),
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
2
|
+
import { SWRConfig } from "swr";
|
|
3
|
+
import { apiJson, apiJsonPost } from "@truedat/core/services/api";
|
|
4
|
+
import { groupKey, useGroup, useFilteredGroupUsers } from "../useGroups";
|
|
5
|
+
|
|
6
|
+
jest.mock("@truedat/core/services/api", () => ({
|
|
7
|
+
apiJson: jest.fn(),
|
|
8
|
+
apiJsonPost: jest.fn(),
|
|
9
|
+
JSON_OPTS: {},
|
|
10
|
+
}));
|
|
11
|
+
const setup = (hook, initialProps) => {
|
|
12
|
+
const cache = new Map();
|
|
13
|
+
const wrapper = ({ children }) => (
|
|
14
|
+
<SWRConfig
|
|
15
|
+
value={{
|
|
16
|
+
provider: () => cache,
|
|
17
|
+
shouldRetryOnError: false,
|
|
18
|
+
dedupingInterval: 0,
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</SWRConfig>
|
|
23
|
+
);
|
|
24
|
+
return renderHook(hook, { wrapper, initialProps });
|
|
25
|
+
};
|
|
26
|
+
beforeEach(() => jest.resetAllMocks());
|
|
27
|
+
|
|
28
|
+
describe("useGroup", () => {
|
|
29
|
+
it("does not fetch without an ID", () => {
|
|
30
|
+
setup(() => useGroup(null));
|
|
31
|
+
expect(apiJson).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("does not reuse another group's detail while changing IDs", async () => {
|
|
35
|
+
apiJson
|
|
36
|
+
.mockResolvedValueOnce({ data: { data: { id: 1 } } })
|
|
37
|
+
.mockReturnValue(new Promise(() => {}));
|
|
38
|
+
const { result, rerender } = setup((id) => useGroup(id), 1);
|
|
39
|
+
await waitFor(() => expect(result.current.group?.id).toBe(1));
|
|
40
|
+
rerender(2);
|
|
41
|
+
expect(result.current.group).toBeUndefined();
|
|
42
|
+
expect(result.current.loading).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("encodes concrete group keys", () => {
|
|
46
|
+
expect(groupKey("a/b")).toBe("/api/groups/a%2Fb");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("useFilteredGroupUsers", () => {
|
|
51
|
+
it("does not fetch disabled filters", () => {
|
|
52
|
+
setup(() => useFilteredGroupUsers(false, "1", ["2"]));
|
|
53
|
+
expect(apiJsonPost).not.toHaveBeenCalled();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("normalizes domain order and duplicates in the cache key", async () => {
|
|
57
|
+
apiJsonPost.mockResolvedValue({ data: { data: [{ id: 1 }] } });
|
|
58
|
+
const { result, rerender } = setup(
|
|
59
|
+
(domains) => useFilteredGroupUsers(true, "", domains),
|
|
60
|
+
["2", "1", "2"],
|
|
61
|
+
);
|
|
62
|
+
await waitFor(() => expect(result.current.users).toEqual([{ id: 1 }]));
|
|
63
|
+
rerender(["1", "2"]);
|
|
64
|
+
expect(apiJsonPost).toHaveBeenCalledTimes(1);
|
|
65
|
+
expect(apiJsonPost).toHaveBeenCalledWith(
|
|
66
|
+
"/api/users/search",
|
|
67
|
+
{ domains: ["1", "2"] },
|
|
68
|
+
{},
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("ignores a late response for a previous filter", async () => {
|
|
73
|
+
const deferred = {};
|
|
74
|
+
apiJsonPost.mockImplementation((_url, { roles }) =>
|
|
75
|
+
roles[0] === "1"
|
|
76
|
+
? new Promise((resolve) => {
|
|
77
|
+
deferred.resolve = resolve;
|
|
78
|
+
})
|
|
79
|
+
: Promise.resolve({ data: { data: [{ id: 2 }] } }),
|
|
80
|
+
);
|
|
81
|
+
const { result, rerender } = setup(
|
|
82
|
+
(role) => useFilteredGroupUsers(true, role, []),
|
|
83
|
+
"1",
|
|
84
|
+
);
|
|
85
|
+
rerender("2");
|
|
86
|
+
await waitFor(() => expect(result.current.users).toEqual([{ id: 2 }]));
|
|
87
|
+
await act(async () => deferred.resolve({ data: { data: [{ id: 1 }] } }));
|
|
88
|
+
expect(result.current.users).toEqual([{ id: 2 }]);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
|
+
import { useSWRConfig } from "swr";
|
|
3
|
+
import useSWRMutation from "swr/mutation";
|
|
4
|
+
import {
|
|
5
|
+
apiJson,
|
|
6
|
+
apiJsonDelete,
|
|
7
|
+
apiJsonPost,
|
|
8
|
+
apiJsonPut,
|
|
9
|
+
JSON_OPTS,
|
|
10
|
+
} from "@truedat/core/services/api";
|
|
11
|
+
import { API_GROUPS } from "../api";
|
|
12
|
+
import { groupKey } from "./useGroups";
|
|
13
|
+
|
|
14
|
+
const writeGroup = async (url, { arg: { action, values } }) => {
|
|
15
|
+
if (action === "delete") return apiJsonDelete(url, JSON_OPTS);
|
|
16
|
+
const normalizeResponse = (response) =>
|
|
17
|
+
response.data?.data
|
|
18
|
+
? { ...response, data: { ...response.data, data: response.data.data } }
|
|
19
|
+
: response;
|
|
20
|
+
if (action === "removeMember") {
|
|
21
|
+
const { data } = await apiJson(url, JSON_OPTS);
|
|
22
|
+
const { users, ...group } = data.data;
|
|
23
|
+
const response = await apiJsonPut(
|
|
24
|
+
url,
|
|
25
|
+
{
|
|
26
|
+
group: {
|
|
27
|
+
...group,
|
|
28
|
+
user_ids: users
|
|
29
|
+
.filter(({ id }) => String(id) !== String(values.id))
|
|
30
|
+
.map(({ id }) => id),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
JSON_OPTS,
|
|
34
|
+
);
|
|
35
|
+
return normalizeResponse(response);
|
|
36
|
+
}
|
|
37
|
+
const request = action === "create" ? apiJsonPost : apiJsonPut;
|
|
38
|
+
const response = await request(url, { group: values }, JSON_OPTS);
|
|
39
|
+
return normalizeResponse(response);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const useGroupOperation = (action, id, onSuccess) => {
|
|
43
|
+
const { mutate } = useSWRConfig();
|
|
44
|
+
const active = useRef(false);
|
|
45
|
+
const [pending, setPending] = useState(false);
|
|
46
|
+
const key = action === "create" ? API_GROUPS : groupKey(id);
|
|
47
|
+
const { trigger, error } = useSWRMutation(key, writeGroup);
|
|
48
|
+
const submit = async (values) => {
|
|
49
|
+
if (active.current) return;
|
|
50
|
+
active.current = true;
|
|
51
|
+
setPending(true);
|
|
52
|
+
try {
|
|
53
|
+
const response = await trigger({ action, values }, { revalidate: false });
|
|
54
|
+
// A failed cache refresh must not turn a successful write into a retry.
|
|
55
|
+
await Promise.all([
|
|
56
|
+
...(action === "create"
|
|
57
|
+
? []
|
|
58
|
+
: [
|
|
59
|
+
action === "delete"
|
|
60
|
+
? mutate(key, undefined, { revalidate: false })
|
|
61
|
+
: mutate(key, response, { revalidate: false }),
|
|
62
|
+
]),
|
|
63
|
+
]);
|
|
64
|
+
// Refresh the list in the background; its latency must not block the
|
|
65
|
+
// completed write or keep the confirmation dialog pending.
|
|
66
|
+
mutate(
|
|
67
|
+
API_GROUPS,
|
|
68
|
+
async (current) => {
|
|
69
|
+
const result = await apiJson(API_GROUPS, JSON_OPTS);
|
|
70
|
+
return result ?? current;
|
|
71
|
+
},
|
|
72
|
+
{ revalidate: false },
|
|
73
|
+
).catch(() => {});
|
|
74
|
+
onSuccess?.();
|
|
75
|
+
} catch {
|
|
76
|
+
// SWR exposes the write error; leave the dialog and its draft open.
|
|
77
|
+
} finally {
|
|
78
|
+
active.current = false;
|
|
79
|
+
setPending(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
return { submit, pending, failed: Boolean(error) };
|
|
83
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import useSWR from "swr";
|
|
2
|
+
import { compile } from "path-to-regexp";
|
|
3
|
+
import { apiJson, apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
+
import { API_GROUP, API_GROUPS } from "../api";
|
|
5
|
+
import { API_USERS, API_USERS_SEARCH } from "../../users/api";
|
|
6
|
+
|
|
7
|
+
const EMPTY = [];
|
|
8
|
+
export const groupKey = (id) =>
|
|
9
|
+
id == null ? null : compile(API_GROUP)({ id: String(id) });
|
|
10
|
+
|
|
11
|
+
export const useGroups = () => {
|
|
12
|
+
const { data, error, isLoading, mutate } = useSWR(API_GROUPS, apiJson);
|
|
13
|
+
return {
|
|
14
|
+
groups: data?.data?.data ?? EMPTY,
|
|
15
|
+
hasData: data !== undefined,
|
|
16
|
+
error,
|
|
17
|
+
loading: isLoading,
|
|
18
|
+
retry: mutate,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const useGroup = (id) => {
|
|
23
|
+
const { data, error, isLoading, mutate } = useSWR(groupKey(id), apiJson);
|
|
24
|
+
const group = data?.data?.data;
|
|
25
|
+
return {
|
|
26
|
+
group,
|
|
27
|
+
members: group?.users ?? EMPTY,
|
|
28
|
+
hasData: data !== undefined,
|
|
29
|
+
error,
|
|
30
|
+
loading: isLoading,
|
|
31
|
+
retry: mutate,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const useGroupUsers = () => {
|
|
36
|
+
const { data, error, isLoading, mutate } = useSWR(API_USERS, apiJson);
|
|
37
|
+
return {
|
|
38
|
+
users: data?.data?.data ?? EMPTY,
|
|
39
|
+
hasData: data !== undefined,
|
|
40
|
+
error,
|
|
41
|
+
loading: isLoading,
|
|
42
|
+
retry: mutate,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const useFilteredGroupUsers = (enabled, role, domains) => {
|
|
47
|
+
const payload = {
|
|
48
|
+
...(role ? { roles: [String(role)] } : {}),
|
|
49
|
+
...(domains.length
|
|
50
|
+
? { domains: [...new Set(domains.map(String))].sort() }
|
|
51
|
+
: {}),
|
|
52
|
+
};
|
|
53
|
+
const key =
|
|
54
|
+
enabled && (role || domains.length) ? [API_USERS_SEARCH, payload] : null;
|
|
55
|
+
const { data, error, isLoading, mutate } = useSWR(key, ([url, filters]) =>
|
|
56
|
+
apiJsonPost(url, filters, JSON_OPTS),
|
|
57
|
+
);
|
|
58
|
+
return {
|
|
59
|
+
users: key ? (data?.data?.data ?? EMPTY) : null,
|
|
60
|
+
error,
|
|
61
|
+
loading: isLoading,
|
|
62
|
+
retry: mutate,
|
|
63
|
+
};
|
|
64
|
+
};
|
package/src/groups/routines.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import { createRoutine } from "redux-saga-routines";
|
|
2
2
|
|
|
3
|
-
export const clearGroup = createRoutine("CLEAR_GROUP");
|
|
4
3
|
export const clearGroupsSearch = createRoutine("CLEAR_GROUPS_SEARCH");
|
|
5
|
-
export const createGroup = createRoutine("CREATE_GROUP");
|
|
6
|
-
export const deleteGroup = createRoutine("DELETE_GROUP");
|
|
7
|
-
export const deleteGroupUser = createRoutine("DELETE_GROUP_USER");
|
|
8
|
-
export const fetchGroup = createRoutine("FETCH_GROUP");
|
|
9
|
-
export const fetchGroups = createRoutine("FETCH_GROUPS");
|
|
10
4
|
export const searchGroups = createRoutine("SEARCH_GROUPS");
|
|
11
|
-
export const updateGroup = createRoutine("UPDATE_GROUP");
|
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
import { createGroupRequestSaga } from "./createGroup";
|
|
2
|
-
import { deleteGroupRequestSaga } from "./deleteGroup";
|
|
3
|
-
import { fetchGroupRequestSaga } from "./fetchGroup";
|
|
4
|
-
import { fetchGroupsRequestSaga } from "./fetchGroups";
|
|
5
|
-
import { updateGroupRequestSaga } from "./updateGroup";
|
|
6
|
-
import { deleteGroupUserRequestSaga } from "./deleteGroupUser";
|
|
7
1
|
import { searchGroupsRequestSaga } from "./searchGroups";
|
|
8
2
|
|
|
9
|
-
export {
|
|
10
|
-
createGroupRequestSaga,
|
|
11
|
-
fetchGroupRequestSaga,
|
|
12
|
-
updateGroupRequestSaga,
|
|
13
|
-
deleteGroupRequestSaga,
|
|
14
|
-
fetchGroupsRequestSaga,
|
|
15
|
-
deleteGroupUserRequestSaga,
|
|
16
|
-
searchGroupsRequestSaga,
|
|
17
|
-
};
|
|
3
|
+
export { searchGroupsRequestSaga };
|
|
18
4
|
|
|
19
|
-
export default [
|
|
20
|
-
createGroupRequestSaga(),
|
|
21
|
-
fetchGroupRequestSaga(),
|
|
22
|
-
updateGroupRequestSaga(),
|
|
23
|
-
deleteGroupRequestSaga(),
|
|
24
|
-
fetchGroupsRequestSaga(),
|
|
25
|
-
deleteGroupUserRequestSaga(),
|
|
26
|
-
searchGroupsRequestSaga(),
|
|
27
|
-
];
|
|
5
|
+
export default [searchGroupsRequestSaga()];
|
package/src/hooks/useRoles.js
CHANGED
|
@@ -2,7 +2,10 @@ import useSWR from "swr";
|
|
|
2
2
|
import { apiJson } from "@truedat/core/services/api";
|
|
3
3
|
import { API_ROLES } from "../roles/api";
|
|
4
4
|
|
|
5
|
-
export const useRoles = () => {
|
|
6
|
-
const { data, error } = useSWR(
|
|
7
|
-
|
|
5
|
+
export const useRoles = (enabled = true) => {
|
|
6
|
+
const { data, error, isLoading, mutate } = useSWR(
|
|
7
|
+
enabled ? API_ROLES : null,
|
|
8
|
+
apiJson,
|
|
9
|
+
);
|
|
10
|
+
return { data: data?.data?.data, error, loading: isLoading, retry: mutate };
|
|
8
11
|
};
|