- update marketplaces

- add version to nodes and credentials
- hover over node actions
This commit is contained in:
Henry
2023-07-28 15:56:40 +01:00
parent 976e74f13f
commit 05dd23b01d
181 changed files with 884 additions and 148 deletions
+3 -1
View File
@@ -1,7 +1,9 @@
import client from './client'
const getConfig = (id) => client.get(`/flow-config/${id}`)
const getNodeConfig = (body) => client.post(`/node-config`, body)
export default {
getConfig
getConfig,
getNodeConfig
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+2
View File
@@ -13,6 +13,8 @@ export const REMOVE_DIRTY = '@canvas/REMOVE_DIRTY'
export const SET_CHATFLOW = '@canvas/SET_CHATFLOW'
export const SHOW_CANVAS_DIALOG = '@canvas/SHOW_CANVAS_DIALOG'
export const HIDE_CANVAS_DIALOG = '@canvas/HIDE_CANVAS_DIALOG'
export const SET_COMPONENT_NODES = '@canvas/SET_COMPONENT_NODES'
export const SET_COMPONENT_CREDENTIALS = '@canvas/SET_COMPONENT_CREDENTIALS'
// action - notifier reducer
export const ENQUEUE_SNACKBAR = 'ENQUEUE_SNACKBAR'
@@ -1,7 +1,9 @@
import { createContext, useState } from 'react'
import { useDispatch } from 'react-redux'
import PropTypes from 'prop-types'
import { getUniqueNodeId } from 'utils/genericHelper'
import { cloneDeep } from 'lodash'
import { SET_DIRTY } from 'store/actions'
const initialValue = {
reactFlowInstance: null,
@@ -14,17 +16,20 @@ const initialValue = {
export const flowContext = createContext(initialValue)
export const ReactFlowContext = ({ children }) => {
const dispatch = useDispatch()
const [reactFlowInstance, setReactFlowInstance] = useState(null)
const deleteNode = (nodeid) => {
deleteConnectedInput(nodeid, 'node')
reactFlowInstance.setNodes(reactFlowInstance.getNodes().filter((n) => n.id !== nodeid))
reactFlowInstance.setEdges(reactFlowInstance.getEdges().filter((ns) => ns.source !== nodeid && ns.target !== nodeid))
dispatch({ type: SET_DIRTY })
}
const deleteEdge = (edgeid) => {
deleteConnectedInput(edgeid, 'edge')
reactFlowInstance.setEdges(reactFlowInstance.getEdges().filter((edge) => edge.id !== edgeid))
dispatch({ type: SET_DIRTY })
}
const deleteConnectedInput = (id, type) => {
@@ -103,6 +108,7 @@ export const ReactFlowContext = ({ children }) => {
}
reactFlowInstance.setNodes([...nodes, duplicatedNode])
dispatch({ type: SET_DIRTY })
}
}
@@ -4,7 +4,9 @@ import * as actionTypes from '../actions'
export const initialState = {
isDirty: false,
chatflow: null,
canvasDialogShow: false
canvasDialogShow: false,
componentNodes: [],
componentCredentials: []
}
// ==============================|| CANVAS REDUCER ||============================== //
@@ -36,6 +38,16 @@ const canvasReducer = (state = initialState, action) => {
...state,
canvasDialogShow: false
}
case actionTypes.SET_COMPONENT_NODES:
return {
...state,
componentNodes: action.componentNodes
}
case actionTypes.SET_COMPONENT_CREDENTIALS:
return {
...state,
componentCredentials: action.componentCredentials
}
default:
return state
}
+4
View File
@@ -90,6 +90,10 @@ export default function themePalette(theme) {
},
codeEditor: {
main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.primaryLight
},
nodeToolTip: {
background: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.paper,
color: theme.customization.isDarkMode ? theme.colors?.paper : 'rgba(0, 0, 0, 0.87)'
}
}
}
@@ -27,6 +27,7 @@ const AdditionalParamsDialog = ({ show, dialogProps, onCancel }) => {
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
@@ -36,6 +36,7 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
@@ -15,6 +15,7 @@ const FormatPromptValuesDialog = ({ show, dialogProps, onChange, onCancel }) =>
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
@@ -0,0 +1,141 @@
import { createPortal } from 'react-dom'
import { useDispatch } from 'react-redux'
import { useEffect } from 'react'
import PropTypes from 'prop-types'
// Material
import { Dialog, DialogContent, DialogTitle } from '@mui/material'
import { TableViewOnly } from 'ui-component/table/Table'
// Store
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
import { baseURL } from 'store/constant'
// API
import configApi from 'api/config'
import useApi from 'hooks/useApi'
const NodeInfoDialog = ({ show, dialogProps, onCancel }) => {
const portalElement = document.getElementById('portal')
const dispatch = useDispatch()
const getNodeConfigApi = useApi(configApi.getNodeConfig)
useEffect(() => {
if (dialogProps.data) {
getNodeConfigApi.request(dialogProps.data)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
<Dialog
onClose={onCancel}
open={show}
fullWidth
maxWidth='md'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
{dialogProps.data && dialogProps.data.name && dialogProps.data.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={dialogProps.data.name}
src={`${baseURL}/api/v1/node-icon/${dialogProps.data.name}`}
/>
</div>
<div style={{ display: 'flex', flexDirection: 'column', marginLeft: 10 }}>
{dialogProps.data.label}
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div
style={{
display: 'flex',
flexDirection: 'row',
width: 'max-content',
borderRadius: 15,
background: 'rgb(254,252,191)',
padding: 5,
paddingLeft: 10,
paddingRight: 10,
marginTop: 5,
marginBottom: 5
}}
>
<span style={{ color: 'rgb(116,66,16)', fontSize: '0.825rem' }}>{dialogProps.data.id}</span>
</div>
{dialogProps.data.version && (
<div
style={{
display: 'flex',
flexDirection: 'row',
width: 'max-content',
borderRadius: 15,
background: '#e9edc9',
padding: 5,
paddingLeft: 10,
paddingRight: 10,
marginTop: 5,
marginLeft: 10,
marginBottom: 5
}}
>
<span style={{ color: '#606c38', fontSize: '0.825rem' }}>version {dialogProps.data.version}</span>
</div>
)}
</div>
</div>
</div>
)}
</DialogTitle>
<DialogContent>
{dialogProps.data?.description && (
<div
style={{
padding: 10,
marginBottom: 10
}}
>
<span>{dialogProps.data.description}</span>
</div>
)}
{getNodeConfigApi.data && getNodeConfigApi.data.length > 0 && (
<TableViewOnly rows={getNodeConfigApi.data} columns={Object.keys(getNodeConfigApi.data[0])} />
)}
</DialogContent>
</Dialog>
) : null
return createPortal(component, portalElement)
}
NodeInfoDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func
}
export default NodeInfoDialog
+1 -1
View File
@@ -37,7 +37,7 @@ export const Input = ({ inputParam, value, onChange, disabled = false, showDialo
onChange(e.target.value)
}}
inputProps={{
step: inputParam.step ?? 0.1,
step: inputParam.step ?? 1,
style: {
height: inputParam.rows ? '90px' : 'inherit'
}
+1
View File
@@ -268,6 +268,7 @@ export const generateExportFlowData = (flowData) => {
const newNodeData = {
id: node.data.id,
label: node.data.label,
version: node.data.version,
name: node.data.name,
type: node.data.type,
baseClasses: node.data.baseClasses,
+8 -3
View File
@@ -1,5 +1,5 @@
import { useState, useRef, useEffect } from 'react'
import { useSelector } from 'react-redux'
import { useSelector, useDispatch } from 'react-redux'
import PropTypes from 'prop-types'
// material-ui
@@ -38,12 +38,14 @@ import { IconPlus, IconSearch, IconMinus, IconX } from '@tabler/icons'
// const
import { baseURL } from 'store/constant'
import { SET_COMPONENT_NODES } from 'store/actions'
// ==============================|| ADD NODES||============================== //
const AddNodes = ({ nodesData, node }) => {
const theme = useTheme()
const customization = useSelector((state) => state.customization)
const dispatch = useDispatch()
const [searchValue, setSearchValue] = useState('')
const [nodes, setNodes] = useState({})
@@ -131,8 +133,11 @@ const AddNodes = ({ nodesData, node }) => {
}, [node])
useEffect(() => {
if (nodesData) groupByCategory(nodesData)
}, [nodesData])
if (nodesData) {
groupByCategory(nodesData)
dispatch({ type: SET_COMPONENT_NODES, componentNodes: nodesData })
}
}, [nodesData, dispatch])
return (
<>
+181 -102
View File
@@ -1,19 +1,22 @@
import PropTypes from 'prop-types'
import { useContext, useState } from 'react'
import { useContext, useState, useEffect } from 'react'
import { useSelector } from 'react-redux'
// material-ui
import { styled, useTheme } from '@mui/material/styles'
import { IconButton, Box, Typography, Divider, Button } from '@mui/material'
import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'
// project imports
import MainCard from 'ui-component/cards/MainCard'
import NodeInputHandler from './NodeInputHandler'
import NodeOutputHandler from './NodeOutputHandler'
import AdditionalParamsDialog from 'ui-component/dialog/AdditionalParamsDialog'
import NodeInfoDialog from 'ui-component/dialog/NodeInfoDialog'
// const
import { baseURL } from 'store/constant'
import { IconTrash, IconCopy } from '@tabler/icons'
import { IconTrash, IconCopy, IconInfoCircle, IconAlertTriangle } from '@tabler/icons'
import { flowContext } from 'store/context/ReactFlowContext'
const CardWrapper = styled(MainCard)(({ theme }) => ({
@@ -30,14 +33,39 @@ const CardWrapper = styled(MainCard)(({ theme }) => ({
}
}))
const LightTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)(({ theme }) => ({
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: theme.palette.nodeToolTip.background,
color: theme.palette.nodeToolTip.color,
boxShadow: theme.shadows[1]
}
}))
// ===========================|| CANVAS NODE ||=========================== //
const CanvasNode = ({ data }) => {
const theme = useTheme()
const canvas = useSelector((state) => state.canvas)
const { deleteNode, duplicateNode } = useContext(flowContext)
const [showDialog, setShowDialog] = useState(false)
const [dialogProps, setDialogProps] = useState({})
const [showInfoDialog, setShowInfoDialog] = useState(false)
const [infoDialogProps, setInfoDialogProps] = useState({})
const [warningMessage, setWarningMessage] = useState('')
const [open, setOpen] = useState(false)
const handleClose = () => {
setOpen(false)
}
const handleOpen = () => {
setOpen(true)
}
const nodeOutdatedMessage = (oldVersion, newVersion) => `Node version ${oldVersion} outdated\nUpdate to latest version ${newVersion}`
const nodeVersionEmptyMessage = (newVersion) => `Node outdated\nUpdate to latest version ${newVersion}`
const onDialogClicked = () => {
const dialogProps = {
@@ -50,6 +78,17 @@ const CanvasNode = ({ data }) => {
setShowDialog(true)
}
useEffect(() => {
const componentNode = canvas.componentNodes.find((nd) => nd.name === data.name)
if (componentNode) {
if (!data.version) {
setWarningMessage(nodeVersionEmptyMessage(componentNode.version))
} else {
if (componentNode.version > data.version) setWarningMessage(nodeOutdatedMessage(data.version, componentNode.version))
}
}
}, [canvas.componentNodes, data.name, data.version])
return (
<>
<CardWrapper
@@ -60,118 +99,158 @@ const CanvasNode = ({ data }) => {
}}
border={false}
>
<Box>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<Box style={{ width: 50, marginRight: 10, padding: 5 }}>
<div
style={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
borderRadius: '50%',
backgroundColor: 'white',
cursor: 'grab'
}}
>
<img
style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}
src={`${baseURL}/api/v1/node-icon/${data.name}`}
alt='Notification'
/>
</div>
</Box>
<Box>
<Typography
sx={{
fontSize: '1rem',
fontWeight: 500
}}
>
{data.label}
</Typography>
</Box>
<div style={{ flexGrow: 1 }}></div>
<IconButton
title='Duplicate'
onClick={() => {
duplicateNode(data.id)
}}
sx={{ height: 35, width: 35, '&:hover': { color: theme?.palette.primary.main } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconCopy />
</IconButton>
<IconButton
title='Delete'
onClick={() => {
deleteNode(data.id)
}}
sx={{ height: 35, width: 35, mr: 1, '&:hover': { color: 'red' } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconTrash />
</IconButton>
</div>
{(data.inputAnchors.length > 0 || data.inputParams.length > 0) && (
<>
<Divider />
<Box sx={{ background: theme.palette.asyncSelect.main, p: 1 }}>
<Typography
sx={{
fontWeight: 500,
textAlign: 'center'
}}
>
Inputs
</Typography>
</Box>
<Divider />
</>
)}
{data.inputAnchors.map((inputAnchor, index) => (
<NodeInputHandler key={index} inputAnchor={inputAnchor} data={data} />
))}
{data.inputParams.map((inputParam, index) => (
<NodeInputHandler key={index} inputParam={inputParam} data={data} />
))}
{data.inputParams.find((param) => param.additionalParams) && (
<LightTooltip
open={!canvas.canvasDialogShow && open}
onClose={handleClose}
onOpen={handleOpen}
disableFocusListener={true}
title={
<div
style={{
textAlign: 'center',
marginTop:
data.inputParams.filter((param) => param.additionalParams).length ===
data.inputParams.length + data.inputAnchors.length
? 20
: 0
background: 'transparent',
display: 'flex',
flexDirection: 'column'
}}
>
<Button sx={{ borderRadius: 25, width: '90%', mb: 2 }} variant='outlined' onClick={onDialogClicked}>
Additional Parameters
</Button>
<IconButton
title='Duplicate'
onClick={() => {
duplicateNode(data.id)
}}
sx={{ height: '35px', width: '35px', '&:hover': { color: theme?.palette.primary.main } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconCopy />
</IconButton>
<IconButton
title='Delete'
onClick={() => {
deleteNode(data.id)
}}
sx={{ height: '35px', width: '35px', '&:hover': { color: 'red' } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconTrash />
</IconButton>
<IconButton
title='Info'
onClick={() => {
setInfoDialogProps({ data })
setShowInfoDialog(true)
}}
sx={{ height: '35px', width: '35px', '&:hover': { color: theme?.palette.secondary.main } }}
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
>
<IconInfoCircle />
</IconButton>
</div>
)}
<Divider />
<Box sx={{ background: theme.palette.asyncSelect.main, p: 1 }}>
<Typography
sx={{
fontWeight: 500,
textAlign: 'center'
}}
>
Output
</Typography>
</Box>
<Divider />
}
placement='right-start'
>
<Box>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<Box style={{ width: 50, marginRight: 10, padding: 5 }}>
<div
style={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
borderRadius: '50%',
backgroundColor: 'white',
cursor: 'grab'
}}
>
<img
style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }}
src={`${baseURL}/api/v1/node-icon/${data.name}`}
alt='Notification'
/>
</div>
</Box>
<Box>
<Typography
sx={{
fontSize: '1rem',
fontWeight: 500,
mr: 2
}}
>
{data.label}
</Typography>
</Box>
{warningMessage && (
<>
<div style={{ flexGrow: 1 }}></div>
<Tooltip title={<span style={{ whiteSpace: 'pre-line' }}>{warningMessage}</span>} placement='top'>
<IconButton sx={{ height: 35, width: 35 }}>
<IconAlertTriangle size={35} color='orange' />
</IconButton>
</Tooltip>
</>
)}
</div>
{(data.inputAnchors.length > 0 || data.inputParams.length > 0) && (
<>
<Divider />
<Box sx={{ background: theme.palette.asyncSelect.main, p: 1 }}>
<Typography
sx={{
fontWeight: 500,
textAlign: 'center'
}}
>
Inputs
</Typography>
</Box>
<Divider />
</>
)}
{data.inputAnchors.map((inputAnchor, index) => (
<NodeInputHandler key={index} inputAnchor={inputAnchor} data={data} />
))}
{data.inputParams.map((inputParam, index) => (
<NodeInputHandler key={index} inputParam={inputParam} data={data} />
))}
{data.inputParams.find((param) => param.additionalParams) && (
<div
style={{
textAlign: 'center',
marginTop:
data.inputParams.filter((param) => param.additionalParams).length ===
data.inputParams.length + data.inputAnchors.length
? 20
: 0
}}
>
<Button sx={{ borderRadius: 25, width: '90%', mb: 2 }} variant='outlined' onClick={onDialogClicked}>
Additional Parameters
</Button>
</div>
)}
<Divider />
<Box sx={{ background: theme.palette.asyncSelect.main, p: 1 }}>
<Typography
sx={{
fontWeight: 500,
textAlign: 'center'
}}
>
Output
</Typography>
</Box>
<Divider />
{data.outputAnchors.map((outputAnchor, index) => (
<NodeOutputHandler key={index} outputAnchor={outputAnchor} data={data} />
))}
</Box>
{data.outputAnchors.map((outputAnchor, index) => (
<NodeOutputHandler key={index} outputAnchor={outputAnchor} data={data} />
))}
</Box>
</LightTooltip>
</CardWrapper>
<AdditionalParamsDialog
show={showDialog}
dialogProps={dialogProps}
onCancel={() => setShowDialog(false)}
></AdditionalParamsDialog>
<NodeInfoDialog show={showInfoDialog} dialogProps={infoDialogProps} onCancel={() => setShowInfoDialog(false)}></NodeInfoDialog>
</>
)
}
@@ -28,6 +28,9 @@ import useApi from 'hooks/useApi'
// Const
import { baseURL, maxScroll } from 'store/constant'
import robotPNG from 'assets/images/robot.png'
import userPNG from 'assets/images/account.png'
export const ChatMessage = ({ open, chatflowid, isDialog }) => {
const theme = useTheme()
const customization = useSelector((state) => state.customization)
@@ -281,21 +284,9 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
>
{/* Display the correct icon depending on the message type */}
{message.type === 'apiMessage' ? (
<img
src='https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png'
alt='AI'
width='30'
height='30'
className='boticon'
/>
<img src={robotPNG} alt='AI' width='30' height='30' className='boticon' />
) : (
<img
src='https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/usericon.png'
alt='Me'
width='30'
height='30'
className='usericon'
/>
<img src={userPNG} alt='Me' width='30' height='30' className='usericon' />
)}
<div style={{ display: 'flex', flexDirection: 'column', width: '100%' }}>
<div className='markdownanswer'>
@@ -27,6 +27,7 @@ import useNotifier from 'utils/useNotifier'
// const
import { baseURL } from 'store/constant'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
const AddEditCredentialDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
const portalElement = document.getElementById('portal')
@@ -87,6 +88,12 @@ const AddEditCredentialDialog = ({ show, dialogProps, onCancel, onConfirm }) =>
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const addNewCredential = async () => {
try {
const obj = {
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import { useSelector } from 'react-redux'
import { useSelector, useDispatch } from 'react-redux'
import PropTypes from 'prop-types'
import {
List,
@@ -20,11 +20,12 @@ import { IconSearch, IconX } from '@tabler/icons'
// const
import { baseURL } from 'store/constant'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
const CredentialListDialog = ({ show, dialogProps, onCancel, onCredentialSelected }) => {
const portalElement = document.getElementById('portal')
const customization = useSelector((state) => state.customization)
const dispatch = useDispatch()
const theme = useTheme()
const [searchValue, setSearchValue] = useState('')
const [componentsCredentials, setComponentsCredentials] = useState([])
@@ -43,10 +44,16 @@ const CredentialListDialog = ({ show, dialogProps, onCancel, onCredentialSelecte
}
useEffect(() => {
if (show && dialogProps.componentsCredentials) {
if (dialogProps.componentsCredentials) {
setComponentsCredentials(dialogProps.componentsCredentials)
}
}, [show, dialogProps])
}, [dialogProps])
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
const component = show ? (
<Dialog
+3 -1
View File
@@ -30,6 +30,7 @@ import CredentialEmptySVG from 'assets/images/credential_empty.svg'
// const
import { baseURL } from 'store/constant'
import { SET_COMPONENT_CREDENTIALS } from 'store/actions'
// ==============================|| Credentials ||============================== //
@@ -159,8 +160,9 @@ const Credentials = () => {
useEffect(() => {
if (getAllComponentsCredentialsApi.data) {
setComponentsCredentials(getAllComponentsCredentialsApi.data)
dispatch({ type: SET_COMPONENT_CREDENTIALS, componentsCredentials: getAllComponentsCredentialsApi.data })
}
}, [getAllComponentsCredentialsApi.data])
}, [getAllComponentsCredentialsApi.data, dispatch])
return (
<>
@@ -29,6 +29,7 @@ import useApi from 'hooks/useApi'
// utils
import useNotifier from 'utils/useNotifier'
import { generateRandomGradient } from 'utils/genericHelper'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
const exampleAPIFunc = `/*
* You can use any libraries imported in Flowise
@@ -155,6 +156,12 @@ const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm }) =
}
}
useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
else dispatch({ type: HIDE_CANVAS_DIALOG })
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
}, [show, dispatch])
useEffect(() => {
if (getSpecificToolApi.data) {
setToolId(getSpecificToolApi.data.id)