Implemented multiple app passwords.
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
This commit is contained in:
parent
7ea5eaf1f5
commit
1e2adb2b28
8
assets/icons/apppasswordicon.svg
Normal file
8
assets/icons/apppasswordicon.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.913777,0,0,0.913777,-11.957695,-8.458487)">
|
||||||
|
<path d="M56.866,48.187C56.775,48.817 56.734,49.463 56.734,50.118C56.734,52.243 57.143,54.279 57.906,56.141C54.515,53.972 49.815,52.352 44,52.352C31.125,52.352 23.734,60.274 23.734,64.477C23.734,64.93 23.953,65.165 24.547,65.165L63.031,65.165L63.031,71.774L24.766,71.774C19.047,71.774 16.219,69.852 16.219,65.774C16.219,56.587 27.547,45.727 44,45.727C48.778,45.727 53.123,46.643 56.866,48.187ZM57.984,27.368C57.984,35.743 51.766,42.477 44,42.477C36.25,42.477 30.031,35.758 30.031,27.399C30.031,19.227 36.328,12.54 44,12.54C51.719,12.54 57.984,19.165 57.984,27.368ZM36.812,27.384C36.812,32.352 40.109,35.868 44,35.868C47.938,35.868 51.203,32.321 51.203,27.368C51.203,22.587 47.922,19.149 44,19.149C40.125,19.149 36.812,22.634 36.812,27.384Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M72.469,39.508C66.531,39.508 61.812,44.29 61.812,50.118C61.812,54.462 64.266,58.212 68.109,59.899L68.109,74.743C68.109,75.29 68.359,75.821 68.766,76.243L71.609,78.915C72.141,79.399 72.969,79.462 73.562,78.868L78.5,73.946C79.125,73.305 79.062,72.383 78.484,71.774L76.031,69.258L79.641,65.649C80.234,65.055 80.25,64.118 79.609,63.43L76.234,60.087C80.641,57.962 83.125,54.399 83.125,50.118C83.125,44.29 78.375,39.508 72.469,39.508ZM72.453,43.696C74.094,43.696 75.422,45.055 75.422,46.696C75.422,48.321 74.094,49.696 72.453,49.696C70.844,49.696 69.469,48.321 69.469,46.696C69.469,45.055 70.797,43.696 72.453,43.696Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
99
src/components/Dashboard/Management/AppPasswords.jsx
Normal file
99
src/components/Dashboard/Management/AppPasswords.jsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { useRef, useState } from 'react'
|
||||||
|
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
|
||||||
|
import NewAppPassword from './AppPasswords/NewAppPassword'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
|
import GridIcon from '../../Icons/GridIcon'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import ExportListButton from '../common/ExportListButton'
|
||||||
|
|
||||||
|
const AppPasswords = () => {
|
||||||
|
const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('appPassword')
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('appPassword')
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'New App Password',
|
||||||
|
key: 'newAppPassword',
|
||||||
|
icon: <PlusIcon />
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
tableRef.current?.reload()
|
||||||
|
} else if (key === 'newAppPassword') {
|
||||||
|
setNewAppPasswordOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='appPassword'
|
||||||
|
loading={false}
|
||||||
|
visibleState={columnVisibility}
|
||||||
|
updateVisibleState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
<ExportListButton objectType='appPassword' />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
type='appPassword'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={newAppPasswordOpen}
|
||||||
|
footer={null}
|
||||||
|
width={700}
|
||||||
|
onCancel={() => {
|
||||||
|
setNewAppPasswordOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewAppPassword
|
||||||
|
onOk={() => {
|
||||||
|
setNewAppPasswordOpen(false)
|
||||||
|
tableRef.current?.reload()
|
||||||
|
}}
|
||||||
|
reset={newAppPasswordOpen}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppPasswords
|
||||||
@ -0,0 +1,212 @@
|
|||||||
|
import { useRef, useState, useContext } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Modal } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import ViewButton from '../../common/ViewButton'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
|
import EditButtons from '../../common/EditButtons'
|
||||||
|
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
import RegenerateAppPasswordSecret from './RegenerateAppPasswordSecret.jsx'
|
||||||
|
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
|
import { AuthContext } from '../../context/AuthContext.jsx'
|
||||||
|
|
||||||
|
const AppPasswordInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const objectFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const { userProfile } = useContext(AuthContext)
|
||||||
|
const appPasswordId = new URLSearchParams(location.search).get(
|
||||||
|
'appPasswordId'
|
||||||
|
)
|
||||||
|
const [regenerateSecretOpen, setRegenerateSecretOpen] = useState(false)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'AppPasswordInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
auditLogs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const [objectFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
lock: null,
|
||||||
|
loading: false,
|
||||||
|
objectData: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
reload: () => {
|
||||||
|
objectFormRef?.current?.handleFetchObject?.()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
regenerateSecret: () => {
|
||||||
|
setRegenerateSecretOpen(true)
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
edit: () => {
|
||||||
|
objectFormRef?.current?.startEditing?.()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
objectFormRef?.current?.cancelEditing?.()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
objectFormRef?.current?.handleUpdate?.()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editDisabled = getModelByName('appPassword')
|
||||||
|
.actions.find((action) => action.name === 'edit')
|
||||||
|
.disabled({ ...objectFormState.objectData, _user: userProfile })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='middle'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='appPassword'
|
||||||
|
id={appPasswordId}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'App Password Information' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<UserNotifierToggle
|
||||||
|
type='appPassword'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='appPassword'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<LockIndicator lock={objectFormState.lock} />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={objectFormState.isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
actionHandlerRef.current.callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={objectFormState.editLoading}
|
||||||
|
formValid={objectFormState.formValid}
|
||||||
|
disabled={
|
||||||
|
objectFormState.lock?.locked ||
|
||||||
|
objectFormState.loading ||
|
||||||
|
editDisabled
|
||||||
|
}
|
||||||
|
loading={objectFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ScrollBox>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={objectFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
|
<InfoCollapse
|
||||||
|
title='App Password Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={appPasswordId}
|
||||||
|
type='appPassword'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={objectFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='appPassword'
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
|
</InfoCollapse>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Audit Logs'
|
||||||
|
icon={<AuditLogIcon />}
|
||||||
|
active={collapseState.auditLogs}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('auditLogs', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='auditLogs'
|
||||||
|
>
|
||||||
|
{objectFormState.loading ? (
|
||||||
|
<InfoCollapsePlaceholder />
|
||||||
|
) : (
|
||||||
|
<ObjectTable
|
||||||
|
type='auditLog'
|
||||||
|
masterFilter={{ 'parent._id': appPasswordId }}
|
||||||
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</ScrollBox>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={regenerateSecretOpen}
|
||||||
|
destroyOnClose
|
||||||
|
width={650}
|
||||||
|
onCancel={() => {
|
||||||
|
actionHandlerRef.current?.clearAction?.()
|
||||||
|
setRegenerateSecretOpen(false)
|
||||||
|
}}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<RegenerateAppPasswordSecret id={appPasswordId} />
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppPasswordInfo
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
|
const NewAppPassword = ({ onOk, reset, defaultValues = {} }) => {
|
||||||
|
return (
|
||||||
|
<NewObjectForm
|
||||||
|
type='appPassword'
|
||||||
|
reset={reset}
|
||||||
|
defaultValues={{
|
||||||
|
active: true,
|
||||||
|
...defaultValues
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='appPassword'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='appPassword'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='appPassword'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false,
|
||||||
|
secret: false
|
||||||
|
}}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<WizardView
|
||||||
|
steps={steps}
|
||||||
|
loading={submitLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
title='New App Password'
|
||||||
|
onSubmit={async () => {
|
||||||
|
const result = await handleSubmit()
|
||||||
|
if (result) {
|
||||||
|
onOk()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewAppPassword.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool,
|
||||||
|
defaultValues: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewAppPassword
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { useContext, useState } from 'react'
|
||||||
|
import { Result, Typography, Flex, Button } from 'antd'
|
||||||
|
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||||
|
import CopyButton from '../../common/CopyButton'
|
||||||
|
import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
|
||||||
|
import ReloadIcon from '../../../Icons/ReloadIcon'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const RegenerateAppPasswordSecret = ({ id }) => {
|
||||||
|
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||||
|
const [appPassword, setAppPassword] = useState(null)
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [passwordGenerated, setPasswordGenerated] = useState(false)
|
||||||
|
|
||||||
|
const handleRegenerate = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
setAppPassword(null)
|
||||||
|
try {
|
||||||
|
const result = await sendObjectFunction(
|
||||||
|
id,
|
||||||
|
'appPassword',
|
||||||
|
'regenerateSecret',
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
if (result?.appPassword) {
|
||||||
|
setAppPassword(result.appPassword)
|
||||||
|
setPasswordGenerated(true)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex vertical align='center'>
|
||||||
|
<Result
|
||||||
|
title={
|
||||||
|
passwordGenerated ? 'Secret Regenerated' : 'Regenerate Secret'
|
||||||
|
}
|
||||||
|
disabled={passwordGenerated}
|
||||||
|
subTitle={
|
||||||
|
appPassword ? (
|
||||||
|
<Text>Copy this secret now. It will not be shown again.</Text>
|
||||||
|
) : (
|
||||||
|
<Text>Generate a new secret for this app password.</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
icon={<AppPasswordIcon />}
|
||||||
|
>
|
||||||
|
<Flex justify='center' style={{ minWidth: '395px' }}>
|
||||||
|
<Flex justify='center'>
|
||||||
|
<Flex gap='small' align='center' justify='center'>
|
||||||
|
<CopyButton size='default' text={appPassword} />
|
||||||
|
<Text code style={{ fontSize: '18px' }}>
|
||||||
|
{appPassword || '••••••••••••••••••••••••••••••••'}
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
loading={loading}
|
||||||
|
onClick={handleRegenerate}
|
||||||
|
icon={<ReloadIcon />}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Result>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
RegenerateAppPasswordSecret.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RegenerateAppPasswordSecret
|
||||||
@ -21,6 +21,7 @@ import CourierIcon from '../../Icons/CourierIcon'
|
|||||||
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
|
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
|
||||||
import TaxRateIcon from '../../Icons/TaxRateIcon'
|
import TaxRateIcon from '../../Icons/TaxRateIcon'
|
||||||
import TaxRecordIcon from '../../Icons/TaxRecordIcon'
|
import TaxRecordIcon from '../../Icons/TaxRecordIcon'
|
||||||
|
import AppPasswordIcon from '../../Icons/AppPasswordIcon'
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -131,6 +132,12 @@ const items = [
|
|||||||
label: 'Users',
|
label: 'Users',
|
||||||
path: '/dashboard/management/users'
|
path: '/dashboard/management/users'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'appPasswords',
|
||||||
|
icon: <AppPasswordIcon />,
|
||||||
|
label: 'App Passwords',
|
||||||
|
path: '/dashboard/management/apppasswords'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'settings',
|
key: 'settings',
|
||||||
icon: <SettingsIcon />,
|
icon: <SettingsIcon />,
|
||||||
@ -167,6 +174,7 @@ const routeKeyMap = {
|
|||||||
'/dashboard/management/filaments': 'filaments',
|
'/dashboard/management/filaments': 'filaments',
|
||||||
'/dashboard/management/parts': 'parts',
|
'/dashboard/management/parts': 'parts',
|
||||||
'/dashboard/management/users': 'users',
|
'/dashboard/management/users': 'users',
|
||||||
|
'/dashboard/management/apppasswords': 'appPasswords',
|
||||||
'/dashboard/management/products': 'products',
|
'/dashboard/management/products': 'products',
|
||||||
'/dashboard/management/vendors': 'vendors',
|
'/dashboard/management/vendors': 'vendors',
|
||||||
'/dashboard/management/couriers': 'couriers',
|
'/dashboard/management/couriers': 'couriers',
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import ViewButton from '../../common/ViewButton'
|
|||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
import EditButtons from '../../common/EditButtons'
|
import EditButtons from '../../common/EditButtons'
|
||||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||||
@ -20,16 +21,18 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
|||||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import SetAppPassword from './SetAppPassword.jsx'
|
import NewAppPassword from '../AppPasswords/NewAppPassword.jsx'
|
||||||
|
|
||||||
const UserInfo = () => {
|
const UserInfo = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const objectFormRef = useRef(null)
|
const objectFormRef = useRef(null)
|
||||||
|
const appPasswordsTableRef = useRef(null)
|
||||||
const actionHandlerRef = useRef(null)
|
const actionHandlerRef = useRef(null)
|
||||||
const userId = new URLSearchParams(location.search).get('userId')
|
const userId = new URLSearchParams(location.search).get('userId')
|
||||||
const [setAppPasswordOpen, setSetAppPasswordOpen] = useState(false)
|
const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
|
||||||
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
|
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
|
||||||
info: true,
|
info: true,
|
||||||
|
appPasswords: true,
|
||||||
notes: true,
|
notes: true,
|
||||||
auditLogs: true
|
auditLogs: true
|
||||||
})
|
})
|
||||||
@ -44,11 +47,11 @@ const UserInfo = () => {
|
|||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
reload: () => {
|
reload: () => {
|
||||||
objectFormRef?.current?.fetchObject?.()
|
objectFormRef?.current?.handleFetchObject?.()
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
setAppPassword: () => {
|
newAppPassword: () => {
|
||||||
setSetAppPasswordOpen(true)
|
setNewAppPasswordOpen(true)
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
edit: () => {
|
edit: () => {
|
||||||
@ -85,6 +88,7 @@ const UserInfo = () => {
|
|||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'User Information' },
|
{ key: 'info', label: 'User Information' },
|
||||||
|
{ key: 'appPasswords', label: 'App Passwords' },
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
]}
|
]}
|
||||||
@ -158,6 +162,26 @@ const UserInfo = () => {
|
|||||||
</ObjectForm>
|
</ObjectForm>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
</ActionHandler>
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='App Passwords'
|
||||||
|
icon={<AppPasswordIcon />}
|
||||||
|
active={collapseState.appPasswords}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('appPasswords', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='appPasswords'
|
||||||
|
>
|
||||||
|
{!userId ? (
|
||||||
|
<InfoCollapsePlaceholder />
|
||||||
|
) : (
|
||||||
|
<ObjectTable
|
||||||
|
type='appPassword'
|
||||||
|
masterFilter={{ user: userId }}
|
||||||
|
visibleColumns={{ user: false }}
|
||||||
|
ref={appPasswordsTableRef}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Notes'
|
title='Notes'
|
||||||
icon={<NoteIcon />}
|
icon={<NoteIcon />}
|
||||||
@ -193,16 +217,23 @@ const UserInfo = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
open={setAppPasswordOpen}
|
open={newAppPasswordOpen}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
width={650}
|
width={700}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
actionHandlerRef.current?.clearAction?.()
|
actionHandlerRef.current?.clearAction?.()
|
||||||
setSetAppPasswordOpen(false)
|
setNewAppPasswordOpen(false)
|
||||||
}}
|
}}
|
||||||
footer={null}
|
footer={null}
|
||||||
>
|
>
|
||||||
<SetAppPassword id={userId} />
|
<NewAppPassword
|
||||||
|
onOk={() => {
|
||||||
|
setNewAppPasswordOpen(false)
|
||||||
|
appPasswordsTableRef.current?.reload?.()
|
||||||
|
}}
|
||||||
|
reset={newAppPasswordOpen}
|
||||||
|
defaultValues={{ user: { ...objectFormState.objectData } }}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { createElement } from 'react'
|
import { createElement, useContext } from 'react'
|
||||||
import { Dropdown, Button } from 'antd'
|
import { Dropdown, Button } from 'antd'
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate, useLocation } from 'react-router-dom'
|
||||||
import { useActionsModal } from '../context/ActionsModalContext'
|
import { useActionsModal } from '../context/ActionsModalContext'
|
||||||
import KeyboardShortcut from './KeyboardShortcut'
|
import KeyboardShortcut from './KeyboardShortcut'
|
||||||
|
import { AuthContext } from '../context/AuthContext'
|
||||||
|
|
||||||
// Recursively filter actions based on visibleActions
|
// Recursively filter actions based on visibleActions
|
||||||
function filterActionsByVisibility(actions, visibleActions) {
|
function filterActionsByVisibility(actions, visibleActions) {
|
||||||
@ -47,9 +48,11 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
|
|||||||
var disabled = actionUrl && actionUrl === currentUrlWithActions
|
var disabled = actionUrl && actionUrl === currentUrlWithActions
|
||||||
var visible = true
|
var visible = true
|
||||||
|
|
||||||
|
const { userProfile } = useContext(AuthContext)
|
||||||
|
|
||||||
if (action.disabled) {
|
if (action.disabled) {
|
||||||
if (typeof action.disabled === 'function') {
|
if (typeof action.disabled === 'function') {
|
||||||
disabled = action.disabled(objectData)
|
disabled = action.disabled({ ...objectData, _user: userProfile })
|
||||||
} else {
|
} else {
|
||||||
disabled = action.disabled
|
disabled = action.disabled
|
||||||
}
|
}
|
||||||
@ -105,7 +108,6 @@ const ObjectActions = ({
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const { showActionsModal } = useActionsModal()
|
const { showActionsModal } = useActionsModal()
|
||||||
|
|
||||||
// Get current url without 'action' param
|
// Get current url without 'action' param
|
||||||
const currentUrlWithoutActions = stripActionParam(
|
const currentUrlWithoutActions = stripActionParam(
|
||||||
location.pathname,
|
location.pathname,
|
||||||
|
|||||||
@ -105,7 +105,7 @@ const ObjectTable = forwardRef(
|
|||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
const { isElectron } = useContext(ElectronContext)
|
const { isElectron } = useContext(ElectronContext)
|
||||||
const onStateChangeRef = useRef(onStateChange)
|
const onStateChangeRef = useRef(onStateChange)
|
||||||
|
const { userProfile } = useContext(AuthContext)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onStateChangeRef.current = onStateChange
|
onStateChangeRef.current = onStateChange
|
||||||
}, [onStateChange])
|
}, [onStateChange])
|
||||||
@ -191,7 +191,10 @@ const ObjectTable = forwardRef(
|
|||||||
var disabled = false
|
var disabled = false
|
||||||
if (action.disabled) {
|
if (action.disabled) {
|
||||||
if (typeof action.disabled === 'function') {
|
if (typeof action.disabled === 'function') {
|
||||||
disabled = action.disabled(objectData)
|
disabled = action.disabled({
|
||||||
|
...objectData,
|
||||||
|
_user: userProfile
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
disabled = action.disabled
|
disabled = action.disabled
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import PropTypes from 'prop-types'
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
|
import { AuthContext } from './AuthContext'
|
||||||
|
|
||||||
const ActionsModalContext = createContext()
|
const ActionsModalContext = createContext()
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ const ActionsModalProvider = ({ children }) => {
|
|||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const { userProfile } = useContext(AuthContext)
|
||||||
|
|
||||||
const [visible, setVisible] = useState(false)
|
const [visible, setVisible] = useState(false)
|
||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
@ -129,7 +131,7 @@ const ActionsModalProvider = ({ children }) => {
|
|||||||
|
|
||||||
if (typeof action.disabled !== 'undefined') {
|
if (typeof action.disabled !== 'undefined') {
|
||||||
if (typeof action.disabled === 'function') {
|
if (typeof action.disabled === 'function') {
|
||||||
disabled = action.disabled(objectData)
|
disabled = action.disabled({ ...objectData, _user: userProfile })
|
||||||
} else {
|
} else {
|
||||||
disabled = action.disabled
|
disabled = action.disabled
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/components/Icons/AppPasswordIcon.jsx
Normal file
6
src/components/Icons/AppPasswordIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/apppasswordicon.svg?react'
|
||||||
|
|
||||||
|
const AppPasswordIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default AppPasswordIcon
|
||||||
@ -22,6 +22,7 @@ import { OrderItem } from './models/OrderItem'
|
|||||||
import { Shipment } from './models/Shipment'
|
import { Shipment } from './models/Shipment'
|
||||||
import { AuditLog } from './models/AuditLog'
|
import { AuditLog } from './models/AuditLog'
|
||||||
import { User } from './models/User'
|
import { User } from './models/User'
|
||||||
|
import { AppPassword } from './models/AppPassword.js'
|
||||||
import { NoteType } from './models/NoteType'
|
import { NoteType } from './models/NoteType'
|
||||||
import { Note } from './models/Note'
|
import { Note } from './models/Note'
|
||||||
import { DocumentSize } from './models/DocumentSize.js'
|
import { DocumentSize } from './models/DocumentSize.js'
|
||||||
@ -61,6 +62,7 @@ export const objectModels = [
|
|||||||
Shipment,
|
Shipment,
|
||||||
AuditLog,
|
AuditLog,
|
||||||
User,
|
User,
|
||||||
|
AppPassword,
|
||||||
NoteType,
|
NoteType,
|
||||||
Note,
|
Note,
|
||||||
DocumentSize,
|
DocumentSize,
|
||||||
@ -101,6 +103,7 @@ export {
|
|||||||
Shipment,
|
Shipment,
|
||||||
AuditLog,
|
AuditLog,
|
||||||
User,
|
User,
|
||||||
|
AppPassword,
|
||||||
NoteType,
|
NoteType,
|
||||||
Note,
|
Note,
|
||||||
DocumentSize,
|
DocumentSize,
|
||||||
|
|||||||
139
src/database/models/AppPassword.js
Normal file
139
src/database/models/AppPassword.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import AppPasswordIcon from '../../components/Icons/AppPasswordIcon'
|
||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import LockIcon from '../../components/Icons/LockIcon'
|
||||||
|
|
||||||
|
export const AppPassword = {
|
||||||
|
name: 'appPassword',
|
||||||
|
label: 'App Password',
|
||||||
|
prefix: 'APP',
|
||||||
|
icon: AppPasswordIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/apppasswords/info?appPasswordId=${_id}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reload',
|
||||||
|
label: 'Reload',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=edit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
},
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?._user?._id != objectData?.user?._id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
icon: CheckIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=finishEdit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=cancelEdit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'regenerateSecret',
|
||||||
|
label: 'Regenerate Secret',
|
||||||
|
type: 'button',
|
||||||
|
row: true,
|
||||||
|
icon: LockIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=regenerateSecret`,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?._user?._id != objectData?.user?._id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: ['name', '_reference', 'user', 'active', 'createdAt', 'updatedAt'],
|
||||||
|
filters: ['_id', 'name', 'user', 'active', 'user._id'],
|
||||||
|
sorters: ['name', 'user', 'active', 'createdAt', 'updatedAt'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
columnFixed: 'left',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'appPassword',
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
columnFixed: 'left',
|
||||||
|
required: true,
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
label: 'User',
|
||||||
|
required: true,
|
||||||
|
readOnly: (objectData) => {
|
||||||
|
return objectData?.createdAt != null
|
||||||
|
},
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'user',
|
||||||
|
showHyperlink: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'active',
|
||||||
|
label: 'Active',
|
||||||
|
required: true,
|
||||||
|
type: 'bool'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'secret',
|
||||||
|
label: 'Secret',
|
||||||
|
type: 'password',
|
||||||
|
required: false,
|
||||||
|
readOnly: true,
|
||||||
|
value: (objectData) =>
|
||||||
|
objectData?._id ? '••••••••••••••••••••••••••••••••' : undefined
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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 ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
import LockIcon from '../../components/Icons/LockIcon'
|
import AppPasswordIcon from '../../components/Icons/AppPasswordIcon'
|
||||||
|
|
||||||
export const User = {
|
export const User = {
|
||||||
name: 'user',
|
name: 'user',
|
||||||
@ -25,11 +25,15 @@ export const User = {
|
|||||||
`/dashboard/management/users/info?userId=${_id}&action=reload`
|
`/dashboard/management/users/info?userId=${_id}&action=reload`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'setAppPassword',
|
name: 'newAppPassword',
|
||||||
label: 'Set App Password',
|
label: 'New App Password',
|
||||||
icon: LockIcon,
|
type: 'button',
|
||||||
|
icon: AppPasswordIcon,
|
||||||
url: (_id) =>
|
url: (_id) =>
|
||||||
`/dashboard/management/users/info?userId=${_id}&action=setAppPassword`
|
`/dashboard/management/users/info?userId=${_id}&action=newAppPassword`,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?._user?._id != objectData?._id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: ['name', '_reference', 'username', 'email', 'role', 'createdAt'],
|
columns: ['name', '_reference', 'username', 'email', 'role', 'createdAt'],
|
||||||
|
|||||||
@ -21,6 +21,8 @@ const NoteTypeInfo = lazy(() => import('../components/Dashboard/Management/NoteT
|
|||||||
const NoteInfo = lazy(() => import('../components/Dashboard/Management/Notes/NoteInfo.jsx'))
|
const NoteInfo = lazy(() => import('../components/Dashboard/Management/Notes/NoteInfo.jsx'))
|
||||||
const Users = lazy(() => import('../components/Dashboard/Management/Users.jsx'))
|
const Users = lazy(() => import('../components/Dashboard/Management/Users.jsx'))
|
||||||
const UserInfo = lazy(() => import('../components/Dashboard/Management/Users/UserInfo.jsx'))
|
const UserInfo = lazy(() => import('../components/Dashboard/Management/Users/UserInfo.jsx'))
|
||||||
|
const AppPasswords = lazy(() => import('../components/Dashboard/Management/AppPasswords.jsx'))
|
||||||
|
const AppPasswordInfo = lazy(() => import('../components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx'))
|
||||||
const Hosts = lazy(() => import('../components/Dashboard/Management/Hosts.jsx'))
|
const Hosts = lazy(() => import('../components/Dashboard/Management/Hosts.jsx'))
|
||||||
const HostInfo = lazy(() => import('../components/Dashboard/Management/Hosts/HostInfo.jsx'))
|
const HostInfo = lazy(() => import('../components/Dashboard/Management/Hosts/HostInfo.jsx'))
|
||||||
const DocumentSizes = lazy(() => import('../components/Dashboard/Management/DocumentSizes.jsx'))
|
const DocumentSizes = lazy(() => import('../components/Dashboard/Management/DocumentSizes.jsx'))
|
||||||
@ -151,6 +153,16 @@ const ManagementRoutes = [
|
|||||||
element={<DocumentTemplateDesign />}
|
element={<DocumentTemplateDesign />}
|
||||||
/>,
|
/>,
|
||||||
<Route key='users' path='management/users' element={<Users />} />,
|
<Route key='users' path='management/users' element={<Users />} />,
|
||||||
|
<Route
|
||||||
|
key='apppasswords'
|
||||||
|
path='management/apppasswords'
|
||||||
|
element={<AppPasswords />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='apppasswords-info'
|
||||||
|
path='management/apppasswords/info'
|
||||||
|
element={<AppPasswordInfo />}
|
||||||
|
/>,
|
||||||
<Route key='settings' path='management/settings' element={<Settings />} />,
|
<Route key='settings' path='management/settings' element={<Settings />} />,
|
||||||
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />,
|
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />,
|
||||||
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user