Skip to content

Commit

Permalink
added locales
Browse files Browse the repository at this point in the history
fixes

added empty state

added error state

added skeleton and nodata

refactor dialogs

added state provider

caption props

added no search results state

paddings refactor

remove unnecesary props drilling

remove classes sorting

refactor remove unnecessary props drilling

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
  • Loading branch information
athens-server committed Jan 30, 2025
1 parent 3b55b95 commit b9fcc7e
Show file tree
Hide file tree
Showing 83 changed files with 1,560 additions and 716 deletions.
2 changes: 0 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@
"import/no-unresolved": "off",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",

"jsx-a11y/click-events-have-key-events": "warn",
"jsx-a11y/no-autofocus": "off",

"@typescript-eslint/no-unused-vars": [
"error",
{
Expand Down
195 changes: 73 additions & 122 deletions apps/gitness/src/pages-v2/user-management/user-management-container.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useEffect } from 'react'

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

Expand All @@ -9,65 +9,32 @@ import {
useAdminUpdateUserMutation,
useUpdateUserAdminMutation
} from '@harnessio/code-service-client'
import {
AdminDialog,
CreateUserDialog,
DeleteUserDialog,
DialogLabels,
EditUserDialog,
ResetPasswordDialog,
UserManagementPage,
UsersProps
} from '@harnessio/ui/views'

import { parseAsInteger, useQueryState } from '../../framework/hooks/useQueryState'
import { IDataHandlers, UserManagementPage } from '@harnessio/ui/views'

import { useQueryState } from '../../framework/hooks/useQueryState'
import usePaginationQueryStateWithStore from '../../hooks/use-pagination-query-state-with-store'
import { useTranslationStore } from '../../i18n/stores/i18n-store'
import { generateAlphaNumericHash } from '../pull-request/pull-request-utils'
import { useAdminListUsersStore } from './stores/admin-list-store'
import { promisifyMutation } from './utils/promisify-mutation'

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

const [isDeleteUserDialogOpen, setDeleteUserDialogOpen] = useState(false)
const [isEditUserDialogOpen, setEditUserDialogOpen] = useState(false)
const [isAdminDialogOpen, setAdminDialogOpen] = useState(false)
const [isResetPasswordDialogOpen, setResetPasswordDialogOpen] = useState(false)
const [isCreateUserDialogOpen, setCreateUserDialogOpen] = useState(false)

const handleDialogOpen = (user: UsersProps | null, dialogTypeLabel: string) => {
if (user) setUser(user)

switch (dialogTypeLabel) {
case DialogLabels.DELETE_USER:
setDeleteUserDialogOpen(true)
break
case DialogLabels.EDIT_USER:
setEditUserDialogOpen(true)
break
case DialogLabels.TOGGLE_ADMIN:
setAdminDialogOpen(true)
break
case DialogLabels.RESET_PASSWORD:
setGeteneratePassword(false)
setPassword(generateAlphaNumericHash(10))
setResetPasswordDialogOpen(true)
break
case DialogLabels.CREATE_USER:
setPassword(generateAlphaNumericHash(10))
setCreateUserDialogOpen(true)
setGeteneratePassword(true)
break
default:
break
}
}
const { setUsers, setTotalPages, setPage, page, password } = useAdminListUsersStore()

const { data: { body: userData, headers } = {} } = useAdminListUsersQuery({
const [query, setQuery] = useQueryState('query')
const { queryPage } = usePaginationQueryStateWithStore({ page, setPage })

const {
isFetching,
error,
data: { body: userData, headers } = {}
} = useAdminListUsersQuery({
queryParams: {
page: queryPage
page: queryPage,
// TODO: add search functionality by query parameter
//@ts-expect-error - query is not typed
query: query ?? ''
}
})

Expand All @@ -80,45 +47,41 @@ export const UserManagementPageContainer = () => {
}
}, [userData, setUsers, setTotalPages, headers])

useEffect(() => {
setQueryPage(page)
}, [queryPage, page, setPage])

const { mutate: updateUser, isLoading: isUpdatingUser } = useAdminUpdateUserMutation(
const {
mutate: updateUser,
isLoading: isUpdatingUser,
error: updateUserError
} = useAdminUpdateUserMutation(
{},
{
onSuccess: () => {
setEditUserDialogOpen(false)
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
},
onError: error => {
console.error(error)
}
}
)

const { mutate: deleteUser, isLoading: isDeletingUser } = useAdminDeleteUserMutation(
const {
mutate: deleteUser,
isLoading: isDeletingUser,
error: deleteUserError
} = useAdminDeleteUserMutation(
{},
{
onSuccess: () => {
setDeleteUserDialogOpen(false)
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
},
onError: error => {
console.error(error)
}
}
)

const { mutate: updateUserAdmin, isLoading: isUpdatingUserAdmin } = useUpdateUserAdminMutation(
const {
mutate: updateUserAdmin,
isLoading: isUpdatingUserAdmin,
error: updateUserAdminError
} = useUpdateUserAdminMutation(
{},
{
onSuccess: () => {
setAdminDialogOpen(false)
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
},
onError: error => {
console.error(error)
}
}
)
Expand All @@ -131,18 +94,13 @@ export const UserManagementPageContainer = () => {
{},
{
onSuccess: () => {
setCreateUserDialogOpen(false)
setResetPasswordDialogOpen(true)
queryClient.invalidateQueries({ queryKey: ['adminListUsers'] })
},
onError: error => {
console.error(error)
}
}
)

const handleCreateUser = (data: { uid: string; email: string; display_name: string }) => {
createUser({
const handleCreateUser: IDataHandlers['handleCreateUser'] = data => {
return promisifyMutation(createUser, {
body: {
uid: data.uid,
email: data.email,
Expand All @@ -152,8 +110,8 @@ export const UserManagementPageContainer = () => {
})
}

const handleUpdateUser = (data: { email: string; displayName: string; userID: string }) => {
updateUser({
const handleUpdateUser: IDataHandlers['handleUpdateUser'] = data => {
return promisifyMutation(updateUser, {
user_uid: data.userID,
body: {
email: data.email,
Expand All @@ -162,71 +120,64 @@ export const UserManagementPageContainer = () => {
})
}

const handleDeleteUser = (userUid: string) => {
deleteUser({
const handleDeleteUser: IDataHandlers['handleDeleteUser'] = userUid => {
return promisifyMutation(deleteUser, {
user_uid: userUid
})
}

const handleUpdateUserAdmin = (userUid: string, isAdmin: boolean) => {
updateUserAdmin({
const handleUpdateUserAdmin: IDataHandlers['handleUpdateUserAdmin'] = (userUid, isAdmin) => {
return promisifyMutation(updateUserAdmin, {
user_uid: userUid,
body: {
admin: isAdmin
}
})
}

const handleUpdatePassword = (userId: string) => {
updateUser({
const handleUpdatePassword: IDataHandlers['handleUpdatePassword'] = userId => {
return promisifyMutation(updateUser, {
user_uid: userId,
body: {
password: password
}
})
}

const handlers = {
handleUpdateUser,
handleDeleteUser,
handleUpdateUserAdmin,
handleUpdatePassword,
handleCreateUser
}

const loadingStates = {
isFetchingUsers: isFetching,
isUpdatingUser,
isDeletingUser,
isUpdatingUserAdmin,
isCreatingUser
}

const errorStates = {
fetchUsersError: error?.message?.toString() ?? '',
updateUserError: updateUserError?.message?.toString() ?? '',
deleteUserError: deleteUserError?.message?.toString() ?? '',
updateUserAdminError: updateUserAdminError?.message?.toString() ?? '',
createUserError: createUserError?.message?.toString() ?? ''
}

return (
<>
<UserManagementPage
useAdminListUsersStore={useAdminListUsersStore}
useTranslationStore={useTranslationStore}
handleDialogOpen={handleDialogOpen}
/>

<DeleteUserDialog
open={isDeleteUserDialogOpen}
useAdminListUsersStore={useAdminListUsersStore}
onClose={() => setDeleteUserDialogOpen(false)}
isDeleting={isDeletingUser}
handleDeleteUser={handleDeleteUser}
/>
<EditUserDialog
open={isEditUserDialogOpen}
useAdminListUsersStore={useAdminListUsersStore}
isSubmitting={isUpdatingUser}
onClose={() => setEditUserDialogOpen(false)}
handleUpdateUser={handleUpdateUser}
/>
<AdminDialog
open={isAdminDialogOpen}
useAdminListUsersStore={useAdminListUsersStore}
onClose={() => setAdminDialogOpen(false)}
isLoading={isUpdatingUserAdmin}
updateUserAdmin={handleUpdateUserAdmin}
/>
<ResetPasswordDialog
open={isResetPasswordDialogOpen}
useAdminListUsersStore={useAdminListUsersStore}
onClose={() => setResetPasswordDialogOpen(false)}
handleUpdatePassword={handleUpdatePassword}
/>
<CreateUserDialog
open={isCreateUserDialogOpen}
onClose={() => setCreateUserDialogOpen(false)}
isLoading={isCreatingUser}
apiError={createUserError?.message?.toString() ?? ''}
handleCreateUser={handleCreateUser}
handlers={handlers}
loadingStates={loadingStates}
errorStates={errorStates}
searchQuery={query}
setSearchQuery={setQuery}
/>
</>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type MutationFn<T> = (params: T, options: { onSuccess: () => void; onError: (error: any) => void }) => void

export const promisifyMutation = <T>(mutation: MutationFn<T>, params: T): Promise<void> => {
return new Promise<void>((resolve, reject) => {
mutation(params, {
onSuccess: () => resolve(),
onError: error => reject(error)
})
})
}
67 changes: 67 additions & 0 deletions packages/ui/locales/en/views.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@
"createOrImportRepos": "Create new or import an existing repository.",
"noWebhooks": "No webhooks yet",
"noWebhooksDescription": "Add or manage webhooks to automate tasks and connect external services to your project.",
"noUsers": "No Users Found",
"noUsersDescription": "There are no users in this scope. Click on the button below to start adding them.",
"commit": "Commit",
"noLabels": "No labels yet",
"noLabelsDescription": "Use labels to organize, prioritize, and categorize tasks efficiently."
Expand Down Expand Up @@ -403,6 +405,71 @@
"edit": "Edit webhook",
"delete": "Delete webhook"
},
"userManagement": {
"userId": "User ID",
"userIdHint": "User ID cannot be changed once created",
"enterUsername": "Enter user name",
"enterEmail": "Enter email address",
"email": "Email",
"enterDisplayName": "Enter display name",
"displayName": "Display Name",
"inviting": "Inviting...",
"inviteNewUser": "Invite New User",
"addNewUser": "Add a new user",
"deletingUser": "Deleting user...",
"confirmDelete": "Yes, delete user",
"deleteConfirmation": "Are you sure you want to delete {{name}}?",
"deleteWarning": "This will permanently delete the user \"{{name}}\" from the system.",
"updateUser": "Update User",
"removeAdminMessage": "This will remove the admin tag for \"{{name}}\"",
"grantAdminMessage": "This will grant admin privileges to \"{{name}}\"",
"removingAdmin": "Removing admin...",
"grantingAdmin": "Granting admin...",
"removeAdmin": "Yes, remove admin",
"grantAdmin": "Yes, grant admin",
"updateAdminRights": "Update admin rights",
"passwordGeneratedMessage": "Your password has been generated. Please make sure to copy and store your password somewhere safe, you won't be able to see it again.",
"resetPasswordMessage": "A new password will be generated to assist {{name}} in resetting their current password.",
"resettingPassword": "Resetting Password...",
"resetPassword": "Reset password for {{name}}",
"newUserButton": "New user",
"searchPlaceholder": "Search",
"usersHeader": "Users",
"tabs": {
"active": "Active users",
"inactive": "Pending users"
},
"usersList": {
"user": "User",
"email": "Email",
"roleBinding": "Role binding"
},
"roles": {
"admin": "Admin",
"user": "User"
},
"actions": {
"removeAdmin": "Remove Admin",
"setAsAdmin": "Set as Admin",
"resetPassword": "Reset Password",
"editUser": "Edit User",
"deleteUser": "Delete User"
},
"forms": {
"userIdRequired": "Please provide a user ID",
"emailRequired": "Please enter a valid email address",
"displayNameRequired": "Please provide a display name",
"passwordGenerateSuccess": "Your password has been generated. Please make sure to copy and store your password somewhere safe.",
"saving": "Saving...",
"generating": "Generating...",
"close": "Close"
},
"validation": {
"emailInvalid": "Please enter a valid email",
"displayNameRequired": "Display name is required",
"userIdRequired": "User ID is required"
}
},
"labelData": {
"create": "Create labels"
}
Expand Down
Loading

0 comments on commit b9fcc7e

Please sign in to comment.