Skip to content

feat/show scheduled flow status#6330

Open
HenryHengZJ wants to merge 1 commit intomainfrom
feature/Show-Scheduled-Flow
Open

feat/show scheduled flow status#6330
HenryHengZJ wants to merge 1 commit intomainfrom
feature/Show-Scheduled-Flow

Conversation

@HenryHengZJ
Copy link
Copy Markdown
Contributor

image

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a ScheduleStatusBadge component to visualize the scheduling status of agent flows within the ItemCard and FlowListTable views. The Agentflows view was updated to detect scheduled flows and fetch their status from the backend. Review feedback highlights a potential race condition and lack of error handling in the asynchronous status fetching logic, which could lead to inconsistent states. Additionally, suggestions were made to use optional chaining for safer property access on node data and to incorporate the loading state into the badge UI to prevent misleading 'Paused' statuses during data retrieval.

Comment on lines +182 to +199
Promise.allSettled(
scheduleConfiguredIds.map((id) => chatflowsApi.getScheduleStatus(id).then((res) => ({ id, data: res.data })))
).then((settled) => {
const next = { ...initialStatuses }
settled.forEach((r) => {
if (r.status === 'fulfilled') {
const { id, data } = r.value
next[id] = {
isScheduled: true,
enabled: data?.enabled === true,
nextRunAt: data?.record?.nextRunAt || null,
cronExpression: data?.record?.cronExpression || null,
loading: false
}
}
})
setScheduleStatuses(next)
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are two issues here:

  1. Race Condition: If getAllAgentflows.data changes while the requests are pending, the results from an older execution might overwrite the state of a newer one.
  2. Error Handling: If a request fails, the flow remains in a loading: true state indefinitely because allSettled results are only processed if fulfilled.

It is recommended to handle errors within the map and use a functional update for setScheduleStatuses to ensure state consistency.

                if (scheduleConfiguredIds.length > 0) {
                    Promise.all(
                        scheduleConfiguredIds.map((id) =>
                            chatflowsApi
                                .getScheduleStatus(id)
                                .then((res) => ({ id, data: res.data }))
                                .catch(() => ({ id, error: true }))
                        )
                    ).then((results) => {
                        setScheduleStatuses((prev) => {
                            const next = { ...prev }
                            results.forEach(({ id, data }) => {
                                if (next[id]) {
                                    next[id] = {
                                        ...next[id],
                                        enabled: data?.enabled === true,
                                        nextRunAt: data?.record?.nextRunAt || null,
                                        cronExpression: data?.record?.cronExpression || null,
                                        loading: false
                                    }
                                }
                            })
                            return next
                        })
                    })
                }

Comment on lines +156 to 168
if (node.data.name === 'stickyNote' || node.data.name === 'stickyNoteAgentflow') continue
const foundIcon = AGENTFLOW_ICONS.find((icon) => icon.name === node.data.name)
if (foundIcon) {
icons[agentflows[i].id].push(foundIcon)
} else {
const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}`
const imageSrc = `${baseURL}/api/v1/node-icon/${node.data.name}`
if (!images[agentflows[i].id].some((img) => img.imageSrc === imageSrc)) {
images[agentflows[i].id].push({
imageSrc,
label: nodes[j].data.label
label: node.data.label
})
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The code accesses node.data.name and node.data.label without optional chaining. Since node.data?.name is used on line 153, it implies that node.data could potentially be undefined. Accessing properties directly on a potentially undefined object will cause a runtime error. This ensures consistency with existing patterns in the file.

Suggested change
if (node.data.name === 'stickyNote' || node.data.name === 'stickyNoteAgentflow') continue
const foundIcon = AGENTFLOW_ICONS.find((icon) => icon.name === node.data.name)
if (foundIcon) {
icons[agentflows[i].id].push(foundIcon)
} else {
const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}`
const imageSrc = `${baseURL}/api/v1/node-icon/${node.data.name}`
if (!images[agentflows[i].id].some((img) => img.imageSrc === imageSrc)) {
images[agentflows[i].id].push({
imageSrc,
label: nodes[j].data.label
label: node.data.label
})
}
}
if (node.data?.name === 'stickyNote' || node.data?.name === 'stickyNoteAgentflow') continue
const foundIcon = AGENTFLOW_ICONS.find((icon) => icon.name === node.data?.name)
if (foundIcon) {
icons[agentflows[i].id].push(foundIcon)
} else {
const imageSrc = baseURL + "/api/v1/node-icon/" + node.data?.name
if (!images[agentflows[i].id].some((img) => img.imageSrc === imageSrc)) {
images[agentflows[i].id].push({
imageSrc,
label: node.data?.label
})
}
}
References
  1. To maintain consistency, new functionality should follow existing patterns within the same file.

Comment on lines +18 to +32
const ScheduleStatusBadge = ({ scheduleStatus, size = 'md' }) => {
const theme = useTheme()
const customization = useSelector((state) => state.customization)

if (!scheduleStatus?.isScheduled) return null

const isActive = scheduleStatus.enabled === true
const palette = customization.isDarkMode ? 'dark' : 'light'
const colors = isActive ? ACTIVE[palette] : PAUSED[palette]

const tooltipText = isActive
? scheduleStatus.nextRunAt
? `Schedule active — next run ${moment(scheduleStatus.nextRunAt).format('MMM D, YYYY h:mm A')}`
: 'Schedule active'
: 'Schedule configured but turned off'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The loading property is defined in propTypes but not used in the component logic. Currently, while the schedule status is being fetched, the badge defaults to showing "Paused" because enabled is false in the initial state. This is misleading to the user. You should use the loading state to show a loading indicator or a "Loading..." label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant