@deix/rossini-identity-management 5.1.3 → 5.2.1

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.
@@ -1,8 +1,7 @@
1
- import React from 'react';
2
1
  import { Group } from '../../types/groups';
3
2
  interface GroupUsersProps {
4
3
  group: Group;
5
4
  }
6
- declare const GroupUsers: ({ group }: GroupUsersProps) => React.JSX.Element;
5
+ declare const GroupUsers: ({ group }: GroupUsersProps) => import("react").JSX.Element;
7
6
  export default GroupUsers;
8
7
  //# sourceMappingURL=GroupUsers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GroupUsers.d.ts","sourceRoot":"","sources":["../../../src/components/groups/GroupUsers.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAgC3C,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,QAAA,MAAM,UAAU,cAAe,eAAe,sBAoD7C,CAAC;eAEa,UAAU"}
1
+ {"version":3,"file":"GroupUsers.d.ts","sourceRoot":"","sources":["../../../src/components/groups/GroupUsers.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AA4B3C,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,QAAA,MAAM,UAAU,cAAe,eAAe,gCA2D7C,CAAC;eAEa,UAAU"}
@@ -1,16 +1,16 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Button } from '@mui/material';
3
2
  import AddIcon from '@mui/icons-material/Add';
4
- import { useAddUserToGroup, useRemoveUserFromGroup, useUsers, } from '../../utils/hooks/users';
5
- import { useState } from 'react';
3
+ import { Box, Button } from '@mui/material';
6
4
  import { useQueryClient } from '@tanstack/react-query';
7
- import AddItemDialog from '../AddItemDialog';
8
- import Table from '../tables/Table';
9
- import { useGroupUsers } from '../../utils/hooks/groups';
5
+ import { useState } from 'react';
10
6
  import groupsTranslations from '../../lang/groups';
11
7
  import usersTranslations from '../../lang/users';
8
+ import { useGroupUsers } from '../../utils/hooks/groups';
12
9
  import { useAPIClient } from '../../utils/hooks/useAPIClient';
10
+ import { useAddUserToGroup, useRemoveUserFromGroup, useUsers, } from '../../utils/hooks/users';
13
11
  import useTranslate from '../../utils/hooks/useTranslate';
12
+ import AddItemDialog from '../AddItemDialog';
13
+ import Table from '../tables/Table';
14
14
  const groupUsersTableColumns = [
15
15
  {
16
16
  field: 'username',
@@ -38,9 +38,16 @@ const GroupUsers = ({ group }) => {
38
38
  if (value) {
39
39
  addUserToGroup({ userId: value?.value, groupId: group.id });
40
40
  }
41
- }, title: groupsTranslations.addUser, options: users?.map((user) => ({
42
- value: user.id,
43
- label: user.username || '',
44
- })) })] }));
41
+ }, title: groupsTranslations.addUser, options: users?.map((user) => {
42
+ const fullName = [user.firstName, user.lastName]
43
+ .filter(Boolean)
44
+ .join(' ');
45
+ return {
46
+ value: user.id,
47
+ label: fullName
48
+ ? `${fullName} (${user.username})`
49
+ : user.username || '',
50
+ };
51
+ }) })] }));
45
52
  };
46
53
  export default GroupUsers;
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { default as Users } from './components/users/Users';
2
2
  export { default as Groups } from './components/groups/Groups';
3
- export type { User, KeycloakQueryParams } from './types/users';
3
+ export type { User, KeycloakQueryParams, UserByIdResult, UseUsersByIdsOptions, } from './types/users';
4
4
  export type { Group } from './types/groups';
5
5
  export { default as SearchField } from './components/SearchField';
6
6
  export { default as DeleteConfirmationDialog } from './components/DeleteConfirmationDialog';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC/D,YAAY,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EACV,IAAI,EACJ,mBAAmB,EACnB,cAAc,EACd,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC"}
@@ -7,8 +7,20 @@ export interface User {
7
7
  enabled?: boolean;
8
8
  email?: string;
9
9
  emailVerified?: boolean;
10
+ serviceAccountClientId?: string;
10
11
  }
