Skip to content

Commit afeacc1

Browse files
committed
feat: add status column, status update functionality, export to csv and abort ctrl
1 parent e58f137 commit afeacc1

File tree

13 files changed

+153
-54
lines changed

13 files changed

+153
-54
lines changed

src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/PermissionGroupList.component.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useMemo } from 'react'
1+
import React, { useCallback, useMemo, useRef } from 'react'
22
import {
33
ErrorScreenNotAuthorized,
44
ERROR_EMPTY_SCREEN,
@@ -20,6 +20,7 @@ import useUrlFilters from '../../shared/hooks/useUrlFilters'
2020
import SortableTableHeaderCell from '../../../../../components/common/SortableTableHeaderCell'
2121
import FiltersEmptyState from '../../shared/components/FilterEmptyState/FilterEmptyState.component'
2222
import NoPermissionGroups from './NoPermissionGroups'
23+
import { abortPreviousRequests, getIsRequestAborted } from '../../utils'
2324

2425
const PermissionGroupInfoBar = importComponentFromFELibrary('PermissionGroupInfoBar', noop, 'function')
2526

@@ -46,9 +47,19 @@ const PermissionGroupList = () => {
4647
}),
4748
[pageSize, offset, searchKey, sortBy, sortOrder],
4849
)
49-
const [isLoading, result, error, reload] = useAsync(() => getPermissionGroupList(filterConfig), [filterConfig])
50+
const abortControllerRef = useRef(new AbortController())
51+
const [isLoading, result, error, reload] = useAsync(
52+
() =>
53+
abortPreviousRequests(
54+
() => getPermissionGroupList(filterConfig, abortControllerRef.current.signal),
55+
abortControllerRef,
56+
),
57+
[filterConfig],
58+
)
5059
const { isAutoAssignFlowEnabled } = useAuthorizationContext()
5160

