Skip to content

Commit 14d182e

Browse files
Merge branch 'main' into admin-ui-issue-2560
2 parents c124fe6 + 377359e commit 14d182e

File tree

8 files changed

+117
-119
lines changed

8 files changed

+117
-119
lines changed

admin-ui/app/locales/en/translation.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@
542542
"modality": "Modality",
543543
"dateAdded": "Date Added",
544544
"givenName": "Given Name",
545+
"sn": "Last Name",
546+
"mail": "Email",
545547
"resources": "Resources",
546548
"resourceId": "Resource id",
547549
"scopeSelection": "Scope Selection",

admin-ui/app/locales/es/translation.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@
542542
"modality": "Modalidad",
543543
"dateAdded": "Fecha de Alta",
544544
"givenName": "Nombre",
545+
"sn": "Apellido",
546+
"mail": "Correo",
545547
"resources": "Recursos",
546548
"resourceId": "ID de Recurso",
547549
"scopeSelection": "Selección de Ámbito",

admin-ui/app/locales/fr/translation.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@
569569
"ssl_trust_store_format": "Format de magasin de confiance SSL",
570570
"ssl_trust_store_pin": "NIP du magasin de confiance SSL",
571571
"state": "État",
572-
"status": "StatutStatut",
573572
"subject_type": "Type de sujet",
574573
"test_config": "Tester la configuration",
575574
"test_connection": "Tester la connexion",
@@ -605,6 +604,10 @@
605604
"nickName": "Surnom",
606605
"modality": "Modalité",
607606
"dateAdded": "date ajoutée",
607+
"givenName": "Prénom",
608+
"sn": "Nom de famille",
609+
"mail": "Courriel",
610+
"status": "Statut",
608611
"resources": "Ressources",
609612
"resourceId": "Identifiant de la ressource",
610613
"iconUrl": "URL de l'icône",

admin-ui/app/locales/pt/translation.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,9 @@
615615
"userName": "Nome do usuário",
616616
"nickName": "Apelido",
617617
"modality": "modalidade",
618+
"givenName": "Nome Próprio",
619+
"sn": "Sobrenome",
620+
"mail": "E-mail",
618621
"dateAdded": "data adicionada",
619622
"resources": "Recursos",
620623
"resourceId": "ID do recurso",
Lines changed: 98 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useContext, useEffect, useCallback, useMemo } from 'react'
1+
import React, { useContext, useEffect, useCallback, useMemo, memo } from 'react'
22
import { Container, Row, Col, Card, CardBody, Button, Badge, AvatarImage } from 'Components'
33
import { ErrorBoundary } from 'react-error-boundary'
44
import GluuErrorFallBack from '../Gluu/GluuErrorFallBack'
@@ -10,31 +10,47 @@ import styles from './styles'
1010
import { Box, Divider, Skeleton } from '@mui/material'
1111
import { getProfileDetails } from 'Redux/features/ProfileDetailsSlice'
1212
import { randomAvatar } from '../../../utilities'
13-
import getThemeColor from '../../../context/theme/config'
1413
import { useCedarling } from '@/cedarling'
1514
import { ADMIN_UI_RESOURCES } from '@/cedarling/utility'
1615
import { CEDAR_RESOURCE_SCOPES } from '@/cedarling/constants/resourceScopes'
1716
import { useAppNavigation, ROUTES } from '@/helpers/navigation'
17+
import customColors from '@/customColors'
1818
import type { AppDispatch, ProfileRootState, ThemeContextValue, CustomAttribute } from './types'
1919

