mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 21:00:58 +03:00
add credentials
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import client from './client'
|
||||
|
||||
const getAllCredentials = () => client.get('/credentials')
|
||||
|
||||
const getCredentialsByName = (componentCredentialName) => client.get(`/credentials?credentialName=${componentCredentialName}`)
|
||||
|
||||
const getAllComponentsCredentials = () => client.get('/components-credentials')
|
||||
|
||||
const getSpecificCredential = (id) => client.get(`/credentials/${id}`)
|
||||
|
||||
const getSpecificComponentCredential = (name) => client.get(`/components-credentials/${name}`)
|
||||
|
||||
const createCredential = (body) => client.post(`/credentials`, body)
|
||||
|
||||
const updateCredential = (id, body) => client.put(`/credentials/${id}`, body)
|
||||
|
||||
const deleteCredential = (id) => client.delete(`/credentials/${id}`)
|
||||
|
||||
export default {
|
||||
getAllCredentials,
|
||||
getCredentialsByName,
|
||||
getAllComponentsCredentials,
|
||||
getSpecificCredential,
|
||||
getSpecificComponentCredential,
|
||||
createCredential,
|
||||
updateCredential,
|
||||
deleteCredential
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.2 KiB |
@@ -1,8 +1,8 @@
|
||||
// assets
|
||||
import { IconHierarchy, IconBuildingStore, IconKey, IconTool } from '@tabler/icons'
|
||||
import { IconHierarchy, IconBuildingStore, IconKey, IconTool, IconLock } from '@tabler/icons'
|
||||
|
||||
// constant
|
||||
const icons = { IconHierarchy, IconBuildingStore, IconKey, IconTool }
|
||||
const icons = { IconHierarchy, IconBuildingStore, IconKey, IconTool, IconLock }
|
||||
|
||||
// ==============================|| DASHBOARD MENU ITEMS ||============================== //
|
||||
|
||||
@@ -35,6 +35,14 @@ const dashboard = {
|
||||
icon: icons.IconTool,
|
||||
breadcrumbs: true
|
||||
},
|
||||
{
|
||||
id: 'credentials',
|
||||
title: 'Credentials',
|
||||
type: 'item',
|
||||
url: '/credentials',
|
||||
icon: icons.IconLock,
|
||||
breadcrumbs: true
|
||||
},
|
||||
{
|
||||
id: 'apikey',
|
||||
title: 'API Keys',
|
||||
|
||||
@@ -13,9 +13,12 @@ const Marketplaces = Loadable(lazy(() => import('views/marketplaces')))
|
||||
// apikey routing
|
||||
const APIKey = Loadable(lazy(() => import('views/apikey')))
|
||||
|
||||
// apikey routing
|
||||
// tools routing
|
||||
const Tools = Loadable(lazy(() => import('views/tools')))
|
||||
|
||||
// credentials routing
|
||||
const Credentials = Loadable(lazy(() => import('views/credentials')))
|
||||
|
||||
// ==============================|| MAIN ROUTING ||============================== //
|
||||
|
||||
const MainRoutes = {
|
||||
@@ -41,6 +44,10 @@ const MainRoutes = {
|
||||
{
|
||||
path: '/tools',
|
||||
element: <Tools />
|
||||
},
|
||||
{
|
||||
path: '/credentials',
|
||||
element: <Credentials />
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ export const appDrawerWidth = 320
|
||||
export const maxScroll = 100000
|
||||
export const baseURL = process.env.NODE_ENV === 'production' ? window.location.origin : window.location.origin.replace(':8080', ':3000')
|
||||
export const uiBaseURL = window.location.origin
|
||||
export const FLOWISE_CREDENTIAL_ID = 'FLOWISE_CREDENTIAL_ID'
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useState, useEffect, Fragment } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import PropTypes from 'prop-types'
|
||||
import axios from 'axios'
|
||||
|
||||
// Material
|
||||
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'
|
||||
import { Popper, CircularProgress, TextField, Box, Typography } from '@mui/material'
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
// API
|
||||
import credentialsApi from 'api/credentials'
|
||||
|
||||
// const
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
const StyledPopper = styled(Popper)({
|
||||
@@ -49,6 +53,7 @@ export const AsyncDropdown = ({
|
||||
onSelect,
|
||||
isCreateNewOption,
|
||||
onCreateNew,
|
||||
credentialNames = [],
|
||||
disabled = false,
|
||||
disableClearable = false
|
||||
}) => {
|
||||
@@ -62,11 +67,36 @@ export const AsyncDropdown = ({
|
||||
const addNewOption = [{ label: '- Create New -', name: '-create-' }]
|
||||
let [internalValue, setInternalValue] = useState(value ?? 'choose an option')
|
||||
|
||||
const fetchCredentialList = async () => {
|
||||
try {
|
||||
let names = ''
|
||||
if (credentialNames.length > 1) {
|
||||
names = credentialNames.join('&credentialName=')
|
||||
} else {
|
||||
names = credentialNames[0]
|
||||
}
|
||||
const resp = await credentialsApi.getCredentialsByName(names)
|
||||
if (resp.data) {
|
||||
const returnList = []
|
||||
for (let i = 0; i < resp.data.length; i += 1) {
|
||||
const data = {
|
||||
label: resp.data[i].name,
|
||||
name: resp.data[i].id
|
||||
}
|
||||
returnList.push(data)
|
||||
}
|
||||
return returnList
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
;(async () => {
|
||||
const fetchData = async () => {
|
||||
let response = await fetchList({ name, nodeData })
|
||||
let response = credentialNames.length ? await fetchCredentialList() : await fetchList({ name, nodeData })
|
||||
if (isCreateNewOption) setOptions([...response, ...addNewOption])
|
||||
else setOptions([...response])
|
||||
setLoading(false)
|
||||
@@ -142,6 +172,7 @@ AsyncDropdown.propTypes = {
|
||||
onSelect: PropTypes.func,
|
||||
onCreateNew: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
credentialNames: PropTypes.array,
|
||||
disableClearable: PropTypes.bool,
|
||||
isCreateNewOption: PropTypes.bool
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export const Input = ({ inputParam, value, onChange, disabled = false, showDialo
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
inputProps={{
|
||||
step: 0.1,
|
||||
step: inputParam.step ?? 0.1,
|
||||
style: {
|
||||
height: inputParam.rows ? '90px' : 'inherit'
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ export const initNode = (nodeData, newNodeId) => {
|
||||
|
||||
const whitelistTypes = ['asyncOptions', 'options', 'string', 'number', 'boolean', 'password', 'json', 'code', 'date', 'file', 'folder']
|
||||
|
||||
// Inputs
|
||||
for (let i = 0; i < incoming; i += 1) {
|
||||
const newInput = {
|
||||
...nodeData.inputs[i],
|
||||
@@ -53,6 +54,16 @@ export const initNode = (nodeData, newNodeId) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Credential
|
||||
if (nodeData.credential) {
|
||||
const newInput = {
|
||||
...nodeData.credential,
|
||||
id: `${newNodeId}-input-${nodeData.credential.name}-${nodeData.credential.type}`
|
||||
}
|
||||
inputParams.unshift(newInput)
|
||||
}
|
||||
|
||||
// Outputs
|
||||
const outputAnchors = []
|
||||
for (let i = 0; i < outgoing; i += 1) {
|
||||
if (nodeData.outputs && nodeData.outputs.length) {
|
||||
@@ -129,6 +140,8 @@ export const initNode = (nodeData, newNodeId) => {
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
// Inputs
|
||||
if (nodeData.inputs) {
|
||||
nodeData.inputAnchors = inputAnchors
|
||||
nodeData.inputParams = inputParams
|
||||
@@ -139,13 +152,17 @@ export const initNode = (nodeData, newNodeId) => {
|
||||
nodeData.inputs = {}
|
||||
}
|
||||
|
||||
// Outputs
|
||||
if (nodeData.outputs) {
|
||||
nodeData.outputs = initializeDefaultNodeData(outputAnchors)
|
||||
} else {
|
||||
nodeData.outputs = {}
|
||||
}
|
||||
|
||||
nodeData.outputAnchors = outputAnchors
|
||||
|
||||
// Credential
|
||||
if (nodeData.credential) nodeData.credential = ''
|
||||
|
||||
nodeData.id = newNodeId
|
||||
|
||||
return nodeData
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
// material-ui
|
||||
import { IconButton } from '@mui/material'
|
||||
import { IconEdit } from '@tabler/icons'
|
||||
|
||||
// project import
|
||||
import { AsyncDropdown } from 'ui-component/dropdown/AsyncDropdown'
|
||||
import AddEditCredentialDialog from 'views/credentials/AddEditCredentialDialog'
|
||||
import CredentialListDialog from 'views/credentials/CredentialListDialog'
|
||||
|
||||
// API
|
||||
import credentialsApi from 'api/credentials'
|
||||
|
||||
// ===========================|| CredentialInputHandler ||=========================== //
|
||||
|
||||
const CredentialInputHandler = ({ inputParam, data, onSelect, disabled = false }) => {
|
||||
const ref = useRef(null)
|
||||
const [credentialId, setCredentialId] = useState(data?.credential ?? '')
|
||||
const [showCredentialListDialog, setShowCredentialListDialog] = useState(false)
|
||||
const [credentialListDialogProps, setCredentialListDialogProps] = useState({})
|
||||
const [showSpecificCredentialDialog, setShowSpecificCredentialDialog] = useState(false)
|
||||
const [specificCredentialDialogProps, setSpecificCredentialDialogProps] = useState({})
|
||||
const [reloadTimestamp, setReloadTimestamp] = useState(Date.now().toString())
|
||||
|
||||
const editCredential = (credentialId) => {
|
||||
const dialogProp = {
|
||||
type: 'EDIT',
|
||||
cancelButtonName: 'Cancel',
|
||||
confirmButtonName: 'Save',
|
||||
credentialId
|
||||
}
|
||||
setSpecificCredentialDialogProps(dialogProp)
|
||||
setShowSpecificCredentialDialog(true)
|
||||
}
|
||||
|
||||
const addAsyncOption = async () => {
|
||||
try {
|
||||
let names = ''
|
||||
if (inputParam.credentialNames.length > 1) {
|
||||
names = inputParam.credentialNames.join('&')
|
||||
} else {
|
||||
names = inputParam.credentialNames[0]
|
||||
}
|
||||
const componentCredentialsResp = await credentialsApi.getSpecificComponentCredential(names)
|
||||
if (componentCredentialsResp.data) {
|
||||
if (Array.isArray(componentCredentialsResp.data)) {
|
||||
const dialogProp = {
|
||||
title: 'Add New Credential',
|
||||
componentsCredentials: componentCredentialsResp.data
|
||||
}
|
||||
setCredentialListDialogProps(dialogProp)
|
||||
setShowCredentialListDialog(true)
|
||||
} else {
|
||||
const dialogProp = {
|
||||
type: 'ADD',
|
||||
cancelButtonName: 'Cancel',
|
||||
confirmButtonName: 'Add',
|
||||
credentialComponent: componentCredentialsResp.data
|
||||
}
|
||||
setSpecificCredentialDialogProps(dialogProp)
|
||||
setShowSpecificCredentialDialog(true)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const onConfirmAsyncOption = (selectedCredentialId = '') => {
|
||||
setCredentialId(selectedCredentialId)
|
||||
setReloadTimestamp(Date.now().toString())
|
||||
setSpecificCredentialDialogProps({})
|
||||
setShowSpecificCredentialDialog(false)
|
||||
onSelect(selectedCredentialId)
|
||||
}
|
||||
|
||||
const onCredentialSelected = (credentialComponent) => {
|
||||
setShowCredentialListDialog(false)
|
||||
const dialogProp = {
|
||||
type: 'ADD',
|
||||
cancelButtonName: 'Cancel',
|
||||
confirmButtonName: 'Add',
|
||||
credentialComponent
|
||||
}
|
||||
setSpecificCredentialDialogProps(dialogProp)
|
||||
setShowSpecificCredentialDialog(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
{inputParam && (
|
||||
<>
|
||||
{inputParam.type === 'credential' && (
|
||||
<>
|
||||
<div style={{ marginTop: 10 }} />
|
||||
<div key={reloadTimestamp} style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<AsyncDropdown
|
||||
disabled={disabled}
|
||||
name={inputParam.name}
|
||||
nodeData={data}
|
||||
value={credentialId ?? 'choose an option'}
|
||||
isCreateNewOption={true}
|
||||
credentialNames={inputParam.credentialNames}
|
||||
onSelect={(newValue) => {
|
||||
setCredentialId(newValue)
|
||||
onSelect(newValue)
|
||||
}}
|
||||
onCreateNew={() => addAsyncOption(inputParam.name)}
|
||||
/>
|
||||
{credentialId && (
|
||||
<IconButton title='Edit' color='primary' size='small' onClick={() => editCredential(credentialId)}>
|
||||
<IconEdit />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{showSpecificCredentialDialog && (
|
||||
<AddEditCredentialDialog
|
||||
show={showSpecificCredentialDialog}
|
||||
dialogProps={specificCredentialDialogProps}
|
||||
onCancel={() => setShowSpecificCredentialDialog(false)}
|
||||
onConfirm={onConfirmAsyncOption}
|
||||
></AddEditCredentialDialog>
|
||||
)}
|
||||
{showCredentialListDialog && (
|
||||
<CredentialListDialog
|
||||
show={showCredentialListDialog}
|
||||
dialogProps={credentialListDialogProps}
|
||||
onCancel={() => setShowCredentialListDialog(false)}
|
||||
onCredentialSelected={onCredentialSelected}
|
||||
></CredentialListDialog>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
CredentialInputHandler.propTypes = {
|
||||
inputParam: PropTypes.object,
|
||||
data: PropTypes.object,
|
||||
onSelect: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
}
|
||||
|
||||
export default CredentialInputHandler
|
||||
@@ -21,9 +21,14 @@ import { JsonEditorInput } from 'ui-component/json/JsonEditor'
|
||||
import { TooltipWithParser } from 'ui-component/tooltip/TooltipWithParser'
|
||||
import ToolDialog from 'views/tools/ToolDialog'
|
||||
import FormatPromptValuesDialog from 'ui-component/dialog/FormatPromptValuesDialog'
|
||||
import CredentialInputHandler from './CredentialInputHandler'
|
||||
|
||||
// utils
|
||||
import { getInputVariables } from 'utils/genericHelper'
|
||||
|
||||
// const
|
||||
import { FLOWISE_CREDENTIAL_ID } from 'store/constant'
|
||||
|
||||
const EDITABLE_TOOLS = ['selectedTool']
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)({
|
||||
@@ -226,6 +231,17 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
|
||||
<span style={{ color: 'rgb(116,66,16)', marginLeft: 10 }}>{inputParam.warning}</span>
|
||||
</div>
|
||||
)}
|
||||
{inputParam.type === 'credential' && (
|
||||
<CredentialInputHandler
|
||||
disabled={disabled}
|
||||
data={data}
|
||||
inputParam={inputParam}
|
||||
onSelect={(newValue) => {
|
||||
data.credential = newValue
|
||||
data.inputs[FLOWISE_CREDENTIAL_ID] = newValue // in case data.credential is not updated
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{inputParam.type === 'file' && (
|
||||
<File
|
||||
disabled={disabled}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
enqueueSnackbar as enqueueSnackbarAction,
|
||||
closeSnackbar as closeSnackbarAction
|
||||
} from 'store/actions'
|
||||
import { omit, cloneDeep } from 'lodash'
|
||||
|
||||
// material-ui
|
||||
import { Toolbar, Box, AppBar, Button } from '@mui/material'
|
||||
@@ -41,6 +42,9 @@ import { IconX } from '@tabler/icons'
|
||||
import { getUniqueNodeId, initNode, getEdgeLabelName, rearrangeToolsOrdering } from 'utils/genericHelper'
|
||||
import useNotifier from 'utils/useNotifier'
|
||||
|
||||
// const
|
||||
import { FLOWISE_CREDENTIAL_ID } from 'store/constant'
|
||||
|
||||
const nodeTypes = { customNode: CanvasNode }
|
||||
const edgeTypes = { buttonedge: ButtonEdge }
|
||||
|
||||
@@ -185,17 +189,21 @@ const Canvas = () => {
|
||||
|
||||
const handleSaveFlow = (chatflowName) => {
|
||||
if (reactFlowInstance) {
|
||||
setNodes((nds) =>
|
||||
nds.map((node) => {
|
||||
node.data = {
|
||||
...node.data,
|
||||
selected: false
|
||||
}
|
||||
return node
|
||||
})
|
||||
)
|
||||
const nodes = reactFlowInstance.getNodes().map((node) => {
|
||||
const nodeData = cloneDeep(node.data)
|
||||
if (Object.prototype.hasOwnProperty.call(nodeData.inputs, FLOWISE_CREDENTIAL_ID)) {
|
||||
nodeData.credential = nodeData.inputs[FLOWISE_CREDENTIAL_ID]
|
||||
nodeData.inputs = omit(nodeData.inputs, [FLOWISE_CREDENTIAL_ID])
|
||||
}
|
||||
node.data = {
|
||||
...nodeData,
|
||||
selected: false
|
||||
}
|
||||
return node
|
||||
})
|
||||
|
||||
const rfInstanceObject = reactFlowInstance.toObject()
|
||||
rfInstanceObject.nodes = nodes
|
||||
const flowData = JSON.stringify(rfInstanceObject)
|
||||
|
||||
if (!chatflow.id) {
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'
|
||||
import parser from 'html-react-parser'
|
||||
|
||||
// Material
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Box, Stack, OutlinedInput, Typography } from '@mui/material'
|
||||
|
||||
// Project imports
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import ConfirmDialog from 'ui-component/dialog/ConfirmDialog'
|
||||
import CredentialInputHandler from './CredentialInputHandler'
|
||||
|
||||
// Icons
|
||||
import { IconX } from '@tabler/icons'
|
||||
|
||||
// API
|
||||
import credentialsApi from 'api/credentials'
|
||||
|
||||
// Hooks
|
||||
import useApi from 'hooks/useApi'
|
||||
|
||||
// utils
|
||||
import useNotifier from 'utils/useNotifier'
|
||||
|
||||
// const
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
const AddEditCredentialDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
// ==============================|| Snackbar ||============================== //
|
||||
|
||||
useNotifier()
|
||||
|
||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
||||
|
||||
const getSpecificCredentialApi = useApi(credentialsApi.getSpecificCredential)
|
||||
const getSpecificComponentCredentialApi = useApi(credentialsApi.getSpecificComponentCredential)
|
||||
|
||||
const [credential, setCredential] = useState({})
|
||||
const [name, setName] = useState('')
|
||||
const [credentialData, setCredentialData] = useState({})
|
||||
const [componentCredential, setComponentCredential] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
if (getSpecificCredentialApi.data) {
|
||||
setCredential(getSpecificCredentialApi.data)
|
||||
if (getSpecificCredentialApi.data.name) {
|
||||
setName(getSpecificCredentialApi.data.name)
|
||||
}
|
||||
if (getSpecificCredentialApi.data.plainDataObj) {
|
||||
setCredentialData(getSpecificCredentialApi.data.plainDataObj)
|
||||
}
|
||||
getSpecificComponentCredentialApi.request(getSpecificCredentialApi.data.credentialName)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getSpecificCredentialApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (getSpecificComponentCredentialApi.data) {
|
||||
setComponentCredential(getSpecificComponentCredentialApi.data)
|
||||
}
|
||||
}, [getSpecificComponentCredentialApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.type === 'EDIT' && dialogProps.data) {
|
||||
// When credential dialog is opened from Credentials dashboard
|
||||
getSpecificCredentialApi.request(dialogProps.data.id)
|
||||
} else if (dialogProps.type === 'EDIT' && dialogProps.credentialId) {
|
||||
// When credential dialog is opened from node in canvas
|
||||
getSpecificCredentialApi.request(dialogProps.credentialId)
|
||||
} else if (dialogProps.type === 'ADD' && dialogProps.credentialComponent) {
|
||||
// When credential dialog is to add a new credential
|
||||
setName('')
|
||||
setCredential({})
|
||||
setCredentialData({})
|
||||
setComponentCredential(dialogProps.credentialComponent)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dialogProps])
|
||||
|
||||
const addNewCredential = async () => {
|
||||
try {
|
||||
const obj = {
|
||||
name,
|
||||
credentialName: componentCredential.name,
|
||||
plainDataObj: credentialData
|
||||
}
|
||||
const createResp = await credentialsApi.createCredential(obj)
|
||||
if (createResp.data) {
|
||||
enqueueSnackbar({
|
||||
message: 'New Credential added',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'success',
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
onConfirm(createResp.data.id)
|
||||
}
|
||||
} catch (error) {
|
||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||
enqueueSnackbar({
|
||||
message: `Failed to add new Credential: ${errorData}`,
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'error',
|
||||
persist: true,
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
const saveCredential = async () => {
|
||||
try {
|
||||
const saveResp = await credentialsApi.updateCredential(credential.id, {
|
||||
name,
|
||||
credentialName: componentCredential.name,
|
||||
plainDataObj: credentialData
|
||||
})
|
||||
if (saveResp.data) {
|
||||
enqueueSnackbar({
|
||||
message: 'Credential saved',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'success',
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
onConfirm(saveResp.data.id)
|
||||
}
|
||||
} catch (error) {
|
||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||
enqueueSnackbar({
|
||||
message: `Failed to save Credential: ${errorData}`,
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'error',
|
||||
persist: true,
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
fullWidth
|
||||
maxWidth='sm'
|
||||
open={show}
|
||||
onClose={onCancel}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||
{componentCredential && componentCredential.label && (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 7,
|
||||
borderRadius: '50%',
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt={componentCredential.name}
|
||||
src={`${baseURL}/api/v1/components-credentials-icon/${componentCredential.name}`}
|
||||
/>
|
||||
</div>
|
||||
{componentCredential.label}
|
||||
</div>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
{componentCredential && componentCredential.description && (
|
||||
<Box sx={{ pl: 2, pr: 2 }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
borderRadius: 10,
|
||||
background: 'rgb(254,252,191)',
|
||||
padding: 10,
|
||||
marginTop: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'rgb(116,66,16)' }}>{parser(componentCredential.description)}</span>
|
||||
</div>
|
||||
</Box>
|
||||
)}
|
||||
{componentCredential && componentCredential.label && (
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Stack sx={{ position: 'relative' }} direction='row'>
|
||||
<Typography variant='overline'>
|
||||
Credential Name
|
||||
<span style={{ color: 'red' }}> *</span>
|
||||
</Typography>
|
||||
</Stack>
|
||||
<OutlinedInput
|
||||
id='credName'
|
||||
type='string'
|
||||
fullWidth
|
||||
placeholder={componentCredential.label}
|
||||
value={name}
|
||||
name='name'
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{componentCredential &&
|
||||
componentCredential.inputs &&
|
||||
componentCredential.inputs.map((inputParam, index) => (
|
||||
<CredentialInputHandler key={index} inputParam={inputParam} data={credentialData} />
|
||||
))}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<StyledButton
|
||||
disabled={!name}
|
||||
variant='contained'
|
||||
onClick={() => (dialogProps.type === 'ADD' ? addNewCredential() : saveCredential())}
|
||||
>
|
||||
{dialogProps.confirmButtonName}
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
<ConfirmDialog />
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
AddEditCredentialDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onConfirm: PropTypes.func
|
||||
}
|
||||
|
||||
export default AddEditCredentialDialog
|
||||
@@ -0,0 +1,137 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { Box, Typography, IconButton } from '@mui/material'
|
||||
import { IconArrowsMaximize, IconAlertTriangle } from '@tabler/icons'
|
||||
|
||||
// project import
|
||||
import { Dropdown } from 'ui-component/dropdown/Dropdown'
|
||||
import { Input } from 'ui-component/input/Input'
|
||||
import { SwitchInput } from 'ui-component/switch/Switch'
|
||||
import { JsonEditorInput } from 'ui-component/json/JsonEditor'
|
||||
import { TooltipWithParser } from 'ui-component/tooltip/TooltipWithParser'
|
||||
|
||||
// ===========================|| NodeInputHandler ||=========================== //
|
||||
|
||||
const CredentialInputHandler = ({ inputParam, data, disabled = false }) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const ref = useRef(null)
|
||||
|
||||
const [showExpandDialog, setShowExpandDialog] = useState(false)
|
||||
const [expandDialogProps, setExpandDialogProps] = useState({})
|
||||
|
||||
const onExpandDialogClicked = (value, inputParam) => {
|
||||
const dialogProp = {
|
||||
value,
|
||||
inputParam,
|
||||
disabled,
|
||||
confirmButtonName: 'Save',
|
||||
cancelButtonName: 'Cancel'
|
||||
}
|
||||
setExpandDialogProps(dialogProp)
|
||||
setShowExpandDialog(true)
|
||||
}
|
||||
|
||||
const onExpandDialogSave = (newValue, inputParamName) => {
|
||||
setShowExpandDialog(false)
|
||||
data[inputParamName] = newValue
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
{inputParam && (
|
||||
<>
|
||||
<Box sx={{ p: 2 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography>
|
||||
{inputParam.label}
|
||||
{!inputParam.optional && <span style={{ color: 'red' }}> *</span>}
|
||||
{inputParam.description && <TooltipWithParser style={{ marginLeft: 10 }} title={inputParam.description} />}
|
||||
</Typography>
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
{inputParam.type === 'string' && inputParam.rows && (
|
||||
<IconButton
|
||||
size='small'
|
||||
sx={{
|
||||
height: 25,
|
||||
width: 25
|
||||
}}
|
||||
title='Expand'
|
||||
color='primary'
|
||||
onClick={() => onExpandDialogClicked(data[inputParam.name] ?? inputParam.default ?? '', inputParam)}
|
||||
>
|
||||
<IconArrowsMaximize />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
{inputParam.warning && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
borderRadius: 10,
|
||||
background: 'rgb(254,252,191)',
|
||||
padding: 10,
|
||||
marginTop: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<IconAlertTriangle size={36} color='orange' />
|
||||
<span style={{ color: 'rgb(116,66,16)', marginLeft: 10 }}>{inputParam.warning}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{inputParam.type === 'boolean' && (
|
||||
<SwitchInput
|
||||
disabled={disabled}
|
||||
onChange={(newValue) => (data[inputParam.name] = newValue)}
|
||||
value={data[inputParam.name] ?? inputParam.default ?? false}
|
||||
/>
|
||||
)}
|
||||
{(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && (
|
||||
<Input
|
||||
key={data[inputParam.name]}
|
||||
disabled={disabled}
|
||||
inputParam={inputParam}
|
||||
onChange={(newValue) => (data[inputParam.name] = newValue)}
|
||||
value={data[inputParam.name] ?? inputParam.default ?? ''}
|
||||
showDialog={showExpandDialog}
|
||||
dialogProps={expandDialogProps}
|
||||
onDialogCancel={() => setShowExpandDialog(false)}
|
||||
onDialogConfirm={(newValue, inputParamName) => onExpandDialogSave(newValue, inputParamName)}
|
||||
/>
|
||||
)}
|
||||
{inputParam.type === 'json' && (
|
||||
<JsonEditorInput
|
||||
disabled={disabled}
|
||||
onChange={(newValue) => (data[inputParam.name] = newValue)}
|
||||
value={data[inputParam.name] ?? inputParam.default ?? ''}
|
||||
isDarkMode={customization.isDarkMode}
|
||||
/>
|
||||
)}
|
||||
{inputParam.type === 'options' && (
|
||||
<Dropdown
|
||||
disabled={disabled}
|
||||
name={inputParam.name}
|
||||
options={inputParam.options}
|
||||
onSelect={(newValue) => (data[inputParam.name] = newValue)}
|
||||
value={data[inputParam.name] ?? inputParam.default ?? 'choose an option'}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
CredentialInputHandler.propTypes = {
|
||||
inputAnchor: PropTypes.object,
|
||||
inputParam: PropTypes.object,
|
||||
data: PropTypes.object,
|
||||
disabled: PropTypes.bool
|
||||
}
|
||||
|
||||
export default CredentialInputHandler
|
||||
@@ -0,0 +1,172 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Box,
|
||||
OutlinedInput,
|
||||
InputAdornment
|
||||
} from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import { IconSearch, IconX } from '@tabler/icons'
|
||||
|
||||
// const
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
const CredentialListDialog = ({ show, dialogProps, onCancel, onCredentialSelected }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const theme = useTheme()
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [componentsCredentials, setComponentsCredentials] = useState([])
|
||||
|
||||
const filterSearch = (value) => {
|
||||
setSearchValue(value)
|
||||
setTimeout(() => {
|
||||
if (value) {
|
||||
const searchData = dialogProps.componentsCredentials.filter((crd) => crd.name.toLowerCase().includes(value.toLowerCase()))
|
||||
setComponentsCredentials(searchData)
|
||||
} else if (value === '') {
|
||||
setComponentsCredentials(dialogProps.componentsCredentials)
|
||||
}
|
||||
// scrollTop()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (show && dialogProps.componentsCredentials) {
|
||||
setComponentsCredentials(dialogProps.componentsCredentials)
|
||||
}
|
||||
}, [show, dialogProps])
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
fullWidth
|
||||
maxWidth='xs'
|
||||
open={show}
|
||||
onClose={onCancel}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||
{dialogProps.title}
|
||||
<Box sx={{ p: 2 }}>
|
||||
<OutlinedInput
|
||||
sx={{ width: '100%', pr: 2, pl: 2, my: 2 }}
|
||||
id='input-search-credential'
|
||||
value={searchValue}
|
||||
onChange={(e) => filterSearch(e.target.value)}
|
||||
placeholder='Search credential'
|
||||
startAdornment={
|
||||
<InputAdornment position='start'>
|
||||
<IconSearch stroke={1.5} size='1rem' color={theme.palette.grey[500]} />
|
||||
</InputAdornment>
|
||||
}
|
||||
endAdornment={
|
||||
<InputAdornment
|
||||
position='end'
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
color: theme.palette.grey[500],
|
||||
'&:hover': {
|
||||
color: theme.palette.grey[900]
|
||||
}
|
||||
}}
|
||||
title='Clear Search'
|
||||
>
|
||||
<IconX
|
||||
stroke={1.5}
|
||||
size='1rem'
|
||||
onClick={() => filterSearch('')}
|
||||
style={{
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
/>
|
||||
</InputAdornment>
|
||||
}
|
||||
aria-describedby='search-helper-text'
|
||||
inputProps={{
|
||||
'aria-label': 'weight'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<List
|
||||
sx={{
|
||||
width: '100%',
|
||||
py: 0,
|
||||
borderRadius: '10px',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
maxWidth: 370
|
||||
},
|
||||
'& .MuiListItemSecondaryAction-root': {
|
||||
top: 22
|
||||
},
|
||||
'& .MuiDivider-root': {
|
||||
my: 0
|
||||
},
|
||||
'& .list-container': {
|
||||
pl: 7
|
||||
}
|
||||
}}
|
||||
>
|
||||
{[...componentsCredentials].map((componentCredential) => (
|
||||
<div key={componentCredential.name}>
|
||||
<ListItemButton
|
||||
onClick={() => onCredentialSelected(componentCredential)}
|
||||
sx={{ p: 0, borderRadius: `${customization.borderRadius}px` }}
|
||||
>
|
||||
<ListItem alignItems='center'>
|
||||
<ListItemAvatar>
|
||||
<div
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 7,
|
||||
borderRadius: '50%',
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt={componentCredential.name}
|
||||
src={`${baseURL}/api/v1/components-credentials-icon/${componentCredential.name}`}
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText sx={{ ml: 1 }} primary={componentCredential.label} />
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
</div>
|
||||
))}
|
||||
</List>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
CredentialListDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onCredentialSelected: PropTypes.func
|
||||
}
|
||||
|
||||
export default CredentialListDialog
|
||||
@@ -0,0 +1,274 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions'
|
||||
import moment from 'moment'
|
||||
|
||||
// material-ui
|
||||
import { Button, Box, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton } from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
// project imports
|
||||
import MainCard from 'ui-component/cards/MainCard'
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import CredentialListDialog from './CredentialListDialog'
|
||||
import ConfirmDialog from 'ui-component/dialog/ConfirmDialog'
|
||||
import AddEditCredentialDialog from './AddEditCredentialDialog'
|
||||
|
||||
// API
|
||||
import credentialsApi from 'api/credentials'
|
||||
|
||||
// Hooks
|
||||
import useApi from 'hooks/useApi'
|
||||
import useConfirm from 'hooks/useConfirm'
|
||||
|
||||
// utils
|
||||
import useNotifier from 'utils/useNotifier'
|
||||
|
||||
// Icons
|
||||
import { IconTrash, IconEdit, IconX, IconPlus } from '@tabler/icons'
|
||||
import CredentialEmptySVG from 'assets/images/credential_empty.svg'
|
||||
|
||||
// const
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
// ==============================|| Credentials ||============================== //
|
||||
|
||||
const Credentials = () => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
useNotifier()
|
||||
|
||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
||||
|
||||
const [showCredentialListDialog, setShowCredentialListDialog] = useState(false)
|
||||
const [credentialListDialogProps, setCredentialListDialogProps] = useState({})
|
||||
const [showSpecificCredentialDialog, setShowSpecificCredentialDialog] = useState(false)
|
||||
const [specificCredentialDialogProps, setSpecificCredentialDialogProps] = useState({})
|
||||
const [credentials, setCredentials] = useState([])
|
||||
const [componentsCredentials, setComponentsCredentials] = useState([])
|
||||
|
||||
const { confirm } = useConfirm()
|
||||
|
||||
const getAllCredentialsApi = useApi(credentialsApi.getAllCredentials)
|
||||
const getAllComponentsCredentialsApi = useApi(credentialsApi.getAllComponentsCredentials)
|
||||
|
||||
const listCredential = () => {
|
||||
const dialogProp = {
|
||||
title: 'Add New Credential',
|
||||
componentsCredentials
|
||||
}
|
||||
setCredentialListDialogProps(dialogProp)
|
||||
setShowCredentialListDialog(true)
|
||||
}
|
||||
|
||||
const addNew = (credentialComponent) => {
|
||||
const dialogProp = {
|
||||
type: 'ADD',
|
||||
cancelButtonName: 'Cancel',
|
||||
confirmButtonName: 'Add',
|
||||
credentialComponent
|
||||
}
|
||||
setSpecificCredentialDialogProps(dialogProp)
|
||||
setShowSpecificCredentialDialog(true)
|
||||
}
|
||||
|
||||
const edit = (credential) => {
|
||||
const dialogProp = {
|
||||
type: 'EDIT',
|
||||
cancelButtonName: 'Cancel',
|
||||
confirmButtonName: 'Save',
|
||||
data: credential
|
||||
}
|
||||
setSpecificCredentialDialogProps(dialogProp)
|
||||
setShowSpecificCredentialDialog(true)
|
||||
}
|
||||
|
||||
const deleteCredential = async (credential) => {
|
||||
const confirmPayload = {
|
||||
title: `Delete`,
|
||||
description: `Delete credential ${credential.name}?`,
|
||||
confirmButtonName: 'Delete',
|
||||
cancelButtonName: 'Cancel'
|
||||
}
|
||||
const isConfirmed = await confirm(confirmPayload)
|
||||
|
||||
if (isConfirmed) {
|
||||
try {
|
||||
const deleteResp = await credentialsApi.deleteCredential(credential.id)
|
||||
if (deleteResp.data) {
|
||||
enqueueSnackbar({
|
||||
message: 'Credential deleted',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'success',
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
onConfirm()
|
||||
}
|
||||
} catch (error) {
|
||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||
enqueueSnackbar({
|
||||
message: `Failed to delete Credential: ${errorData}`,
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'error',
|
||||
persist: true,
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onCredentialSelected = (credentialComponent) => {
|
||||
setShowCredentialListDialog(false)
|
||||
addNew(credentialComponent)
|
||||
}
|
||||
|
||||
const onConfirm = () => {
|
||||
setShowCredentialListDialog(false)
|
||||
setShowSpecificCredentialDialog(false)
|
||||
getAllCredentialsApi.request()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getAllCredentialsApi.request()
|
||||
getAllComponentsCredentialsApi.request()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (getAllCredentialsApi.data) {
|
||||
setCredentials(getAllCredentialsApi.data)
|
||||
}
|
||||
}, [getAllCredentialsApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (getAllComponentsCredentialsApi.data) {
|
||||
setComponentsCredentials(getAllComponentsCredentialsApi.data)
|
||||
}
|
||||
}, [getAllComponentsCredentialsApi.data])
|
||||
|
||||
return (
|
||||
<>
|
||||
<MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>
|
||||
<Stack flexDirection='row'>
|
||||
<h1>Credentials </h1>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
|
||||
<StyledButton
|
||||
variant='contained'
|
||||
sx={{ color: 'white', mr: 1, height: 37 }}
|
||||
onClick={listCredential}
|
||||
startIcon={<IconPlus />}
|
||||
>
|
||||
Add Credential
|
||||
</StyledButton>
|
||||
</Stack>
|
||||
{credentials.length <= 0 && (
|
||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
||||
<Box sx={{ p: 2, height: 'auto' }}>
|
||||
<img
|
||||
style={{ objectFit: 'cover', height: '30vh', width: 'auto' }}
|
||||
src={CredentialEmptySVG}
|
||||
alt='CredentialEmptySVG'
|
||||
/>
|
||||
</Box>
|
||||
<div>No Credentials Yet</div>
|
||||
</Stack>
|
||||
)}
|
||||
{credentials.length > 0 && (
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 650 }} aria-label='simple table'>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Last Updated</TableCell>
|
||||
<TableCell>Created</TableCell>
|
||||
<TableCell> </TableCell>
|
||||
<TableCell> </TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{credentials.map((credential, index) => (
|
||||
<TableRow key={index} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
|
||||
<TableCell component='th' scope='row'>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: '50%',
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt={credential.credentialName}
|
||||
src={`${baseURL}/api/v1/components-credentials-icon/${credential.credentialName}`}
|
||||
/>
|
||||
</div>
|
||||
{credential.name}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{moment(credential.updatedDate).format('DD-MMM-YY')}</TableCell>
|
||||
<TableCell>{moment(credential.createdDate).format('DD-MMM-YY')}</TableCell>
|
||||
<TableCell>
|
||||
<IconButton title='Edit' color='primary' onClick={() => edit(credential)}>
|
||||
<IconEdit />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton title='Delete' color='error' onClick={() => deleteCredential(credential)}>
|
||||
<IconTrash />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
</MainCard>
|
||||
<CredentialListDialog
|
||||
show={showCredentialListDialog}
|
||||
dialogProps={credentialListDialogProps}
|
||||
onCancel={() => setShowCredentialListDialog(false)}
|
||||
onCredentialSelected={onCredentialSelected}
|
||||
></CredentialListDialog>
|
||||
<AddEditCredentialDialog
|
||||
show={showSpecificCredentialDialog}
|
||||
dialogProps={specificCredentialDialogProps}
|
||||
onCancel={() => setShowSpecificCredentialDialog(false)}
|
||||
onConfirm={onConfirm}
|
||||
></AddEditCredentialDialog>
|
||||
<ConfirmDialog />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Credentials
|
||||
Reference in New Issue
Block a user