61+
const showLoadingState = isLoading || getIsRequestAborted(error)
62+
5263
const getPermissionGroupDataForExport = useCallback(
5364
() =>
5465
getPermissionGroupList({
@@ -62,7 +73,7 @@ const PermissionGroupList = () => {
6273
[filterConfig],
6374
)
6475

65-
if (!isLoading) {
76+
if (!showLoadingState) {
6677
if (error) {
6778
if ([API_STATUS_CODES.PERMISSION_DENIED, API_STATUS_CODES.UNAUTHORIZED].includes(error.code)) {
6879
return (
@@ -72,7 +83,7 @@ const PermissionGroupList = () => {
7283
/>
7384
)
7485
}
75-
return <Reload reload={reload} />
86+
return <Reload reload={reload} className="flex-grow-1" />
7687
}
7788

7889
// The null state is shown only when filters are not applied
@@ -81,18 +92,17 @@ const PermissionGroupList = () => {
8192
}
8293
}
8394

95+
// Disable the filter actions
96+
const isActionsDisabled = showLoadingState || !(result.totalCount && result.permissionGroups.length)
97+
8498
const sortByName = () => {
8599
handleSorting(SortableKeys.name)
86100
}
87101

88-
const handleClearFilters = () => {
89-
clearFilters()
90-
}
91-
92102
return (
93103
<div className="flexbox-col dc__gap-8 flex-grow-1">
94104
<PermissionGroupListHeader
95-
disabled={isLoading || !(result.totalCount && result.permissionGroups.length)}
105+
disabled={isActionsDisabled}
96106
handleSearch={handleSearch}
97107
initialSearchText={searchKey}
98108
getDataToExport={getPermissionGroupDataForExport}
@@ -111,12 +121,12 @@ const PermissionGroupList = () => {
111121
sortOrder={sortOrder}
112122
isSorted={sortBy === SortableKeys.name}
113123
triggerSorting={sortByName}
114-
disabled={isLoading}
124+
disabled={isActionsDisabled}
115125
/>
116126
<span>Description</span>
117127
<span />
118128
</div>
119-
{isLoading ? (
129+
{showLoadingState ? (
120130
permissionGroupLoading.map((permissionGroup) => (
121131
<div
122132
className="user-permission__row pl-20 pr-20 show-shimmer-loading"
@@ -153,7 +163,7 @@ const PermissionGroupList = () => {
153163
)}
154164
</div>
155165
) : (
156-
<FiltersEmptyState clearFilters={handleClearFilters} />
166+
<FiltersEmptyState clearFilters={clearFilters} />
157167
)}
158168
</div>
159169
)

src/Pages/GlobalConfigurations/Authorization/UserAndGroupPermissions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ const UserAndGroupPermissions = () => {
216216
const [userGroups, projects, environments, chartGroups, envClustersList, customRolesList] = data
217217

218218
return (
219-
<div className="flexbox-col flex-grow-1 h-100 w-100">
219+
<div className="flexbox-col flex-grow-1 h-100 w-100 dc__content-center">
220220
<AuthorizationProvider
221221
// TODO (v3): Simplify and move these to API service instead
222222
value={{

src/Pages/GlobalConfigurations/Authorization/UserPermissions/AddEdit/UserForm.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ const CreatableChipStyle = {
6363
}),
6464
}
6565

66-
// TODO (v1): Replace with enterprise check
67-
const showStatus = false
68-
6966
const UserForm = ({ isAddMode, userData = null }: { isAddMode: boolean; userData: User }) => {
7067
const { serverMode } = useMainContext()
7168

@@ -390,9 +387,8 @@ const UserForm = ({ isAddMode, userData = null }: { isAddMode: boolean; userData
390387
<span className="cn-5">/</span>
391388
<span className="cn-9 fw-6 dc__ellipsis-right">{isAddMode ? 'Add User' : userData.emailId}</span>
392389
</div>
393-
{(showStatus || !isAddMode) && (
390+
{!isAddMode && (
394391
<div className="flex dc__content-start dc__gap-12">
395-
{showStatus && <div>status</div>}
396392
{!isAddMode && (
397393
<button
398394
disabled={submitting}

src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/ExportUserPermissionsToCsv.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { FILE_NAMES, USER_EXPORT_HEADER_ROW } from '../../../../../components/co
88
import { useAuthorizationContext } from '../../AuthorizationProvider'
99
import { getRoleFiltersToExport } from '../../utils'
1010
import { LAST_LOGIN_TIME_NULL_STATE } from '../constants'
11+
import { importComponentFromFELibrary } from '../../../../../components/common'
12+
13+
const getStatusExportText = importComponentFromFELibrary('getStatusExportText', null, 'function')
14+
const showStatus = !!getStatusExportText
1115

1216
const ExportUserPermissionsToCsv = ({
1317
disabled,
@@ -35,7 +39,11 @@ const ExportUserPermissionsToCsv = ({
3539
const _userData = {
3640
emailId: _user.emailId,
3741
userId: _user.id,
38-
// TODO (v1): Add support for status column
42+
...(showStatus
43+
? {
44+
status: getStatusExportText(_user.userStatus, _user.timeToLive),
45+
}
46+
: {}),
3947
lastLoginTime:
4048
_user.lastLoginTime === LAST_LOGIN_TIME_NULL_STATE
4149
? _user.lastLoginTime

src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionList.component.tsx

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useMemo } from 'react'
1+
import React, { useCallback, useMemo, useRef } from 'react'
22
import {
33
ErrorScreenNotAuthorized,
44
ERROR_EMPTY_SCREEN,
@@ -18,6 +18,7 @@ import SortableTableHeaderCell from '../../../../../components/common/SortableTa
1818
import FiltersEmptyState from '../../shared/components/FilterEmptyState/FilterEmptyState.component'
1919
import NoUsers from './NoUsers'
2020
import { importComponentFromFELibrary } from '../../../../../components/common'
21+
import { abortPreviousRequests, getIsRequestAborted } from '../../utils'
2122

2223
const StatusHeaderCell = importComponentFromFELibrary('StatusHeaderCell', null, 'function')
2324

@@ -47,8 +48,17 @@ const UserPermissionList = () => {
4748
[pageSize, offset, searchKey, sortBy, sortOrder],
4849
)
4950

50-
// TODO (v1): Add abort controller
51-
const [isLoading, result, error, reload] = useAsync(() => getUserList(filterConfig), [filterConfig])
51+
const abortControllerRef = useRef(new AbortController())
52+
const [isLoading, result, error, reload] = useAsync(
53+
() =>
54+
abortPreviousRequests(
55+
() => getUserList(filterConfig, abortControllerRef.current.signal),
56+
abortControllerRef,
57+
),
58+
[filterConfig],
59+
)
60+
61+
const showLoadingState = isLoading || getIsRequestAborted(error)
5262

5363
const getUserDataForExport = useCallback(
5464
() =>
@@ -63,7 +73,7 @@ const UserPermissionList = () => {
6373
[filterConfig],
6474
)
6575

66-
if (!isLoading) {
76+
if (!showLoadingState) {
6777
if (error) {
6878
if ([API_STATUS_CODES.PERMISSION_DENIED, API_STATUS_CODES.UNAUTHORIZED].includes(error.code)) {
6979
return (
@@ -73,7 +83,7 @@ const UserPermissionList = () => {
7383
/>
7484
)
7585
}
76-
return <Reload reload={reload} />
86+
return <Reload reload={reload} className="flex-grow-1" />
7787
}
7888

7989
// The null state is shown only when filters are not applied
@@ -82,6 +92,9 @@ const UserPermissionList = () => {
8292
}
8393
}
8494

95+
// Disable the filter actions
96+
const isActionsDisabled = showLoadingState || !(result.totalCount && result.users.length)
97+
8598
const sortByEmail = () => {
8699
handleSorting(SortableKeys.email)
87100
}
@@ -90,20 +103,16 @@ const UserPermissionList = () => {
90103
handleSorting(SortableKeys.lastLogin)
91104
}
92105

93-
const handleClearFilters = () => {
94-
clearFilters()
95-
}
96-
97106
return (
98107
<div className="flexbox-col dc__gap-8 flex-grow-1">
99108
<UserPermissionListHeader
100-
disabled={isLoading || !(result.totalCount && result.users.length)}
109+
disabled={isActionsDisabled}
101110
showStatus={showStatus}
102111
handleSearch={handleSearch}
103112
initialSearchText={searchKey}
104113
getDataToExport={getUserDataForExport}
105114
/>
106-
{isLoading || (result.totalCount && result.users.length) ? (
115+
{showLoadingState || (result.totalCount && result.users.length) ? (
107116
<div className="flexbox-col flex-grow-1">
108117
<div
109118
className={`user-permission__header ${
@@ -116,22 +125,24 @@ const UserPermissionList = () => {
116125
triggerSorting={sortByEmail}
117126
isSorted={sortBy === SortableKeys.email}
118127
sortOrder={sortOrder}
119-
disabled={isLoading}
128+
disabled={isActionsDisabled}
120129
/>
121130
<SortableTableHeaderCell
122131
title="Last Login"
123132
triggerSorting={sortByLastLogin}
124133
isSorted={sortBy === SortableKeys.lastLogin}
125134
sortOrder={sortOrder}
126-
disabled={isLoading}
135+
disabled={isActionsDisabled}
127136
/>
128137
{showStatus && <StatusHeaderCell />}
129138
<span />
130139
</div>
131-
{isLoading ? (
140+
{showLoadingState ? (
132141
userListLoading.map((user) => (
133142
<div
134-
className="user-permission__row pl-20 pr-20 show-shimmer-loading"
143+
className={`user-permission__row ${
144+
showStatus ? 'user-permission__row--with-status' : ''
145+
} pl-20 pr-20 show-shimmer-loading`}
135146
key={`user-list-${user.id}`}
136147
>
137148
<span className="child child-shimmer-loading" />
@@ -167,7 +178,7 @@ const UserPermissionList = () => {
167178
)}
168179
</div>
169180
) : (
170-
<FiltersEmptyState clearFilters={handleClearFilters} />
181+
<FiltersEmptyState clearFilters={clearFilters} />
171182
)}
172183
</div>
173184
)

src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionListHeader.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ const UserPermissionListHeader = ({
5757
<SearchBar
5858
inputProps={{
5959
placeholder: 'Search User',
60-
disabled,
6160
}}
6261
handleEnter={handleSearch}
6362
shouldDebounce

src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionRow.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,17 @@ const UserPermissionRow = ({
113113
: handleUTCTime(lastLoginTime, true)}
114114
</span>
115115
</ConditionalWrap>
116-
{/* TODO (v1): Status should not be editable for admin/system */}
117-
{showStatus && <StatusCell status={userStatus} timeToLive={timeToLive} />}
116+
{showStatus && (
117+
<StatusCell
118+
status={userStatus}
119+
timeToLive={timeToLive}
120+
userEmail={emailId}
121+
userId={id}
122+
refetchUserPermissionList={refetchUserPermissionList}
123+
// Status is readonly for admin/system user
124+
isReadOnly={isAdminOrSystemUser}
125+
/>
126+
)}
118127
{isAdminOrSystemUser ? (
119128
<span />
120129
) : (

src/Pages/GlobalConfigurations/Authorization/UserPermissions/SSONotConfiguredState.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const SSONotConfiguredState = () => (
2323
linkText={SSO_NOT_CONFIGURED_STATE_TEXTS.linkText}
2424
redirectLink={SSO_NOT_CONFIGURED_STATE_TEXTS.redirectLink}
2525
internalLink
26-
Icon={ErrorIcon}
26+
Icon={() => <ErrorIcon className="h-20" />}
2727
/>
2828
</>
2929
}

src/Pages/GlobalConfigurations/Authorization/authorization.service.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,16 @@ export const createOrUpdateUser = (data: UserCreateOrUpdatePayload) => {
4747
export const deleteUser = (userId: User['id']) => trash(`user/${userId}`)
4848

4949
export const getUserList = async (
50-
queryParams?: BaseFilterQueryParams<UserListSortableKeys>,
50+
queryParams: BaseFilterQueryParams<UserListSortableKeys>,
51+
signal?: AbortSignal,
5152
): Promise<{
5253
users: User[]
5354
totalCount: number
5455
}> => {
5556
try {
5657
const {
5758
result: { users, totalCount },
58-
} = (await get(getUrlWithSearchParams('user', queryParams ?? {}))) as ResponseType<{
59+
} = (await get(getUrlWithSearchParams('user', queryParams ?? {}), { signal })) as ResponseType<{
5960
users: UserDto[]
6061
totalCount: number
6162
}>
@@ -65,7 +66,9 @@ export const getUserList = async (
6566
totalCount,
6667
}
6768
} catch (error) {
68-
showError(error)
69+
if (!signal?.aborted) {
70+
showError(error)
71+
}
6972
throw error
7073
}
7174
}
@@ -92,26 +95,32 @@ export const createOrUpdatePermissionGroup = (payload: PermissionGroupCreateOrUp
9295

9396
export const getPermissionGroupList = async (
9497
queryParams?: BaseFilterQueryParams<PermissionGroupListSortableKeys>,
98+
signal?: AbortSignal,
9599
): Promise<{
96100
permissionGroups: PermissionGroup[]
97101
totalCount: number
98102
}> => {
99-
const {
100-
result: { roleGroups: permissionGroups, totalCount },
101-
} = (await get(getUrlWithSearchParams('user/role/group', queryParams ?? {}))) as ResponseType<{
102-
roleGroups: PermissionGroupDto[]
103-
totalCount: number
104-
}>
103+
try {
104+
const {
105+
result: { roleGroups: permissionGroups, totalCount },
106+
} = (await get(getUrlWithSearchParams('user/role/group', queryParams ?? {}), { signal })) as ResponseType<{
107+
roleGroups: PermissionGroupDto[]
108+
totalCount: number
109+
}>
105110

106-
return {
107-
permissionGroups,
108-
totalCount,
111+
return {
112+
permissionGroups,
113+
totalCount,
114+
}
115+
} catch (error) {
116+
if (!signal?.aborted) {
117+
showError(error)
118+
}
119+
throw error
109120
}
110121
}
111122

112-
export const deletePermissionGroup = (id: PermissionGroup['id']) => {
113-
return trash(`user/role/group/${id}`)
114-
}
123+
export const deletePermissionGroup = (id: PermissionGroup['id']) => trash(`user/role/group/${id}`)
115124

116125
// Others
117126
export const getCustomRoles = async (): Promise<ResponseType<CustomRoles[]>> => {

src/Pages/GlobalConfigurations/Authorization/shared/hooks/useUrlFilters.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const URL_KEYS = {
1515

1616
const { PAGE_SIZE, PAGE_NUMBER, SEARCH_KEY, SORT_BY, SORT_ORDER } = URL_KEYS
1717

18+
// TODO (v1): Move to common lib
1819
/**
1920
* Generic hook for implementing URL based filters.
2021
* eg: pagination, search, sort.

0 commit comments

Comments
 (0)