20+
const JANS_ADMIN_UI_ROLE_ATTR = 'jansAdminUIRole'
21+
const SKELETON_WIDTH = '45%'
22+
const BADGE_PADDING = '4px 6px'
23+
const SKELETON_HEIGHT = 40
24+
25+
const skeletonCenterStyle = {
26+
display: 'flex',
27+
justifyContent: 'center',
28+
alignItems: 'center',
29+
} as const
30+
2031
const ProfileDetails: React.FC = () => {
2132
const { t } = useTranslation()
2233
const dispatch = useDispatch<AppDispatch>()
2334
const theme = useContext(ThemeContext) as ThemeContextValue
24-
const selectedTheme = useMemo(() => theme?.state?.theme ?? 'light', [theme?.state?.theme])
25-
const themeColors = useMemo(() => getThemeColor(selectedTheme), [selectedTheme])
2635
const { classes } = styles()
2736
const { navigateToRoute } = useAppNavigation()
2837

29-
// Set page title
38+
const selectedTheme = useMemo(() => theme?.state?.theme ?? 'light', [theme?.state?.theme])
39+
const buttonColor = useMemo(() => `primary-${selectedTheme}`, [selectedTheme])
40+
3041
SetTitle(t('titles.profile_detail'))
3142

3243
const { loading, profileDetails } = useSelector(
3344
(state: ProfileRootState) => state.profileDetailsReducer,
3445
)
35-
const { userinfo, token: authToken } = useSelector((state: ProfileRootState) => state.authReducer)
36-
const userInum = useMemo(() => userinfo?.inum, [userinfo?.inum])
46+
const authState = useSelector((state: ProfileRootState) => state.authReducer)
47+
const { userinfo, token: authToken } = authState
48+
const stateUserInum = (authState as { userInum?: string | null; hasSession?: boolean }).userInum
49+
const hasSession = (authState as { hasSession?: boolean }).hasSession ?? false
50+
51+
const userInum = useMemo(() => stateUserInum || userinfo?.inum, [stateUserInum, userinfo?.inum])
3752
const apiAccessToken = authToken?.access_token ?? null
53+
const canMakeApiCall = hasSession || !!apiAccessToken
3854

3955
const { authorizeHelper, hasCedarWritePermission } = useCedarling()
4056
const usersResourceId = useMemo(() => ADMIN_UI_RESOURCES.Users, [])
@@ -47,19 +63,19 @@ const ProfileDetails: React.FC = () => {
4763
const jansAdminUIRole = useMemo(
4864
() =>
4965
profileDetails?.customAttributes?.find(
50-
(att: CustomAttribute): boolean => att?.name === 'jansAdminUIRole',
66+
(att: CustomAttribute): boolean => att?.name === JANS_ADMIN_UI_ROLE_ATTR,
5167
),
5268
[profileDetails?.customAttributes],
5369
)
5470

5571
const avatarSrc = useMemo(() => randomAvatar(), [])
5672

5773
useEffect(() => {
58-
if (!apiAccessToken || !userInum) {
74+
if (!canMakeApiCall || !userInum) {
5975
return
6076
}
6177
dispatch(getProfileDetails({ pattern: userInum }))
62-
}, [apiAccessToken, dispatch, userInum])
78+
}, [canMakeApiCall, dispatch, userInum])
6379

6480
useEffect(() => {
6581
if (usersScopes && usersScopes.length > 0) {
@@ -79,14 +95,66 @@ const ProfileDetails: React.FC = () => {
7995
return jansAdminUIRole.values.map((role: string, index: number) => (
8096
<Badge
8197
key={`${role}-${index}`}
82-
style={{ padding: '4px 6px' }}
83-
color={`primary-${selectedTheme}`}
98+
style={{ padding: BADGE_PADDING, color: customColors.white }}
99+
color={buttonColor}
84100
className="me-1"
85101
>
86-
<span style={{ color: themeColors.fontColor }}>{role}</span>
102+
{role}
87103
</Badge>
88104
))
89-
}, [jansAdminUIRole?.values, selectedTheme, themeColors.fontColor])
105+
}, [jansAdminUIRole?.values, buttonColor])
106+
107+
const renderField = useCallback(
108+
(labelKey: string, value: string | undefined, isLoading: boolean) => {
109+
if (isLoading) {
110+
return <Skeleton animation="wave" />
111+
}
112+
return (
113+
<Box display={'flex'} justifyContent={'space-between'} alignItems={'center'} mb={1}>
114+
<Box fontWeight={700}>{t(labelKey)}</Box>
115+
<Box>{value || '-'}</Box>
116+
</Box>
117+
)
118+
},
119+
[t],
120+
)
121+
122+
const renderDisplayName = useMemo(() => {
123+
if (loading) {
124+
return (
125+
<Box display={'flex'} justifyContent={'center'} alignItems={'center'}>
126+
<Skeleton width={SKELETON_WIDTH} sx={skeletonCenterStyle} animation="wave" />
127+
</Box>
128+
)
129+
}
130+
return (
131+
<Box fontWeight={700} fontSize={'16px'} className="text-center mb-4">
132+
{profileDetails?.displayName}
133+
</Box>
134+
)
135+
}, [loading, profileDetails?.displayName])
136+
137+
const renderUserRolesField = useMemo(() => {
138+
if (loading) {
139+
return <Skeleton animation="wave" />
140+
}
141+
return (
142+
<Box display={'flex'} justifyContent={'space-between'} alignItems={'center'} mb={1} gap={3}>
143+
<Box fontWeight={700}>{t('titles.roles')}</Box>
144+
{roleBadges && (
145+
<Box
146+
display={'flex'}
147+
gap={'2px'}
148+
flexWrap={'wrap'}
149+
alignItems={'end'}
150+
justifyContent={'end'}
151+
>
152+
{roleBadges}
153+
</Box>
154+
)}
155+
</Box>
156+
)
157+
}, [loading, roleBadges, t])
90158

91159
return (
92160
<ErrorBoundary FallbackComponent={GluuErrorFallBack}>
@@ -101,120 +169,36 @@ const ProfileDetails: React.FC = () => {
101169
</Box>
102170
<Box display={'flex'} flexDirection={'column'} gap={2}>
103171
<Box display={'flex'} flexDirection={'column'} gap={1}>
104-
{loading ? (
105-
<Box display={'flex'} justifyContent={'center'} alignItems={'center'}>
106-
<Skeleton
107-
width={'45%'}
108-
sx={{
109-
display: 'flex',
110-
justifyContent: 'center',
111-
alignItems: 'center',
112-
}}
113-
animation="wave"
114-
/>
115-
</Box>
116-
) : (
117-
<Box fontWeight={700} fontSize={'16px'} className="text-center mb-4">
118-
{profileDetails?.displayName}
119-
</Box>
120-
)}
121-
{loading ? (
122-
<Skeleton animation="wave" />
123-
) : (
124-
<Box
125-
display={'flex'}
126-
justifyContent={'space-between'}
127-
alignItems={'center'}
128-
mb={1}
129-
>
130-
<Box fontWeight={700}>First Name</Box>
131-
<Box>{profileDetails?.givenName}</Box>
132-
</Box>
133-
)}
172+
{renderDisplayName}
173+
{renderField('fields.givenName', profileDetails?.givenName, loading)}
134174
<Divider />
135-
{loading ? (
136-
<Skeleton animation="wave" />
137-
) : (
138-
<Box
139-
display={'flex'}
140-
justifyContent={'space-between'}
141-
alignItems={'center'}
142-
mb={1}
143-
>
144-
<Box fontWeight={700}>Last Name</Box>
145-
<Box>{userinfo?.family_name}</Box>
146-
</Box>
175+
{renderField(
176+
'fields.sn',
177+
profileDetails?.customAttributes?.find(
178+
(att: CustomAttribute) => att?.name === 'sn',
179+
)?.values?.[0],
180+
loading,
147181
)}
148182
<Divider />
149-
{loading ? (
150-
<Skeleton animation="wave" />
151-
) : (
152-
<Box
153-
display={'flex'}
154-
justifyContent={'space-between'}
155-
alignItems={'center'}
156-
mb={1}
157-
>
158-
<Box fontWeight={700}>Email</Box>
159-
<Box>{profileDetails?.mail}</Box>
160-
</Box>
161-
)}
183+
{renderField('fields.mail', profileDetails?.mail, loading)}
162184
<Divider />
163-
{loading ? (
164-
<Skeleton animation="wave" />
165-
) : (
166-
<Box
167-
display={'flex'}
168-
justifyContent={'space-between'}
169-
alignItems={'center'}
170-
mb={1}
171-
gap={3}
172-
>
173-
<Box fontWeight={700}>User Roles</Box>
174-
{roleBadges && (
175-
<Box
176-
display={'flex'}
177-
gap={'2px'}
178-
flexWrap={'wrap'}
179-
alignItems={'end'}
180-
justifyContent={'end'}
181-
>
182-
{roleBadges}
183-
</Box>
184-
)}
185-
</Box>
186-
)}
185+
{renderUserRolesField}
187186
<Divider />
188-
{loading ? (
189-
<Skeleton animation="wave" />
190-
) : (
191-
<Box
192-
display={'flex'}
193-
justifyContent={'space-between'}
194-
alignItems={'center'}
195-
mb={1}
196-
>
197-
<Box fontWeight={700}>Status</Box>
198-
<Box>{profileDetails?.status || '-'}</Box>
199-
</Box>
200-
)}
187+
{renderField('fields.status', profileDetails?.status, loading)}
201188
<Divider />
202189
</Box>
203-
{canEditProfile ? (
190+
{canEditProfile && (
204191
<>
205192
{loading ? (
206-
<Skeleton animation="wave" height={40} />
193+
<Skeleton animation="wave" height={SKELETON_HEIGHT} />
207194
) : (
208-
<Button
209-
color={`primary-${selectedTheme}`}
210-
onClick={navigateToUserManagement}
211-
>
195+
<Button color={buttonColor} onClick={navigateToUserManagement}>
212196
<i className="fa fa-pencil me-2" />
213197
{t('actions.edit')}
214198
</Button>
215199
)}
216200
</>
217-
) : null}
201+
)}
218202
</Box>
219203
</React.Fragment>
220204
</CardBody>
@@ -226,4 +210,4 @@ const ProfileDetails: React.FC = () => {
226210
)
227211
}
228212

229-
export default React.memo(ProfileDetails)
213+
export default memo(ProfileDetails)

admin-ui/app/routes/Apps/Profile/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ export interface ProfileDetails {
1111
mail?: string
1212
status?: string
1313
inum?: string
14+
sn?: string
15+
surname?: string
1416
customAttributes?: CustomAttribute[]
17+
[key: string]: unknown // Allow additional properties from API
1518
}
1619

1720
export interface UserInfo {
@@ -34,6 +37,7 @@ export interface AuthState {
3437
userinfo?: UserInfo
3538
token?: AuthToken | null
3639
issuer?: string | null
40+
userInum?: string | null
3741
}
3842

3943
export interface ProfileRootState {

admin-ui/plugins/user-management/components/UserAddPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type { CustomUser } from '../types/UserApiTypes'
1717

1818
function UserAddPage() {
1919
const dispatch = useDispatch()
20-
const { navigateToRoute } = useAppNavigation()
20+
const { navigateBack } = useAppNavigation()
2121
const queryClient = useQueryClient()
2222
const { t } = useTranslation()
2323
const { data: attributesData, isLoading: loadingAttributes } = useGetAttributes({
@@ -35,7 +35,7 @@ function UserAddPage() {
3535
await logUserCreation(data, variables.data)
3636
triggerUserWebhook(data)
3737
queryClient.invalidateQueries({ queryKey: getGetUserQueryKey() })
38-
navigateToRoute(ROUTES.USER_MANAGEMENT)
38+
navigateBack(ROUTES.USER_MANAGEMENT)
3939
},
4040
onError: (error: unknown) => {
4141
const errMsg = getErrorMessage(error)

admin-ui/plugins/user-management/components/UserEditPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { AXIOS_INSTANCE } from '../../../api-client'
3232

3333
function UserEditPage() {
3434
const dispatch = useDispatch()
35-
const { navigateToRoute } = useAppNavigation()
35+
const { navigateToRoute, navigateBack } = useAppNavigation()
3636
const location = useLocation()
3737
const queryClient = useQueryClient()
3838
const { t } = useTranslation()
@@ -81,7 +81,7 @@ function UserEditPage() {
8181
await logUserUpdate(data, variables.data)
8282
triggerUserWebhook(data)
8383
queryClient.invalidateQueries({ queryKey: getGetUserQueryKey() })
84-
navigateToRoute(ROUTES.USER_MANAGEMENT)
84+
navigateBack(ROUTES.USER_MANAGEMENT)
8585
},
8686
onError: (error: unknown) => {
8787
const errMsg = getErrorMessage(error)

0 commit comments

Comments
 (0)