diff --git a/assets/icons/apppasswordicon.svg b/assets/icons/apppasswordicon.svg
new file mode 100644
index 0000000..d74a6e7
--- /dev/null
+++ b/assets/icons/apppasswordicon.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/components/Dashboard/Management/AppPasswords.jsx b/src/components/Dashboard/Management/AppPasswords.jsx
new file mode 100644
index 0000000..b9d31ef
--- /dev/null
+++ b/src/components/Dashboard/Management/AppPasswords.jsx
@@ -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:
+ },
+ { type: 'divider' },
+ {
+ label: 'Reload List',
+ key: 'reloadList',
+ icon:
+ }
+ ],
+ onClick: ({ key }) => {
+ if (key === 'reloadList') {
+ tableRef.current?.reload()
+ } else if (key === 'newAppPassword') {
+ setNewAppPasswordOpen(true)
+ }
+ }
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ : }
+ onClick={() =>
+ setViewMode(viewMode === 'cards' ? 'list' : 'cards')
+ }
+ />
+
+
+
+
+
+ {
+ setNewAppPasswordOpen(false)
+ }}
+ >
+ {
+ setNewAppPasswordOpen(false)
+ tableRef.current?.reload()
+ }}
+ reset={newAppPasswordOpen}
+ />
+
+
+ >
+ )
+}
+
+export default AppPasswords
diff --git a/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx b/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx
new file mode 100644
index 0000000..c51877b
--- /dev/null
+++ b/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx
@@ -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 (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ 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}
+ />
+
+
+
+
+
+ }
+ active={collapseState.info}
+ onToggle={(expanded) => updateCollapseState('info', expanded)}
+ collapseKey='info'
+ >
+ {
+ setEditFormState((prev) => ({ ...prev, ...state }))
+ }}
+ >
+ {({ loading, isEditing, objectData }) => (
+ }
+ isEditing={isEditing}
+ type='appPassword'
+ objectData={objectData}
+ />
+ )}
+
+
+
+ }
+ active={collapseState.auditLogs}
+ onToggle={(expanded) =>
+ updateCollapseState('auditLogs', expanded)
+ }
+ collapseKey='auditLogs'
+ >
+ {objectFormState.loading ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {
+ actionHandlerRef.current?.clearAction?.()
+ setRegenerateSecretOpen(false)
+ }}
+ footer={null}
+ >
+
+
+ >
+ )
+}
+
+export default AppPasswordInfo
diff --git a/src/components/Dashboard/Management/AppPasswords/NewAppPassword.jsx b/src/components/Dashboard/Management/AppPasswords/NewAppPassword.jsx
new file mode 100644
index 0000000..5563d53
--- /dev/null
+++ b/src/components/Dashboard/Management/AppPasswords/NewAppPassword.jsx
@@ -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 (
+
+ {({ handleSubmit, submitLoading, objectData, formValid }) => {
+ const steps = [
+ {
+ title: 'Required',
+ key: 'required',
+ content: (
+
+ )
+ },
+ {
+ title: 'Optional',
+ key: 'optional',
+ content: (
+
+ )
+ },
+ {
+ title: 'Summary',
+ key: 'summary',
+ content: (
+
+ )
+ }
+ ]
+ return (
+ {
+ const result = await handleSubmit()
+ if (result) {
+ onOk()
+ }
+ }}
+ />
+ )
+ }}
+
+ )
+}
+
+NewAppPassword.propTypes = {
+ onOk: PropTypes.func.isRequired,
+ reset: PropTypes.bool,
+ defaultValues: PropTypes.object
+}
+
+export default NewAppPassword
diff --git a/src/components/Dashboard/Management/AppPasswords/RegenerateAppPasswordSecret.jsx b/src/components/Dashboard/Management/AppPasswords/RegenerateAppPasswordSecret.jsx
new file mode 100644
index 0000000..22b60e0
--- /dev/null
+++ b/src/components/Dashboard/Management/AppPasswords/RegenerateAppPasswordSecret.jsx
@@ -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 (
+
+ Copy this secret now. It will not be shown again.
+ ) : (
+ Generate a new secret for this app password.
+ )
+ }
+ icon={}
+ >
+
+
+
+
+
+ {appPassword || '••••••••••••••••••••••••••••••••'}
+
+ }
+ />
+
+
+
+
+
+ )
+}
+
+RegenerateAppPasswordSecret.propTypes = {
+ id: PropTypes.string.isRequired
+}
+
+export default RegenerateAppPasswordSecret
diff --git a/src/components/Dashboard/Management/ManagementSidebar.jsx b/src/components/Dashboard/Management/ManagementSidebar.jsx
index 4345eae..2d42229 100644
--- a/src/components/Dashboard/Management/ManagementSidebar.jsx
+++ b/src/components/Dashboard/Management/ManagementSidebar.jsx
@@ -21,6 +21,7 @@ import CourierIcon from '../../Icons/CourierIcon'
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
import TaxRateIcon from '../../Icons/TaxRateIcon'
import TaxRecordIcon from '../../Icons/TaxRecordIcon'
+import AppPasswordIcon from '../../Icons/AppPasswordIcon'
const items = [
{
@@ -131,6 +132,12 @@ const items = [
label: 'Users',
path: '/dashboard/management/users'
},
+ {
+ key: 'appPasswords',
+ icon: ,
+ label: 'App Passwords',
+ path: '/dashboard/management/apppasswords'
+ },
{
key: 'settings',
icon: ,
@@ -167,6 +174,7 @@ const routeKeyMap = {
'/dashboard/management/filaments': 'filaments',
'/dashboard/management/parts': 'parts',
'/dashboard/management/users': 'users',
+ '/dashboard/management/apppasswords': 'appPasswords',
'/dashboard/management/products': 'products',
'/dashboard/management/vendors': 'vendors',
'/dashboard/management/couriers': 'couriers',
diff --git a/src/components/Dashboard/Management/Users/UserInfo.jsx b/src/components/Dashboard/Management/Users/UserInfo.jsx
index 196b096..689efbd 100644
--- a/src/components/Dashboard/Management/Users/UserInfo.jsx
+++ b/src/components/Dashboard/Management/Users/UserInfo.jsx
@@ -10,6 +10,7 @@ import ViewButton from '../../common/ViewButton'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
+import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
import ObjectForm from '../../common/ObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
@@ -20,16 +21,18 @@ 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 SetAppPassword from './SetAppPassword.jsx'
+import NewAppPassword from '../AppPasswords/NewAppPassword.jsx'
const UserInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
+ const appPasswordsTableRef = useRef(null)
const actionHandlerRef = useRef(null)
const userId = new URLSearchParams(location.search).get('userId')
- const [setAppPasswordOpen, setSetAppPasswordOpen] = useState(false)
+ const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
info: true,
+ appPasswords: true,
notes: true,
auditLogs: true
})
@@ -44,11 +47,11 @@ const UserInfo = () => {
const actions = {
reload: () => {
- objectFormRef?.current?.fetchObject?.()
+ objectFormRef?.current?.handleFetchObject?.()
return true
},
- setAppPassword: () => {
- setSetAppPasswordOpen(true)
+ newAppPassword: () => {
+ setNewAppPasswordOpen(true)
return false
},
edit: () => {
@@ -85,6 +88,7 @@ const UserInfo = () => {
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'User Information' },
+ { key: 'appPasswords', label: 'App Passwords' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
@@ -158,6 +162,26 @@ const UserInfo = () => {
+ }
+ active={collapseState.appPasswords}
+ onToggle={(expanded) =>
+ updateCollapseState('appPasswords', expanded)
+ }
+ collapseKey='appPasswords'
+ >
+ {!userId ? (
+
+ ) : (
+
+ )}
+
}
@@ -193,16 +217,23 @@ const UserInfo = () => {
{
actionHandlerRef.current?.clearAction?.()
- setSetAppPasswordOpen(false)
+ setNewAppPasswordOpen(false)
}}
footer={null}
>
-
+ {
+ setNewAppPasswordOpen(false)
+ appPasswordsTableRef.current?.reload?.()
+ }}
+ reset={newAppPasswordOpen}
+ defaultValues={{ user: { ...objectFormState.objectData } }}
+ />
>
)
diff --git a/src/components/Dashboard/common/ObjectActions.jsx b/src/components/Dashboard/common/ObjectActions.jsx
index 852dfef..54f44db 100644
--- a/src/components/Dashboard/common/ObjectActions.jsx
+++ b/src/components/Dashboard/common/ObjectActions.jsx
@@ -1,10 +1,11 @@
-import { createElement } from 'react'
+import { createElement, useContext } from 'react'
import { Dropdown, Button } from 'antd'
import { getModelByName } from '../../../database/ObjectModels'
import PropTypes from 'prop-types'
import { useNavigate, useLocation } from 'react-router-dom'
import { useActionsModal } from '../context/ActionsModalContext'
import KeyboardShortcut from './KeyboardShortcut'
+import { AuthContext } from '../context/AuthContext'
// Recursively filter actions based on visibleActions
function filterActionsByVisibility(actions, visibleActions) {
@@ -47,9 +48,11 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
var disabled = actionUrl && actionUrl === currentUrlWithActions
var visible = true
+ const { userProfile } = useContext(AuthContext)
+
if (action.disabled) {
if (typeof action.disabled === 'function') {
- disabled = action.disabled(objectData)
+ disabled = action.disabled({ ...objectData, _user: userProfile })
} else {
disabled = action.disabled
}
@@ -105,7 +108,6 @@ const ObjectActions = ({
const navigate = useNavigate()
const location = useLocation()
const { showActionsModal } = useActionsModal()
-
// Get current url without 'action' param
const currentUrlWithoutActions = stripActionParam(
location.pathname,
diff --git a/src/components/Dashboard/common/ObjectTable.jsx b/src/components/Dashboard/common/ObjectTable.jsx
index 082e3a1..29a5fe5 100644
--- a/src/components/Dashboard/common/ObjectTable.jsx
+++ b/src/components/Dashboard/common/ObjectTable.jsx
@@ -105,7 +105,7 @@ const ObjectTable = forwardRef(
const { token } = useContext(AuthContext)
const { isElectron } = useContext(ElectronContext)
const onStateChangeRef = useRef(onStateChange)
-
+ const { userProfile } = useContext(AuthContext)
useEffect(() => {
onStateChangeRef.current = onStateChange
}, [onStateChange])
@@ -191,7 +191,10 @@ const ObjectTable = forwardRef(
var disabled = false
if (action.disabled) {
if (typeof action.disabled === 'function') {
- disabled = action.disabled(objectData)
+ disabled = action.disabled({
+ ...objectData,
+ _user: userProfile
+ })
} else {
disabled = action.disabled
}
diff --git a/src/components/Dashboard/context/ActionsModalContext.jsx b/src/components/Dashboard/context/ActionsModalContext.jsx
index 98c26c3..1c33778 100644
--- a/src/components/Dashboard/context/ActionsModalContext.jsx
+++ b/src/components/Dashboard/context/ActionsModalContext.jsx
@@ -11,6 +11,7 @@ import PropTypes from 'prop-types'
import { useLocation, useNavigate } from 'react-router-dom'
import { getModelByName } from '../../../database/ObjectModels'
+import { AuthContext } from './AuthContext'
const ActionsModalContext = createContext()
@@ -63,6 +64,7 @@ const ActionsModalProvider = ({ children }) => {
const { Text } = Typography
const navigate = useNavigate()
const location = useLocation()
+ const { userProfile } = useContext(AuthContext)
const [visible, setVisible] = useState(false)
const [query, setQuery] = useState('')
@@ -129,7 +131,7 @@ const ActionsModalProvider = ({ children }) => {
if (typeof action.disabled !== 'undefined') {
if (typeof action.disabled === 'function') {
- disabled = action.disabled(objectData)
+ disabled = action.disabled({ ...objectData, _user: userProfile })
} else {
disabled = action.disabled
}
diff --git a/src/components/Icons/AppPasswordIcon.jsx b/src/components/Icons/AppPasswordIcon.jsx
new file mode 100644
index 0000000..8f3a5e0
--- /dev/null
+++ b/src/components/Icons/AppPasswordIcon.jsx
@@ -0,0 +1,6 @@
+import Icon from '@ant-design/icons'
+import CustomIconSvg from '../../../assets/icons/apppasswordicon.svg?react'
+
+const AppPasswordIcon = (props) =>
+
+export default AppPasswordIcon
diff --git a/src/database/ObjectModels.js b/src/database/ObjectModels.js
index 83196f9..00ba537 100644
--- a/src/database/ObjectModels.js
+++ b/src/database/ObjectModels.js
@@ -22,6 +22,7 @@ import { OrderItem } from './models/OrderItem'
import { Shipment } from './models/Shipment'
import { AuditLog } from './models/AuditLog'
import { User } from './models/User'
+import { AppPassword } from './models/AppPassword.js'
import { NoteType } from './models/NoteType'
import { Note } from './models/Note'
import { DocumentSize } from './models/DocumentSize.js'
@@ -61,6 +62,7 @@ export const objectModels = [
Shipment,
AuditLog,
User,
+ AppPassword,
NoteType,
Note,
DocumentSize,
@@ -101,6 +103,7 @@ export {
Shipment,
AuditLog,
User,
+ AppPassword,
NoteType,
Note,
DocumentSize,
diff --git a/src/database/models/AppPassword.js b/src/database/models/AppPassword.js
new file mode 100644
index 0000000..1b1a72e
--- /dev/null
+++ b/src/database/models/AppPassword.js
@@ -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
+ }
+ ]
+}
diff --git a/src/database/models/User.js b/src/database/models/User.js
index 7f9f41b..cd967ed 100644
--- a/src/database/models/User.js
+++ b/src/database/models/User.js
@@ -1,7 +1,7 @@
import PersonIcon from '../../components/Icons/PersonIcon'
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
import ReloadIcon from '../../components/Icons/ReloadIcon'
-import LockIcon from '../../components/Icons/LockIcon'
+import AppPasswordIcon from '../../components/Icons/AppPasswordIcon'
export const User = {
name: 'user',
@@ -25,11 +25,15 @@ export const User = {
`/dashboard/management/users/info?userId=${_id}&action=reload`
},
{
- name: 'setAppPassword',
- label: 'Set App Password',
- icon: LockIcon,
+ name: 'newAppPassword',
+ label: 'New App Password',
+ type: 'button',
+ icon: AppPasswordIcon,
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'],
diff --git a/src/routes/ManagementRoutes.jsx b/src/routes/ManagementRoutes.jsx
index 074e9f7..3cc6b0e 100644
--- a/src/routes/ManagementRoutes.jsx
+++ b/src/routes/ManagementRoutes.jsx
@@ -21,6 +21,8 @@ const NoteTypeInfo = lazy(() => import('../components/Dashboard/Management/NoteT
const NoteInfo = lazy(() => import('../components/Dashboard/Management/Notes/NoteInfo.jsx'))
const Users = lazy(() => import('../components/Dashboard/Management/Users.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 HostInfo = lazy(() => import('../components/Dashboard/Management/Hosts/HostInfo.jsx'))
const DocumentSizes = lazy(() => import('../components/Dashboard/Management/DocumentSizes.jsx'))
@@ -151,6 +153,16 @@ const ManagementRoutes = [
element={}
/>,
} />,
+ }
+ />,
+ }
+ />,
} />,
} />,
} />,