Refactor UserProfilePopover and ActionsContext for improved state management and action handling
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

- Integrated useEffect in UserProfilePopover to close the popover on route changes, enhancing user experience.
- Simplified modal object data handling in ActionsContext to ensure consistent user profile access.
- Updated User model action handling to correctly pass user data to the NewAppPassword component.
- Streamlined dropdown action rendering in UserProfilePopover, improving code clarity and maintainability.
This commit is contained in:
Tom Butcher 2026-08-01 19:09:24 +01:00
parent f4635d9188
commit 8fb8f5e337
4 changed files with 39 additions and 50 deletions

View File

@ -1,13 +1,13 @@
import PropTypes from 'prop-types'
import { createElement } from 'react'
import { Flex, Typography, Button, Space, Dropdown, Divider, Card } from 'antd'
import { createElement, useContext, useEffect } from 'react'
import { Flex, Typography, Button, Space, Divider, Card } from 'antd'
import { UserOutlined } from '@ant-design/icons'
import { useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useLocation } from 'react-router-dom'
import LogoutIcon from '../../Icons/LogoutIcon'
import { User } from '../../../database/models/User'
import { buildActionUrl } from '../../../utils/modelActions'
import { AuthContext } from '../context/AuthContext'
import ObjectActions from './ObjectActions'
const { Text } = Typography
@ -16,17 +16,20 @@ const ICON_ACTION_NAMES = ['info', 'edit']
const UserProfilePopover = ({ onClose }) => {
const { userProfile, profileImageUrl, logout } = useContext(AuthContext)
const navigate = useNavigate()
const location = useLocation()
const modelActions = User.actions || []
const iconActions = modelActions.filter((a) =>
ICON_ACTION_NAMES.includes(a.name)
)
const dropdownActions = modelActions.filter(
(a) => !ICON_ACTION_NAMES.includes(a.name)
)
const objectData = { ...userProfile, _user: userProfile }
useEffect(() => {
onClose?.()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location.pathname, location.search])
const runAction = (action) => {
if (action.name === 'logout') {
logout()
@ -50,36 +53,6 @@ const UserProfilePopover = ({ onClose }) => {
.join(' ')
const email = userProfile?.email
const dropdownItems = [
...dropdownActions.map((action) => ({
key: action.name,
label: action.label,
icon: action.icon ? createElement(action.icon) : undefined,
disabled: isActionDisabled(action),
onClick: () => !isActionDisabled(action) && runAction(action)
})),
{ type: 'divider' },
{
key: 'logout',
label: 'Logout',
icon: createElement(LogoutIcon),
onClick: () => runAction({ name: 'logout' })
}
]
const actionButton =
dropdownItems.length > 1 ? (
<Dropdown
menu={{ items: dropdownItems }}
trigger={['hover']}
placement='bottomLeft'
>
<Button type='text' size='small' aria-label='Actions'>
Actions
</Button>
</Dropdown>
) : null
return (
<Flex
align='center'
@ -154,10 +127,28 @@ const UserProfilePopover = ({ onClose }) => {
size='small'
icon={action.icon ? createElement(action.icon) : undefined}
onClick={() => runAction(action)}
disabled={isActionDisabled(action)}
aria-label={action.label}
/>
))}
{actionButton}
<Button
type='text'
size='small'
icon={createElement(LogoutIcon)}
onClick={() => runAction({ name: 'logout' })}
aria-label='Logout'
/>
{userProfile?._id && (
<ObjectActions
type='user'
id={userProfile._id}
objectData={objectData}
visibleActions={{ info: false, edit: false }}
buttonProps={{ type: 'text', size: 'small' }}
trigger={['hover']}
placement='bottomLeft'
/>
)}
</Flex>
</Flex>
</Flex>

View File

@ -152,7 +152,7 @@ const ActionsProvider = ({ children }) => {
const modalObjectData = currentObject
? { ...currentObject, _user: userProfile }
: null
: { _user: userProfile }
const [modelWidth, setModelWidth] = useState(520)
useEffect(() => {

View File

@ -4,7 +4,8 @@ const UserInfo = lazy(
() => import('../../components/Dashboard/Management/Users/UserInfo')
)
const NewAppPassword = lazy(
() => import('../../components/Dashboard/Management/AppPasswords/NewAppPassword')
() =>
import('../../components/Dashboard/Management/AppPasswords/NewAppPassword')
)
import PersonIcon from '../../components/Icons/PersonIcon'
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
@ -71,7 +72,12 @@ export const User = {
return objectData?._user?._id != objectData?._id
},
content: (objectData, { onOk } = {}) => {
return createElement(NewAppPassword, { defaultValues: { user: objectData }, onOk, reset: true })
console.log('newAppPassword objectData', objectData)
return createElement(NewAppPassword, {
defaultValues: { user: objectData._user },
onOk,
reset: true
})
}
}
],
@ -92,14 +98,7 @@ export const User = {
'updatedAt',
'_reference'
],
sorters: [
'name',
'email',
'role',
'createdAt',
'_id',
'updatedAt'
],
sorters: ['name', 'email', 'role', 'createdAt', '_id', 'updatedAt'],
properties: [
{
name: '_id',

View File

@ -29,7 +29,6 @@ export function buildActionUrl(model, action, objectId, pathname, search = '') {
}
const params = new URLSearchParams(search)
params.set(idParam, objectId)
params.set('action', action.name)
params.set('actionObjectType', model.name)
const query = params.toString()