Skip to content

Commit 73eb9cf

Browse files
committed
fix-margins
first versions of modals replace text with span change eslint rule due to conflict between prettier and eslint, prettier already sort classnames Adds promisifyMutation helper to simplify mutation handling Updates user management handlers to use the helper Adds proper typing for handlers using IDataHandlers interface Removes duplicate type definitions added search query make pagination using usePaginationQueryStateWithStore fixes code-review: remove onErrors callbacks fix enter fix pathes refactoring dialogs refactoring types commit: refactor: use store provider for user management hooks This commit refactors the user management page to use a centralized store provider by: Creating UserManagementStoreProvider to manage shared hooks Removing hook props drilling through components Accessing hooks via useUserManagementStore hook in child components Splitting UserManagementPage into container and content components This change improves code organization and reduces prop drilling while maintaining existing functionality. commit: refactor: move dialog context to providers directory This commit reorganizes the dialog-related code by: Moving DialogsProvider from context/ to providers/ directory Updating import paths across components to use the new location 3. Removing unused context files and types Consolidating dialog-related code for better maintainability This is a structural change that improves code organization while maintaining existing functionality. remove redundant index from dialog commit: refactor: migrate dialog state management to UI layer The commit refactors the user management dialog state handling by: Moving dialog state management from container to UI layer using DialogsProvider Converting mutation handlers to return Promises for proper async handling Removing redundant dialog state management code from container Consolidating error and loading states into dedicated objects Moving EActiveTab enum to UI package for better organization This change improves separation of concerns by keeping UI state management in the UI layer while maintaining the same functionality. refactor: reorganize validation schemas and types - Move Zod schemas to dedicated schema files - Update type definitions to use schema inference - Fix import paths to use absolute imports - Remove duplicate schema definitions from components - Centralize types in dedicated type files refactor(user-management): reorganize dialogs structure - Move all dialogs to dedicated folders with proper structure - Add types and index files for each dialog - Update imports and exports - Add new translations for empty state - Improve code organization in UserManagementPage first version of filters and sorting remove unnecessary fragment added tabs fixes after design review
1 parent 0397383 commit 73eb9cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1681
-714
lines changed