11
12
  export interface KeycloakQueryParams {
12
13
  briefRepresentation?: boolean;
13
14
  }
15
+ export interface UserByIdResult {
16
+ data?: User;
17
+ isLoading: boolean;
18
+ isError: boolean;
19
+ }
20
+ export interface UseUsersByIdsOptions {
21
+ /** Milliseconds a cached user is considered fresh. Default: 60000. */
22
+ staleTime?: number;
23
+ /** Retries on error (true/false or a number). Default: false. */
24
+ retry?: boolean | number;
25
+ }
14
26
  //# sourceMappingURL=users.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/types/users.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B"}
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/types/users.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AASD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B"}
@@ -1,12 +1,13 @@
1
1
  import { QueryClient, UseQueryResult } from '@tanstack/react-query';
2
2
  import { AxiosInstance } from 'axios';
3
- import { KeycloakQueryParams, User } from '../../types/users';
4
- import { Group } from '../../types/groups';
5
3
  import { UserFormFields } from '../../components/users/UserForm';
4
+ import { Group } from '../../types/groups';
5
+ import { KeycloakQueryParams, User, UserByIdResult, UseUsersByIdsOptions } from '../../types/users';
6
6
  export declare const getUsers: (apiClient: AxiosInstance, username?: string, group?: string, offset?: number, limit?: number, params?: KeycloakQueryParams) => Promise<User[]>;
7
7
  export declare const useUsers: (apiClient: AxiosInstance, group?: string, offset?: number, limit?: number, params?: KeycloakQueryParams) => UseQueryResult<User[] | undefined, Error>;
8
8
  export declare const getUser: (id: string, apiClient: AxiosInstance) => Promise<User>;
9
9
  export declare const useUser: (id: string, apiClient: AxiosInstance) => UseQueryResult<User | undefined, Error>;
