Skip to content

Commit 98ffb30

Browse files
jonenstsBouzols
andauthored
show names instead of ids in directory content (#512)
* show names instead of ids in directory content Signed-off-by: Jon HARPER <> Co-authored-by: sBouzols <[email protected]>
1 parent 2d32e11 commit 98ffb30

File tree

4 files changed

+111
-16
lines changed

4 files changed

+111
-16
lines changed

src/components/utils/renderers/user-cell-renderer.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,35 @@
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
7-
import { Box, Chip, Tooltip } from '@mui/material';
7+
import { Avatar, Box, Theme, Tooltip } from '@mui/material';
88

9-
const abbreviationFromUserName = (name: string | null) => {
10-
if (name === null) {
9+
function getAbbreviationFromUserName(name: string) {
10+
if (name === null || name.trim() === '') {
1111
return '';
1212
}
13-
const tab = name.split(' ').map((x) => x.charAt(0));
14-
if (tab.length === 1) {
15-
return tab[0];
13+
const splittedName = name.split(' ');
14+
if (splittedName.length > 1) {
15+
return `${splittedName[0][0]}${splittedName[splittedName.length - 1][0]}`;
1616
}
17-
return tab[0] + tab[tab.length - 1];
18-
};
17+
return `${splittedName[0][0]}`;
18+
}
1919

2020
const styles = {
21-
chip: {
21+
avatar: (theme: Theme) => ({
2222
cursor: 'pointer',
23-
},
23+
height: '32px',
24+
width: '32px',
25+
fontSize: theme.typography.fontSize,
26+
}),
2427
};
2528

2629
export type UserCellRendererProps = { value: string };
2730

2831
export function UserCellRenderer({ value }: Readonly<UserCellRendererProps>) {
2932
return (
30-
<Box>
33+
<Box sx={{ display: 'inline-flex', verticalAlign: 'middle' }}>
3134
<Tooltip title={value} placement="right">
32-
<Chip sx={styles.chip} label={abbreviationFromUserName(value)} />
35+
<Avatar sx={styles.avatar}>{getAbbreviationFromUserName(value)}</Avatar>
3336
</Tooltip>
3437
</Box>
3538
);

src/hooks/useDirectoryContent.ts

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,29 @@
66
*/
77

88
import { useSelector } from 'react-redux';
9-
import { useCallback, useEffect, useRef, useState } from 'react';
9+
import { useRef, useEffect, useCallback, useState, useMemo } from 'react';
1010
import { ElementAttributes, fetchElementsInfos, useSnackMessage } from '@gridsuite/commons-ui';
1111
import { UUID } from 'crypto';
12+
import { UsersIdentities, UsersIdentitiesMap } from 'utils/user-identities.type';
13+
import { fetchUsersIdentities } from '../utils/rest-api';
1214
import { AppState } from '../redux/types';
1315

16+
const getName = (userId: string, data: UsersIdentitiesMap): string => {
17+
const firstName = data?.[userId]?.firstName;
18+
const lastName = data?.[userId]?.lastName;
19+
if (firstName && lastName) {
20+
return `${firstName} ${lastName}`;
21+
}
22+
if (firstName) {
23+
return firstName;
24+
}
25+
if (lastName) {
26+
return lastName;
27+
}
28+
// fallback to id
29+
return userId;
30+
};
31+
1432
export const useDirectoryContent = () => {
1533
const currentChildren = useSelector((state: AppState) => state.currentChildren);
1634
const [childrenMetadata, setChildrenMetadata] = useState<Record<UUID, ElementAttributes>>({});
@@ -40,12 +58,35 @@ export const useDirectoryContent = () => {
4058

4159
const metadata: Record<UUID, ElementAttributes> = {};
4260
const childrenToFetchElementsInfos = Object.values(currentChildren).map((e) => e.elementUuid);
61+
62+
const fetchUsersIdentitiesPromise = fetchUsersIdentities(childrenToFetchElementsInfos).catch(() => {
63+
// Last resort, server down, error 500, fallback to subs as users Identities
64+
// We write this code to have the same behavior as when there are partial results,
65+
// (missing users identities), see getName()
66+
const fallbackUsersIdentities: UsersIdentities = {
67+
data: Object.fromEntries(
68+
[...new Set(currentChildren.flatMap((e) => [e.owner, e.lastModifiedBy]))].map((sub) => [
69+
sub,
70+
{ sub, firstName: '', lastName: '' },
71+
])
72+
),
73+
errors: {},
74+
};
75+
76+
return fallbackUsersIdentities;
77+
});
78+
4379
if (childrenToFetchElementsInfos.length > 0) {
44-
fetchElementsInfos(childrenToFetchElementsInfos)
80+
Promise.all([
81+
fetchUsersIdentitiesPromise, // TODO cache user identities across elements
82+
fetchElementsInfos(childrenToFetchElementsInfos),
83+
])
4584
.then((res) => {
4685
// discarding request for older directory
4786
if (previousData.current === currentChildren) {
48-
res.forEach((e) => {
87+
res[1].forEach((e) => {
88+
e.owner = getName(e.owner, res[0].data);
89+
e.lastModifiedBy = getName(e.lastModifiedBy, res[0].data);
4990
metadata[e.elementUuid] = e;
5091
});
5192
setChildrenMetadata(metadata);
@@ -59,5 +100,17 @@ export const useDirectoryContent = () => {
59100
}
60101
}, [handleError, currentChildren]);
61102

62-
return [currentChildren, childrenMetadata] as const;
103+
// TODO remove this when global user identity caching is implemented
104+
const currentChildrenWithOwnerNames = useMemo(() => {
105+
if (!currentChildren) {
106+
return currentChildren;
107+
}
108+
return currentChildren.map((x) => ({
109+
...x,
110+
owner: childrenMetadata?.[x.elementUuid]?.owner,
111+
lastModifiedBy: childrenMetadata?.[x.elementUuid]?.lastModifiedBy,
112+
}));
113+
}, [currentChildren, childrenMetadata]);
114+
115+
return [currentChildrenWithOwnerNames, childrenMetadata] as const;
63116
};

src/utils/rest-api.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { CONTINGENCY_ENDPOINTS } from './constants-endpoints';
2727
import { AppState } from '../redux/types';
2828
import { CriteriaBasedData } from '../components/dialogs/contingency-list/contingency-list-utils';
2929
import { PrepareContingencyListForBackend } from '../components/dialogs/contingency-list-helper';
30+
import { UsersIdentities } from './user-identities.type';
3031

3132
const PREFIX_USER_ADMIN_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/user-admin`;
3233
const PREFIX_CONFIG_NOTIFICATION_WS = `${import.meta.env.VITE_WS_GATEWAY}/config-notification`;
@@ -183,6 +184,15 @@ export function fetchVersion() {
183184
});
184185
}
185186

187+
export function fetchUsersIdentities(elementUuids: string[]) {
188+
console.info('fetching users identities for elements : %s', elementUuids);
189+
const idsParams = getRequestParamFromList('ids', elementUuids).toString();
190+
const fetchParams = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/elements/users-identities?${idsParams}`;
191+
return backendFetchJson(fetchParams, {
192+
method: 'get',
193+
}) as Promise<UsersIdentities>;
194+
}
195+
186196
export type ConfigParameter =
187197
| {
188198
readonly name: typeof PARAM_LANGUAGE;

src/utils/user-identities.type.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright (c) 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
export type UserIdentity = {
9+
firstName: string;
10+
lastName: string;
11+
};
12+
13+
export type UsersIdentitiesMap = {
14+
[sub: string]: UserIdentity;
15+
};
16+
17+
export type UserIdentityError = {
18+
sub: string;
19+
code: string;
20+
};
21+
22+
export type UsersIdentitiesErrorMap = {
23+
[sub: string]: UserIdentity;
24+
};
25+
26+
export type UsersIdentities = {
27+
data: UsersIdentitiesMap;
28+
errors: UsersIdentitiesErrorMap;
29+
};

0 commit comments

Comments
 (0)