.eslintrc.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,9 @@
4343
"import/no-unresolved": "off",
4444
"react/prop-types": "off",
4545
"react/react-in-jsx-scope": "off",
46-
46+
"tailwindcss/classnames-order": "off",
4747
"jsx-a11y/click-events-have-key-events": "warn",
4848
"jsx-a11y/no-autofocus": "off",
49-
5049
"@typescript-eslint/no-unused-vars": [
5150
"error",
5251
{

apps/gitness/src/pages-v2/user-management/user-management-container.tsx

Lines changed: 66 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react'
1+
import { useEffect } from 'react'
22

33
import { useQueryClient } from '@tanstack/react-query'
44

@@ -9,65 +9,28 @@ import {
99
useAdminUpdateUserMutation,
1010
useUpdateUserAdminMutation
1111
} from '@harnessio/code-service-client'
12-
import {
13-
AdminDialog,
14-
CreateUserDialog,
15-
DeleteUserDialog,
16-
DialogLabels,
17-
EditUserDialog,
18-
ResetPasswordDialog,
19-
UserManagementPage,
20-
UsersProps
21-
} from '@harnessio/ui/views'
22-
23-
import { parseAsInteger, useQueryState } from '../../framework/hooks/useQueryState'
12+
import { IDataHandlers, UserManagementPage } from '@harnessio/ui/views'
13+
14+
import { useQueryState } from '../../framework/hooks/useQueryState'
15+
import usePaginationQueryStateWithStore from '../../hooks/use-pagination-query-state-with-store'
2416
import { useTranslationStore } from '../../i18n/stores/i18n-store'
25-
import { generateAlphaNumericHash } from '../pull-request/pull-request-utils'
2617
import { useAdminListUsersStore } from './stores/admin-list-store'
18+
import { promisifyMutation } from './utils/promisify-mutation'
2719

2820
export const UserManagementPageContainer = () => {
29-
const [queryPage, setQueryPage] = useQueryState('page', parseAsInteger.withDefault(1))
30-
const { setUsers, setTotalPages, setPage, page, password, setUser, setPassword, setGeteneratePassword } =
31-
useAdminListUsersStore()
3221
const queryClient = useQueryClient()
3322

34-
const [isDeleteUserDialogOpen, setDeleteUserDialogOpen] = useState(false)
35-
const [isEditUserDialogOpen, setEditUserDialogOpen] = useState(false)
36-
const [isAdminDialogOpen, setAdminDialogOpen] = useState(false)
37-
const [isResetPasswordDialogOpen, setResetPasswordDialogOpen] = useState(false)
38-
const [isCreateUserDialogOpen, setCreateUserDialogOpen] = useState(false)
39-
40-
const handleDialogOpen = (user: UsersProps | null, dialogTypeLabel: string) => {
41-
if (user) setUser(user)
42-
43-
switch (dialogTypeLabel) {
44-
case DialogLabels.DELETE_USER:
45-
setDeleteUserDialogOpen(true)
46-
break
47-
case DialogLabels.EDIT_USER:
48-
setEditUserDialogOpen(true)
49-
break
50-
case DialogLabels.TOGGLE_ADMIN:
51-
setAdminDialogOpen(true)
52-
break
53-
case DialogLabels.RESET_PASSWORD:
54-
setGeteneratePassword(false)
55-
setPassword(generateAlphaNumericHash(10))
56-
setResetPasswordDialogOpen(true)
57-
break
58-
case DialogLabels.CREATE_USER:
59-
setPassword(generateAlphaNumericHash(10))
60-
setCreateUserDialogOpen(true)
61-
setGeteneratePassword(true)
62-
break
63-
default:
64-
break
65-
}
66-
}
23+
const { setUsers, setTotalPages, setPage, page, password } = useAdminListUsersStore()
24+
25+
const [query, setQuery] = useQueryState('query')
26+
const { queryPage } = usePaginationQueryStateWithStore({ page, setPage })
6727

6828
const { data: { body: userData, headers } = {} } = useAdminListUsersQuery({
6929
queryParams: {
70-
page: queryPage
30+
page: queryPage,
31+
// TODO: add search functionality by query parameter
32+
//@ts-expect-error - query is not typed
33+
query: query ?? ''
7134
}
7235
})
7336

@@ -80,45 +43,41 @@ export const UserManagementPageContainer = () => {
8043
}
8144
}, [userData, setUsers, setTotalPages, headers])
8245

83-
useEffect(() => {
84-
setQueryPage(page)
85-
}, [queryPage, page, setPage])
86-
87-
const { mutate: updateUser, isLoading: isUpdatingUser } = useAdminUpdateUserMutation(
46+
const {
47+
mutate: updateUser,
48+
isLoading: isUpdatingUser,
49+
error: updateUserError
50+
} = useAdminUpdateUserMutation(
8851
{},
8952
{
9053
onSuccess: () => {
91-
setEditUserDialogOpen(false)
9254
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
93-
},
94-
onError: error => {
95-
console.error(error)
9655
}
9756
}
9857
)
9958

100-
const { mutate: deleteUser, isLoading: isDeletingUser } = useAdminDeleteUserMutation(
59+
const {
60+
mutate: deleteUser,
61+
isLoading: isDeletingUser,
62+
error: deleteUserError
63+
} = useAdminDeleteUserMutation(
10164
{},
10265
{
10366
onSuccess: () => {
104-
setDeleteUserDialogOpen(false)
10567
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
106-
},
107-
onError: error => {
108-
console.error(error)
10968
}
11069
}
11170
)
11271

113-
const { mutate: updateUserAdmin, isLoading: isUpdatingUserAdmin } = useUpdateUserAdminMutation(
72+
const {
73+
mutate: updateUserAdmin,
74+
isLoading: isUpdatingUserAdmin,
75+
error: updateUserAdminError
76+
} = useUpdateUserAdminMutation(
11477
{},
11578
{
11679
onSuccess: () => {
117-
setAdminDialogOpen(false)
11880
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
119-
},
120-
onError: error => {
121-
console.error(error)
12281
}
12382
}
12483
)
@@ -131,18 +90,13 @@ export const UserManagementPageContainer = () => {
13190
{},
13291
{
13392
onSuccess: () => {
134-
setCreateUserDialogOpen(false)
135-
setResetPasswordDialogOpen(true)
13693
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
137-
},
138-
onError: error => {
139-
console.error(error)
14094
}
14195
}
14296
)
14397

144-
const handleCreateUser = (data: { uid: string; email: string; display_name: string }) => {
145-
createUser({
98+
const handleCreateUser: IDataHandlers['handleCreateUser'] = data => {
99+
return promisifyMutation(createUser, {
146100
body: {
147101
uid: data.uid,
148102
email: data.email,
@@ -152,8 +106,8 @@ export const UserManagementPageContainer = () => {
152106
})
153107
}
154108

155-
const handleUpdateUser = (data: { email: string; displayName: string; userID: string }) => {
156-
updateUser({
109+
const handleUpdateUser: IDataHandlers['handleUpdateUser'] = data => {
110+
return promisifyMutation(updateUser, {
157111
user_uid: data.userID,
158112
body: {
159113
email: data.email,
@@ -162,71 +116,62 @@ export const UserManagementPageContainer = () => {
162116
})
163117
}
164118

165-
const handleDeleteUser = (userUid: string) => {
166-
deleteUser({
119+
const handleDeleteUser: IDataHandlers['handleDeleteUser'] = userUid => {
120+
return promisifyMutation(deleteUser, {
167121
user_uid: userUid
168122
})
169123
}
170124

171-
const handleUpdateUserAdmin = (userUid: string, isAdmin: boolean) => {
172-
updateUserAdmin({
125+
const handleUpdateUserAdmin: IDataHandlers['handleUpdateUserAdmin'] = (userUid, isAdmin) => {
126+
return promisifyMutation(updateUserAdmin, {
173127
user_uid: userUid,
174128
body: {
175129
admin: isAdmin
176130
}
177131
})
178132
}
179133

180-
const handleUpdatePassword = (userId: string) => {
181-
updateUser({
134+
const handleUpdatePassword: IDataHandlers['handleUpdatePassword'] = userId => {
135+
return promisifyMutation(updateUser, {
182136
user_uid: userId,
183137
body: {
184138
password: password
185139
}
186140
})
187141
}
188142

143+
const handlers = {
144+
handleUpdateUser,
145+
handleDeleteUser,
146+
handleUpdateUserAdmin,
147+
handleUpdatePassword,
148+
handleCreateUser
149+
}
150+
151+
const loadingStates = {
152+
isUpdatingUser,
153+
isDeletingUser,
154+
isUpdatingUserAdmin,
155+
isCreatingUser
156+
}
157+
158+
const errorStates = {
159+
updateUserError: updateUserError?.message?.toString() ?? '',
160+
deleteUserError: deleteUserError?.message?.toString() ?? '',
161+
updateUserAdminError: updateUserAdminError?.message?.toString() ?? '',
162+
createUserError: createUserError?.message?.toString() ?? ''
163+
}
164+
189165
return (
190166
<>
191167
<UserManagementPage
192168
useAdminListUsersStore={useAdminListUsersStore}
193169
useTranslationStore={useTranslationStore}
194-
handleDialogOpen={handleDialogOpen}
195-
/>
196-
197-
<DeleteUserDialog
198-
open={isDeleteUserDialogOpen}
199-
useAdminListUsersStore={useAdminListUsersStore}
200-
onClose={() => setDeleteUserDialogOpen(false)}
201-
isDeleting={isDeletingUser}
202-
handleDeleteUser={handleDeleteUser}
203-
/>
204-
<EditUserDialog
205-
open={isEditUserDialogOpen}
206-
useAdminListUsersStore={useAdminListUsersStore}
207-
isSubmitting={isUpdatingUser}
208-
onClose={() => setEditUserDialogOpen(false)}
209-
handleUpdateUser={handleUpdateUser}
210-
/>
211-
<AdminDialog
212-
open={isAdminDialogOpen}
213-
useAdminListUsersStore={useAdminListUsersStore}
214-
onClose={() => setAdminDialogOpen(false)}
215-
isLoading={isUpdatingUserAdmin}
216-
updateUserAdmin={handleUpdateUserAdmin}
217-
/>
218-
<ResetPasswordDialog
219-
open={isResetPasswordDialogOpen}
220-
useAdminListUsersStore={useAdminListUsersStore}
221-
onClose={() => setResetPasswordDialogOpen(false)}
222-
handleUpdatePassword={handleUpdatePassword}
223-
/>
224-
<CreateUserDialog
225-
open={isCreateUserDialogOpen}
226-
onClose={() => setCreateUserDialogOpen(false)}
227-
isLoading={isCreatingUser}
228-
apiError={createUserError?.message?.toString() ?? ''}
229-
handleCreateUser={handleCreateUser}
170+
handlers={handlers}
171+
loadingStates={loadingStates}
172+
errorStates={errorStates}
173+
searchQuery={query}
174+
setSearchQuery={setQuery}
230175
/>
231176
</>
232177
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export type MutationFn<T> = (params: T, options: { onSuccess: () => void; onError: (error: any) => void }) => void
2+
3+
export const promisifyMutation = <T>(mutation: MutationFn<T>, params: T): Promise<void> => {
4+
return new Promise<void>((resolve, reject) => {
5+
mutation(params, {
6+
onSuccess: () => resolve(),
7+
onError: error => reject(error)
8+
})
9+
})
10+
}

packages/ui/locales/en/views.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@
190190
"createOrImportRepos": "Create new or import an existing repository.",
191191
"noWebhooks": "No webhooks yet",
192192
"noWebhooksDescription": "Add or manage webhooks to automate tasks and connect external services to your project.",
193+
"noUsers": "No Users Found",
194+
"noUsersDescription": "There are no users in this scope. Click on the button below to start adding them.",
193195
"commit": "Commit",
194196
"noLabels": "No labels yet",
195197
"noLabelsDescription": "Use labels to organize, prioritize, and categorize tasks efficiently."
@@ -401,6 +403,15 @@
401403
"edit": "Edit webhook",
402404
"delete": "Delete webhook"
403405
},
406+
"userManagement": {
407+
"newUserButton": "New user",
408+
"searchPlaceholder": "Search",
409+
"usersHeader": "Users",
410+
"tabs": {
411+
"active": "Active users",
412+
"inactive": "Pending users"
413+
}
414+
},
404415
"labelData": {
405416
"create": "Create labels"
406417
}

packages/ui/locales/es/views.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@
190190
"createOrImportRepos": "Create new or import an existing repository.",
191191
"noWebhooks": "No webhooks yet",
192192
"noWebhooksDescription": "Add or manage webhooks to automate tasks and connect external services to your project.",
193+
"noUsers": "No Users Found",
194+
"noUsersDescription": "There are no users in this scope. Click on the button below to start adding them.",
193195
"commit": "Commit",
194196
"noLabels": "No labels yet",
195197
"noLabelsDescription": "Use labels to organize, prioritize, and categorize tasks efficiently."
@@ -389,6 +391,15 @@
389391
"edit": "Edit webhook",
390392
"delete": "Delete webhook"
391393
},
394+
"userManagement": {
395+
"newUserButton": "New user",
396+
"searchPlaceholder": "Search",
397+
"usersHeader": "Users",
398+
"tabs": {
399+
"active": "Active users",
400+
"inactive": "Pending users"
401+
}
402+
},
392403
"labelData": {
393404
"create": "Create labels"
394405
},

packages/ui/locales/fr/views.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@
190190
"createOrImportRepos": "Créer un nouveau dépôt ou importer un dépôt existant.",
191191
"noWebhooks": "No webhooks yet",
192192
"noWebhooksDescription": "Add or manage webhooks to automate tasks and connect external services to your project.",
193+
"noUsers": "No Users Found",
194+
"noUsersDescription": "There are no users in this scope. Click on the button below to start adding them.",
193195
"commit": "Validation",
194196
"noLabels": "No labels yet",
195197
"noLabelsDescription": "Use labels to organize, prioritize, and categorize tasks efficiently."
@@ -395,6 +397,15 @@
395397
"edit": "Edit webhook",
396398
"delete": "Delete webhook"
397399
},
400+
"userManagement": {
401+
"newUserButton": "New user",
402+
"searchPlaceholder": "Search",
403+
"usersHeader": "Users",
404+
"tabs": {
405+
"active": "Active users",
406+
"inactive": "Pending users"
407+
}
408+
},
398409
"labelData": {
399410
"create": "Create labels"
400411
}

packages/ui/src/components/table.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const tableVariants = cva('w-full text-sm', {
88
variant: {
99
default: 'caption-bottom ',
1010
asStackedList:
11-
'rounded-md border [&_td]:px-4 [&_td]:py-2.5 [&_td]:align-top [&_th]:px-4 [&_thead]:bg-background-2'
11+
'[&_thead]:bg-background-2 rounded-md border [&_td]:px-4 [&_td]:py-2.5 [&_td]:align-top [&_th]:px-4'
1212
}
1313
},
1414
defaultVariants: {

0 commit comments

Comments
 (0)