10
+ export declare const useUsersByIds: (apiClient: AxiosInstance, ids: (string | undefined)[], options?: UseUsersByIdsOptions) => UserByIdResult[];
10
11
  export declare const addUser: ({ newUser, sendEmailOnCreation, apiClient, groups, }: {
11
12
  newUser: User;
12
13
  sendEmailOnCreation: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/users.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,cAAc,EAGf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAGtC,OAAO,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAIjE,eAAO,MAAM,QAAQ,cACR,aAAa,aACb,MAAM,UACT,MAAM,WACN,MAAM,UACP,MAAM,WACJ,mBAAmB,oBA8B7B,CAAC;AAEF,eAAO,MAAM,QAAQ,cACR,aAAa,UAChB,MAAM,WACN,MAAM,UACP,MAAM,WACJ,mBAAmB,KAC3B,cAAc,CAAC,IAAI,EAAE,GAAG,SAAS,EAAE,KAAK,CAc1C,CAAC;AAGF,eAAO,MAAM,OAAO,OAAc,MAAM,aAAa,aAAa,kBAGjE,CAAC;AAEF,eAAO,MAAM,OAAO,OACd,MAAM,aACC,aAAa,KACvB,cAAc,CAAC,IAAI,GAAG,SAAS,EAAE,KAAK,CAMxC,CAAC;AAGF,eAAO,MAAM,OAAO,yDAKjB;IACD,OAAO,EAAE,IAAI,CAAC;IACd,mBAAmB,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,kBAWA,CAAC;AAEF,eAAO,MAAM,UAAU,gBACR,WAAW,aACb,aAAa;UAQd,cAAc;YACZ,MAAM,EAAE,GAAG,SAAS;WA6BjC,CAAC;AAiBF,eAAO,MAAM,aAAa,gBACX,WAAW,aACb,aAAa,kFA6BzB,CAAC;AAgBF,eAAO,MAAM,aAAa,gBACX,WAAW,aACb,aAAa,4GA6BzB,CAAC;AAcF,eAAO,MAAM,aAAa,OACpB,MAAM,GAAG,SAAS,aACX,aAAa,KACvB,cAAc,CAAC,KAAK,EAAE,GAAG,SAAS,EAAE,KAAK,CAM3C,CAAC;AAmBF,eAAO,MAAM,iBAAiB,gBACf,WAAW,aACb,aAAa;;;;YAIsB,MAAM;aAAW,MAAM;WA8BtE,CAAC;AAmBF,eAAO,MAAM,sBAAsB,gBACpB,WAAW,aACb,aAAa;;;;YAIsB,MAAM;aAAW,MAAM;WA8BtE,CAAC;AAkBF,eAAO,MAAM,0BAA0B,gBACxB,WAAW,aACb,aAAa;;0BAoBzB,CAAC;AAEF,eAAO,MAAM,aAAa,cACb,aAAa,aACb,MAAM,UACT,MAAM,oBAmBf,CAAC;AAEF,eAAO,MAAM,aAAa,cACb,aAAa,UAChB,MAAM,KACb,cAAc,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,CAM1C,CAAC"}
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/users.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAKX,cAAc,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAGtC,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACL,mBAAmB,EACnB,IAAI,EACJ,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAO3B,eAAO,MAAM,QAAQ,cACR,aAAa,aACb,MAAM,UACT,MAAM,WACN,MAAM,UACP,MAAM,WACJ,mBAAmB,oBA2B7B,CAAC;AAEF,eAAO,MAAM,QAAQ,cACR,aAAa,UAChB,MAAM,WACN,MAAM,UACP,MAAM,WACJ,mBAAmB,KAC3B,cAAc,CAAC,IAAI,EAAE,GAAG,SAAS,EAAE,KAAK,CAc1C,CAAC;AAGF,eAAO,MAAM,OAAO,OAAc,MAAM,aAAa,aAAa,kBAGjE,CAAC;AAEF,eAAO,MAAM,OAAO,OACd,MAAM,aACC,aAAa,KACvB,cAAc,CAAC,IAAI,GAAG,SAAS,EAAE,KAAK,CAMxC,CAAC;AAoBF,eAAO,MAAM,aAAa,cACb,aAAa,OACnB,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,YACjB,oBAAoB,KAC7B,cAAc,EAUb,CAAC;AAGL,eAAO,MAAM,OAAO,yDAKjB;IACD,OAAO,EAAE,IAAI,CAAC;IACd,mBAAmB,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,kBAWA,CAAC;AAEF,eAAO,MAAM,UAAU,gBACR,WAAW,aACb,aAAa;UAQd,cAAc;YACZ,MAAM,EAAE,GAAG,SAAS;WA6BjC,CAAC;AAiBF,eAAO,MAAM,aAAa,gBACX,WAAW,aACb,aAAa,kFA6BzB,CAAC;AAgBF,eAAO,MAAM,aAAa,gBACX,WAAW,aACb,aAAa,4GA6BzB,CAAC;AAcF,eAAO,MAAM,aAAa,OACpB,MAAM,GAAG,SAAS,aACX,aAAa,KACvB,cAAc,CAAC,KAAK,EAAE,GAAG,SAAS,EAAE,KAAK,CAM3C,CAAC;AAmBF,eAAO,MAAM,iBAAiB,gBACf,WAAW,aACb,aAAa;;;;YAIsB,MAAM;aAAW,MAAM;WA8BtE,CAAC;AAmBF,eAAO,MAAM,sBAAsB,gBACpB,WAAW,aACb,aAAa;;;;YAIsB,MAAM;aAAW,MAAM;WA8BtE,CAAC;AAkBF,eAAO,MAAM,0BAA0B,gBACxB,WAAW,aACb,aAAa;;0BAoBzB,CAAC;AAEF,eAAO,MAAM,aAAa,cACb,aAAa,aACb,MAAM,UACT,MAAM,oBAmBf,CAAC;AAEF,eAAO,MAAM,aAAa,cACb,aAAa,UAChB,MAAM,KACb,cAAc,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,CAM1C,CAAC"}
@@ -1,7 +1,8 @@
1
- import { useMutation, useQuery, } from '@tanstack/react-query';
1
+ import { useMutation, useQueries, useQuery, } from '@tanstack/react-query';
2
2
  import { enqueueSnackbar } from 'notistack';
3
3
  import usersTranslations from '../../lang/users';
4
4
  import useTranslate from './useTranslate';
5
+ const isServiceAccount = (u) => !!u.serviceAccountClientId || !!u.username?.startsWith('service-account-');
5
6
  // Get list of users
6
7
  export const getUsers = async (apiClient, username, group, offset = 0, limit = 1000, params) => {
7
8
  const { briefRepresentation } = params ?? {};
@@ -10,26 +11,20 @@ export const getUsers = async (apiClient, username, group, offset = 0, limit = 1
10
11
  const { data: groupData } = await apiClient.get(`/groups`, {
11
12
  params: { max: 1, search: group, exact: 'true' },
12
13
  });
13
- if (groupData.length === 1) {
14
- const { data } = await apiClient.get(`/groups/${groupData[0].id}/members`, { params: { max: limit, first: offset, ...extraParams } });
15
- return data;
16
- }
17
- else {
14
+ if (groupData.length !== 1)
18
15
  return [];
19
- }
20
- }
21
- else {
22
- const { data } = await apiClient.get(`/users`, {
23
- params: {
24
- max: limit,
25
- first: offset,
26
- username,
27
- exact: 'true',
28
- ...extraParams,
29
- },
30
- });
31
- return data;
16
+ const { data } = await apiClient.get(`/groups/${groupData[0].id}/members`, { params: { max: limit, first: offset, ...extraParams } });
17
+ return data.filter((u) => !isServiceAccount(u));
32
18
  }
19
+ const { data } = await apiClient.get(`/users`, {
20
+ params: {
21
+ max: limit,
22
+ first: offset,
23
+ ...(username ? { username, exact: 'true' } : {}),
24
+ ...extraParams,
25
+ },
26
+ });
27
+ return data;
33
28
  };
34
29
  export const useUsers = (apiClient, group, offset = 0, limit = 1000, params) => {
35
30
  return useQuery({
@@ -58,6 +53,30 @@ export const useUser = (id, apiClient) => {
58
53
  staleTime: 0,
59
54
  });
60
55
  };
56
+ // Get multiple users by id
57
+ // Defined OUTSIDE the hook: its identity must be stable, otherwise React Query
58
+ // recomputes the combined result on every render and loses structural sharing.
59
+ // Without combine, useQueries returns a new array on every render.
60
+ const combineUsers = (results) => results.map((r) => ({
61
+ data: r.data,
62
+ isLoading: r.isLoading,
63
+ isError: r.isError,
64
+ }));
65
+ // Loads several Keycloak users with one GET /users/{id} each.
66
+ // useUser cannot be used here because hooks cannot be called in a loop.
67
+ // It uses the SAME queryKey as useUser (['user', id]), so the cache is shared with useUser.
68
+ // Results are in the same order as ids; an undefined id triggers no request.
69
+ // Each result is a reduced state (see UserByIdResult), not the full UseQueryResult.
70
+ export const useUsersByIds = (apiClient, ids, options) => useQueries({
71
+ queries: ids.map((id) => ({
72
+ queryKey: ['user', id],
73
+ queryFn: () => getUser(id, apiClient),
74
+ enabled: !!id,
75
+ staleTime: options?.staleTime ?? 60 * 1000,
76
+ retry: options?.retry ?? false,
77
+ })),
78
+ combine: combineUsers,
79
+ });
61
80
  // Add user
62
81
  export const addUser = async ({ newUser, sendEmailOnCreation, apiClient, groups, }) => {
63
82
  await apiClient.post(`/users`, { ...newUser, groups });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deix/rossini-identity-management",
3
- "version": "5.1.3",
3
+ "version": "5.2.1",
4
4
  "main": "lib/index.js",
5
5
  "repository": {
6
6
  "type": "git",
@@ -62,7 +62,7 @@
62
62
  "eslint-plugin-prettier": "^5.5.6",
63
63
  "eslint-plugin-storybook": "^10.6.0",
64
64
  "json-server": "^1.0.0-beta.15",
65
- "next": "~16.3.8",
65
+ "next": "~16.4.3",
66
66
  "react": "^19.3.0",
67
67
  "react-dom": "19.3.0",
68
68
  "remark-mdx": "^3.0.0",