Add user profile popover and logout icon; refactor dashboard navigation
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
This commit is contained in:
parent
fd968bb2b5
commit
775393dfd1
8
assets/icons/logouticon.svg
Normal file
8
assets/icons/logouticon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.905936,0,0,0.905936,8.091803,1)">
|
||||||
|
<path d="M0,64.406C0,66.625 1.812,68.438 4.031,68.438C6.25,68.438 8.062,66.625 8.062,64.406L8.062,9.844C8.062,8.75 8.75,8.062 9.797,8.062L42.984,8.062C44.031,8.062 44.719,8.75 44.719,9.844L44.719,64.406C44.719,66.625 46.531,68.438 48.766,68.438C50.984,68.438 52.781,66.625 52.781,64.406L52.781,8.828C52.781,3.391 49.391,0 43.875,0L8.922,0C3.406,0 0,3.391 0,8.828L0,64.406Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M11.891,65.75C11.891,66.547 12.5,66.938 13.359,66.578L22.859,62.547C23.672,62.203 24.031,61.828 24.031,61.062L24.031,17.438C24.031,16.672 23.672,16.281 22.875,15.969L13.359,11.906C12.5,11.562 11.891,11.953 11.891,12.766L11.891,65.75Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -5,18 +5,14 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
Tag,
|
Tag,
|
||||||
Space,
|
Space,
|
||||||
Dropdown,
|
|
||||||
Button,
|
Button,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Badge,
|
Badge,
|
||||||
Divider,
|
Divider,
|
||||||
Typography
|
Typography,
|
||||||
|
Popover
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import {
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
LogoutOutlined,
|
|
||||||
MailOutlined,
|
|
||||||
LoadingOutlined
|
|
||||||
} from '@ant-design/icons'
|
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import { SpotlightContext } from '../context/SpotlightContext'
|
import { SpotlightContext } from '../context/SpotlightContext'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
@ -25,6 +21,7 @@ import { useNavigate, useLocation } from 'react-router-dom'
|
|||||||
import { Header } from 'antd/es/layout/layout'
|
import { Header } from 'antd/es/layout/layout'
|
||||||
import { useMediaQuery } from 'react-responsive'
|
import { useMediaQuery } from 'react-responsive'
|
||||||
import KeyboardShortcut from './KeyboardShortcut'
|
import KeyboardShortcut from './KeyboardShortcut'
|
||||||
|
import UserProfilePopover from './UserProfilePopover'
|
||||||
|
|
||||||
import FarmControlLogo from '../../Logos/FarmControlLogo'
|
import FarmControlLogo from '../../Logos/FarmControlLogo'
|
||||||
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
|
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
|
||||||
@ -45,7 +42,7 @@ import DashboardWindowButtons from './DashboardWindowButtons'
|
|||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const DashboardNavigation = () => {
|
const DashboardNavigation = () => {
|
||||||
const { logout, userProfile } = useContext(AuthContext)
|
const { userProfile } = useContext(AuthContext)
|
||||||
const { showSpotlight } = useContext(SpotlightContext)
|
const { showSpotlight } = useContext(SpotlightContext)
|
||||||
const { connecting, connected } = useContext(ApiServerContext)
|
const { connecting, connected } = useContext(ApiServerContext)
|
||||||
const { toggleNotificationCenter, unreadCount } =
|
const { toggleNotificationCenter, unreadCount } =
|
||||||
@ -93,34 +90,11 @@ const DashboardNavigation = () => {
|
|||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
const userMenuItems = {
|
const [userPopoverOpen, setUserPopoverOpen] = useState(false)
|
||||||
items: [
|
|
||||||
{
|
const userPopoverContent = (
|
||||||
key: 'username',
|
<UserProfilePopover onClose={() => setUserPopoverOpen(false)} />
|
||||||
label: userProfile?.username,
|
)
|
||||||
icon: <PersonIcon />,
|
|
||||||
disabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'email',
|
|
||||||
label: userProfile?.email,
|
|
||||||
icon: <MailOutlined />,
|
|
||||||
disabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'logout',
|
|
||||||
label: 'Logout',
|
|
||||||
icon: <LogoutOutlined />
|
|
||||||
}
|
|
||||||
],
|
|
||||||
onClick: (key) => {
|
|
||||||
if (key === 'profile') {
|
|
||||||
navigate('/profile')
|
|
||||||
} else if (key === 'logout') {
|
|
||||||
logout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const pathParts = location.pathname.split('/').filter(Boolean)
|
const pathParts = location.pathname.split('/').filter(Boolean)
|
||||||
@ -322,11 +296,18 @@ const DashboardNavigation = () => {
|
|||||||
)}
|
)}
|
||||||
{userProfile ? (
|
{userProfile ? (
|
||||||
<Space>
|
<Space>
|
||||||
<Dropdown menu={userMenuItems} placement='bottomRight'>
|
<Popover
|
||||||
|
content={userPopoverContent}
|
||||||
|
placement='bottomRight'
|
||||||
|
trigger='hover'
|
||||||
|
open={userPopoverOpen}
|
||||||
|
onOpenChange={setUserPopoverOpen}
|
||||||
|
arrow={false}
|
||||||
|
>
|
||||||
<Tag style={{ marginRight: 0 }} icon={<PersonIcon />}>
|
<Tag style={{ marginRight: 0 }} icon={<PersonIcon />}>
|
||||||
{!isMobile && (userProfile?.name || userProfile.username)}
|
{!isMobile && (userProfile?.name || userProfile.username)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Dropdown>
|
</Popover>
|
||||||
</Space>
|
</Space>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
158
src/components/Dashboard/common/UserProfilePopover.jsx
Normal file
158
src/components/Dashboard/common/UserProfilePopover.jsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { createElement } from 'react'
|
||||||
|
import { Flex, Typography, Button, Space, Dropdown, Divider } from 'antd'
|
||||||
|
import { UserOutlined } from '@ant-design/icons'
|
||||||
|
import { useContext } from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import LogoutIcon from '../../Icons/LogoutIcon'
|
||||||
|
import { User } from '../../../database/models/User'
|
||||||
|
import { AuthContext } from '../context/AuthContext'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const ICON_ACTION_NAMES = ['info', 'edit']
|
||||||
|
|
||||||
|
const UserProfilePopover = ({ onClose }) => {
|
||||||
|
const { userProfile, profileImageUrl, logout } = useContext(AuthContext)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
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 }
|
||||||
|
|
||||||
|
const runAction = (action) => {
|
||||||
|
if (action.name === 'logout') {
|
||||||
|
logout()
|
||||||
|
} else if (action.url && userProfile?._id) {
|
||||||
|
const url = action.url(userProfile._id)
|
||||||
|
navigate(url)
|
||||||
|
}
|
||||||
|
onClose?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const isActionDisabled = (action) => {
|
||||||
|
if (action.disabled && typeof action.disabled === 'function') {
|
||||||
|
return action.disabled(objectData)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = userProfile?.username
|
||||||
|
const fullName = [userProfile?.firstName, userProfile?.lastName]
|
||||||
|
.filter(Boolean)
|
||||||
|
.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'
|
||||||
|
gap='middle'
|
||||||
|
style={{ minWidth: 240, padding: '0 1px' }}
|
||||||
|
>
|
||||||
|
<Flex align='center' gap='12px' style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
{profileImageUrl ? (
|
||||||
|
<img
|
||||||
|
src={profileImageUrl}
|
||||||
|
alt=''
|
||||||
|
style={{
|
||||||
|
width: 76,
|
||||||
|
height: 76,
|
||||||
|
borderRadius: '5px',
|
||||||
|
objectFit: 'cover'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<UserOutlined
|
||||||
|
style={{ fontSize: 48, color: 'var(--color-text-secondary)' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Flex vertical style={{ minWidth: 0 }}>
|
||||||
|
{fullName && (
|
||||||
|
<Text
|
||||||
|
strong
|
||||||
|
ellipsis
|
||||||
|
style={{ fontSize: 18, lineHeight: 1, marginTop: 1 }}
|
||||||
|
>
|
||||||
|
{fullName}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{email && (
|
||||||
|
<Space>
|
||||||
|
<Text
|
||||||
|
type='secondary'
|
||||||
|
strong
|
||||||
|
style={{ fontSize: 12, lineHeight: 1 }}
|
||||||
|
>
|
||||||
|
@{username}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
type='secondary'
|
||||||
|
ellipsis
|
||||||
|
style={{ fontSize: 12, lineHeight: 1 }}
|
||||||
|
>
|
||||||
|
{email}
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
{!username && !fullName && !email && (
|
||||||
|
<Text type='secondary'>Unknown user</Text>
|
||||||
|
)}
|
||||||
|
<Divider style={{ margin: '5px 0' }} />
|
||||||
|
<Flex gap={'1px'}>
|
||||||
|
{iconActions.map((action) => (
|
||||||
|
<Button
|
||||||
|
key={action.name}
|
||||||
|
type='text'
|
||||||
|
size='small'
|
||||||
|
icon={action.icon ? createElement(action.icon) : undefined}
|
||||||
|
onClick={() => runAction(action)}
|
||||||
|
aria-label={action.label}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{actionButton}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfilePopover.propTypes = {
|
||||||
|
onClose: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserProfilePopover
|
||||||
@ -28,8 +28,13 @@ const runningSpotlightFetches = new Map()
|
|||||||
const ApiServerContext = createContext()
|
const ApiServerContext = createContext()
|
||||||
|
|
||||||
const ApiServerProvider = ({ children }) => {
|
const ApiServerProvider = ({ children }) => {
|
||||||
const { token, userProfile, authenticated, setUnauthenticated } =
|
const {
|
||||||
useContext(AuthContext)
|
token,
|
||||||
|
userProfile,
|
||||||
|
setUserProfile,
|
||||||
|
authenticated,
|
||||||
|
setUnauthenticated
|
||||||
|
} = useContext(AuthContext)
|
||||||
const socketRef = useRef(null)
|
const socketRef = useRef(null)
|
||||||
const [connected, setConnected] = useState(false)
|
const [connected, setConnected] = useState(false)
|
||||||
const [connecting, setConnecting] = useState(false)
|
const [connecting, setConnecting] = useState(false)
|
||||||
@ -446,6 +451,25 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
[offObjectUpdatesEvent]
|
[offObjectUpdatesEvent]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Subscribe to user profile updates when WebSocket is connected and userProfile._id exists
|
||||||
|
useEffect(() => {
|
||||||
|
if (connected && userProfile?._id) {
|
||||||
|
const unsubscribe = subscribeToObjectUpdates(
|
||||||
|
userProfile._id,
|
||||||
|
'user',
|
||||||
|
(updatedUser) => {
|
||||||
|
logger.debug('Notifying user profile update:', updatedUser)
|
||||||
|
setUserProfile((prev) =>
|
||||||
|
prev && updatedUser ? { ...prev, ...updatedUser } : prev
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return () => {
|
||||||
|
if (unsubscribe) unsubscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [connected, userProfile?._id, subscribeToObjectUpdates, setUserProfile])
|
||||||
|
|
||||||
const subscribeToObjectTypeUpdates = useCallback(
|
const subscribeToObjectTypeUpdates = useCallback(
|
||||||
(objectType, callback) => {
|
(objectType, callback) => {
|
||||||
logger.debug('Subscribing to type updates:', objectType)
|
logger.debug('Subscribing to type updates:', objectType)
|
||||||
|
|||||||
@ -4,7 +4,8 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useContext
|
useContext,
|
||||||
|
useRef
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {
|
import {
|
||||||
@ -53,6 +54,8 @@ const AuthProvider = ({ children }) => {
|
|||||||
const [token, setToken] = useState(null)
|
const [token, setToken] = useState(null)
|
||||||
const [expiresAt, setExpiresAt] = useState(null)
|
const [expiresAt, setExpiresAt] = useState(null)
|
||||||
const [userProfile, setUserProfile] = useState(null)
|
const [userProfile, setUserProfile] = useState(null)
|
||||||
|
const [profileImageUrl, setProfileImageUrl] = useState(null)
|
||||||
|
const profileImageUrlRef = useRef(null)
|
||||||
const [showSessionExpiredModal, setShowSessionExpiredModal] = useState(false)
|
const [showSessionExpiredModal, setShowSessionExpiredModal] = useState(false)
|
||||||
const [showUnauthorizedModal, setShowUnauthorizedModal] = useState(false)
|
const [showUnauthorizedModal, setShowUnauthorizedModal] = useState(false)
|
||||||
const [showAuthErrorModal, setShowAuthErrorModal] = useState(false)
|
const [showAuthErrorModal, setShowAuthErrorModal] = useState(false)
|
||||||
@ -238,6 +241,97 @@ const AuthProvider = ({ children }) => {
|
|||||||
return cleanupCookieSync
|
return cleanupCookieSync
|
||||||
}, [token, expiresAt, userProfile, isElectron])
|
}, [token, expiresAt, userProfile, isElectron])
|
||||||
|
|
||||||
|
// Persist userProfile changes to cookies/electron storage so updates (e.g. from
|
||||||
|
// WebSocket or profile edits) are saved for session restoration
|
||||||
|
useEffect(() => {
|
||||||
|
if (!authenticated || !token || !expiresAt) return
|
||||||
|
persistSession({
|
||||||
|
token,
|
||||||
|
expiresAt,
|
||||||
|
user: userProfile
|
||||||
|
})
|
||||||
|
}, [authenticated, token, expiresAt, userProfile, persistSession])
|
||||||
|
|
||||||
|
// Fetch and cache profile image when userProfile.profileImage changes
|
||||||
|
useEffect(() => {
|
||||||
|
const profileImage = userProfile?.profileImage
|
||||||
|
const profileImageId =
|
||||||
|
profileImage?._id ??
|
||||||
|
(typeof profileImage === 'string' ? profileImage : null)
|
||||||
|
|
||||||
|
console.log('Fetching profile image:', profileImageId)
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
if (profileImageUrlRef.current) {
|
||||||
|
URL.revokeObjectURL(profileImageUrlRef.current)
|
||||||
|
profileImageUrlRef.current = null
|
||||||
|
}
|
||||||
|
setProfileImageUrl(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!profileImageId) {
|
||||||
|
if (profileImageUrlRef.current) {
|
||||||
|
URL.revokeObjectURL(profileImageUrlRef.current)
|
||||||
|
profileImageUrlRef.current = null
|
||||||
|
}
|
||||||
|
setProfileImageUrl(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelled = false
|
||||||
|
const file =
|
||||||
|
typeof profileImage === 'object' && profileImage !== null
|
||||||
|
? profileImage
|
||||||
|
: { _id: profileImageId, name: '', extension: '' }
|
||||||
|
|
||||||
|
const fetchProfileImage = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.backendUrl}/files/${file._id}/content`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: '*/*',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
responseType: 'blob'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const blob = new Blob([response.data], {
|
||||||
|
type: response.headers['content-type']
|
||||||
|
})
|
||||||
|
const fileURL = window.URL.createObjectURL(blob)
|
||||||
|
if (!cancelled) {
|
||||||
|
if (profileImageUrlRef.current) {
|
||||||
|
URL.revokeObjectURL(profileImageUrlRef.current)
|
||||||
|
}
|
||||||
|
profileImageUrlRef.current = fileURL
|
||||||
|
setProfileImageUrl(fileURL)
|
||||||
|
} else {
|
||||||
|
URL.revokeObjectURL(fileURL)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug('Failed to fetch profile image:', err)
|
||||||
|
if (!cancelled) {
|
||||||
|
setProfileImageUrl(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchProfileImage()
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
if (profileImageUrlRef.current) {
|
||||||
|
URL.revokeObjectURL(profileImageUrlRef.current)
|
||||||
|
profileImageUrlRef.current = null
|
||||||
|
}
|
||||||
|
setProfileImageUrl(null)
|
||||||
|
}
|
||||||
|
}, [userProfile?.profileImage?._id ?? userProfile?.profileImage, token])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('userProfile', userProfile)
|
||||||
|
}, [userProfile])
|
||||||
|
|
||||||
const logout = useCallback(
|
const logout = useCallback(
|
||||||
(redirectUri = '/login') => {
|
(redirectUri = '/login') => {
|
||||||
setAuthenticated(false)
|
setAuthenticated(false)
|
||||||
@ -627,6 +721,8 @@ const AuthProvider = ({ children }) => {
|
|||||||
token,
|
token,
|
||||||
loading,
|
loading,
|
||||||
userProfile,
|
userProfile,
|
||||||
|
setUserProfile,
|
||||||
|
profileImageUrl,
|
||||||
logout
|
logout
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
6
src/components/Icons/LogoutIcon.jsx
Normal file
6
src/components/Icons/LogoutIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/logouticon.svg?react'
|
||||||
|
|
||||||
|
const LogoutIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default LogoutIcon
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import PersonIcon from '../../components/Icons/PersonIcon'
|
import PersonIcon from '../../components/Icons/PersonIcon'
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
import AppPasswordIcon from '../../components/Icons/AppPasswordIcon'
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||||
|
|
||||||
export const User = {
|
export const User = {
|
||||||
name: 'user',
|
name: 'user',
|
||||||
@ -18,17 +18,17 @@ export const User = {
|
|||||||
url: (_id) => `/dashboard/management/users/info?userId=${_id}`
|
url: (_id) => `/dashboard/management/users/info?userId=${_id}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'reload',
|
name: 'edit',
|
||||||
label: 'Reload',
|
label: 'Edit',
|
||||||
icon: ReloadIcon,
|
row: true,
|
||||||
url: (_id) =>
|
icon: EditIcon,
|
||||||
`/dashboard/management/users/info?userId=${_id}&action=reload`
|
url: (_id) => `/dashboard/management/users/info?userId=${_id}&action=edit`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'newAppPassword',
|
name: 'newAppPassword',
|
||||||
label: 'New App Password',
|
label: 'New App Password',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
icon: AppPasswordIcon,
|
icon: PlusIcon,
|
||||||
url: (_id) =>
|
url: (_id) =>
|
||||||
`/dashboard/management/users/info?userId=${_id}&action=newAppPassword`,
|
`/dashboard/management/users/info?userId=${_id}&action=newAppPassword`,
|
||||||
disabled: (objectData) => {
|
disabled: (objectData) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user