Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions packages/ui/src/ui-component/dialog/SaveChatflowDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'
import { Button, Dialog, DialogActions, DialogContent, OutlinedInput, DialogTitle } from '@mui/material'
import { StyledButton } from '@/ui-component/button/StyledButton'

const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm, isSubmitting }) => {
const portalElement = document.getElementById('portal')

const [chatflowName, setChatflowName] = useState('')
Expand All @@ -21,9 +21,10 @@ const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
open={show}
fullWidth
maxWidth='xs'
onClose={onCancel}
onClose={isSubmitting ? undefined : onCancel}
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
disableEscapeKeyDown={isSubmitting}
disableRestoreFocus // needed due to StrictMode
>
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
Expand All @@ -41,14 +42,16 @@ const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
value={chatflowName}
onChange={(e) => setChatflowName(e.target.value)}
onKeyDown={(e) => {
if (isReadyToSave && e.key === 'Enter') onConfirm(e.target.value)
if (!isSubmitting && isReadyToSave && e.key === 'Enter') onConfirm(e.target.value)
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>
<StyledButton disabled={!isReadyToSave} variant='contained' onClick={() => onConfirm(chatflowName)}>
{dialogProps.confirmButtonName}
<Button disabled={isSubmitting} onClick={onCancel}>
{dialogProps.cancelButtonName}
</Button>
<StyledButton disabled={!isReadyToSave || isSubmitting} variant='contained' onClick={() => onConfirm(chatflowName)}>
{isSubmitting ? 'Saving...' : dialogProps.confirmButtonName}
</StyledButton>
</DialogActions>
</Dialog>
Expand All @@ -61,7 +64,8 @@ SaveChatflowDialog.propTypes = {
show: PropTypes.bool,
dialogProps: PropTypes.object,
onCancel: PropTypes.func,
onConfirm: PropTypes.func
onConfirm: PropTypes.func,
isSubmitting: PropTypes.bool
}

export default SaveChatflowDialog
11 changes: 11 additions & 0 deletions packages/ui/src/views/agentflowsv2/Canvas.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const AgentflowCanvas = () => {
const createNewChatflowApi = useApi(chatflowsApi.createNewChatflow)
const updateChatflowApi = useApi(chatflowsApi.updateChatflow)
const getSpecificChatflowApi = useApi(chatflowsApi.getSpecificChatflow)
const saveInProgressRef = useRef(false)

// ==============================|| Events & Actions ||============================== //

Expand Down Expand Up @@ -217,6 +218,9 @@ const AgentflowCanvas = () => {
}

const handleSaveFlow = (chatflowName) => {
if (saveInProgressRef.current || createNewChatflowApi.loading || updateChatflowApi.loading) return
saveInProgressRef.current = true

if (reactFlowInstance) {
const nodes = reactFlowInstance.getNodes().map((node) => {
const nodeData = cloneDeep(node.data)
Expand Down Expand Up @@ -252,6 +256,8 @@ const AgentflowCanvas = () => {
}
updateChatflowApi.request(chatflow.id, updateBody)
}
} else {
saveInProgressRef.current = false
}
}

Expand Down Expand Up @@ -555,11 +561,13 @@ const AgentflowCanvas = () => {
// Create new chatflow successful
useEffect(() => {
if (createNewChatflowApi.data) {
saveInProgressRef.current = false
const chatflow = createNewChatflowApi.data
dispatch({ type: SET_CHATFLOW, chatflow })
saveChatflowSuccess()
window.history.replaceState(state, null, `/v2/agentcanvas/${chatflow.id}`)
} else if (createNewChatflowApi.error) {
saveInProgressRef.current = false
errorFailed(`Failed to save ${canvasTitle}: ${createNewChatflowApi.error.response.data.message}`)
}

Expand All @@ -569,9 +577,11 @@ const AgentflowCanvas = () => {
// Update chatflow successful
useEffect(() => {
if (updateChatflowApi.data) {
saveInProgressRef.current = false
dispatch({ type: SET_CHATFLOW, chatflow: updateChatflowApi.data })
saveChatflowSuccess()
} else if (updateChatflowApi.error) {
saveInProgressRef.current = false
errorFailed(`Failed to save ${canvasTitle}: ${updateChatflowApi.error.response.data.message}`)
}

Expand Down Expand Up @@ -711,6 +721,7 @@ const AgentflowCanvas = () => {
handleLoadFlow={handleLoadFlow}
isAgentCanvas={true}
isAgentflowV2={true}
isSaveLoading={createNewChatflowApi.loading || updateChatflowApi.loading}
/>
</Toolbar>
</AppBar>
Expand Down
13 changes: 9 additions & 4 deletions packages/ui/src/views/canvas/CanvasHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const LockedScheduleSwitch = styled(ScheduleSwitch, { shouldForwardProp: (prop)

// ==============================|| CANVAS HEADER ||============================== //

const CanvasHeader = ({ chatflow, isAgentCanvas, isAgentflowV2, handleSaveFlow, handleDeleteFlow, handleLoadFlow }) => {
const CanvasHeader = ({ chatflow, isAgentCanvas, isAgentflowV2, isSaveLoading, handleSaveFlow, handleDeleteFlow, handleLoadFlow }) => {
const theme = useTheme()
const dispatch = useDispatch()
const navigate = useNavigate()
Expand Down Expand Up @@ -319,11 +319,13 @@ const CanvasHeader = ({ chatflow, isAgentCanvas, isAgentflowV2, handleSaveFlow,
}

const onSaveChatflowClick = () => {
if (isSaveLoading) return
if (chatflow.id) handleSaveFlow(flowName)
else setFlowDialogOpen(true)
}

const onConfirmSaveName = (flowName) => {
if (isSaveLoading) return
setFlowDialogOpen(false)
setSavePermission(isAgentCanvas ? 'agentflows:update' : 'chatflows:update')
handleSaveFlow(flowName)
Expand Down Expand Up @@ -593,7 +595,7 @@ const CanvasHeader = ({ chatflow, isAgentCanvas, isAgentflowV2, handleSaveFlow,
</ButtonBase>
)}
<Available permission={savePermission}>
<ButtonBase title={`Save ${title}`} sx={{ borderRadius: '50%', mr: 2 }}>
<ButtonBase disabled={isSaveLoading} title={`Save ${title}`} sx={{ borderRadius: '50%', mr: 2 }}>
<Avatar
variant='rounded'
sx={{
Expand All @@ -605,7 +607,8 @@ const CanvasHeader = ({ chatflow, isAgentCanvas, isAgentflowV2, handleSaveFlow,
'&:hover': {
background: theme.palette.canvasHeader.saveDark,
color: theme.palette.canvasHeader.saveLight
}
},
opacity: isSaveLoading ? 0.6 : 1
}}
color='inherit'
onClick={onSaveChatflowClick}
Expand Down Expand Up @@ -653,6 +656,7 @@ const CanvasHeader = ({ chatflow, isAgentCanvas, isAgentflowV2, handleSaveFlow,
}}
onCancel={() => setFlowDialogOpen(false)}
onConfirm={onConfirmSaveName}
isSubmitting={isSaveLoading}
/>
{apiDialogOpen && <APICodeDialog show={apiDialogOpen} dialogProps={apiDialogProps} onCancel={() => setAPIDialogOpen(false)} />}
<ViewMessagesDialog
Expand Down Expand Up @@ -690,7 +694,8 @@ CanvasHeader.propTypes = {
handleDeleteFlow: PropTypes.func,
handleLoadFlow: PropTypes.func,
isAgentCanvas: PropTypes.bool,
isAgentflowV2: PropTypes.bool
isAgentflowV2: PropTypes.bool,
isSaveLoading: PropTypes.bool
}

export default CanvasHeader
15 changes: 15 additions & 0 deletions packages/ui/src/views/canvas/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const Canvas = () => {
const updateChatflowApi = useApi(chatflowsApi.updateChatflow)
const getSpecificChatflowApi = useApi(chatflowsApi.getSpecificChatflow)
const getHasChatflowChangedApi = useApi(chatflowsApi.getHasChatflowChanged)
const saveInProgressRef = useRef(false)

// ==============================|| Events & Actions ||============================== //

Expand Down Expand Up @@ -209,6 +210,10 @@ const Canvas = () => {
}

const handleSaveFlow = async (chatflowName) => {
if (saveInProgressRef.current || createNewChatflowApi.loading || updateChatflowApi.loading || getHasChatflowChangedApi.loading)
return
saveInProgressRef.current = true

if (reactFlowInstance) {
const nodes = reactFlowInstance.getNodes().map((node) => {
const nodeData = cloneDeep(node.data)
Expand Down Expand Up @@ -241,6 +246,8 @@ const Canvas = () => {
setFlowData(flowData)
getHasChatflowChangedApi.request(chatflow.id, lastUpdatedDateTime)
}
} else {
saveInProgressRef.current = false
}
}

Expand Down Expand Up @@ -429,11 +436,13 @@ const Canvas = () => {
// Create new chatflow successful
useEffect(() => {
if (createNewChatflowApi.data) {
saveInProgressRef.current = false
const chatflow = createNewChatflowApi.data
dispatch({ type: SET_CHATFLOW, chatflow })
saveChatflowSuccess()
window.history.replaceState(state, null, `/${isAgentCanvas ? 'agentcanvas' : 'canvas'}/${chatflow.id}`)
} else if (createNewChatflowApi.error) {
saveInProgressRef.current = false
errorFailed(`Failed to retrieve ${canvasTitle}: ${createNewChatflowApi.error.response.data.message}`)
}

Expand All @@ -443,10 +452,12 @@ const Canvas = () => {
// Update chatflow successful
useEffect(() => {
if (updateChatflowApi.data) {
saveInProgressRef.current = false
dispatch({ type: SET_CHATFLOW, chatflow: updateChatflowApi.data })
setLasUpdatedDateTime(updateChatflowApi.data.updatedDate)
saveChatflowSuccess()
} else if (updateChatflowApi.error) {
saveInProgressRef.current = false
errorFailed(`Failed to retrieve ${canvasTitle}: ${updateChatflowApi.error.response.data.message}`)
}

Expand All @@ -466,6 +477,7 @@ const Canvas = () => {
const isConfirmed = await confirm(confirmPayload)

if (!isConfirmed) {
saveInProgressRef.current = false
return
}
}
Expand All @@ -478,6 +490,8 @@ const Canvas = () => {

if (getHasChatflowChangedApi.data) {
checkIfHasChanged()
} else if (getHasChatflowChangedApi.error) {
saveInProgressRef.current = false
}

// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -577,6 +591,7 @@ const Canvas = () => {
handleDeleteFlow={handleDeleteFlow}
handleLoadFlow={handleLoadFlow}
isAgentCanvas={isAgentCanvas}
isSaveLoading={createNewChatflowApi.loading || updateChatflowApi.loading || getHasChatflowChangedApi.loading}
/>
</Toolbar>
</AppBar>
Expand Down