Workflow template GitHub PR -> Confluence Doc#237
Conversation
…t selectors, and include a new example workflow.
…e to support workflow agents, and enhance color utilities for new node types
…and space selectors, enhancing configuration handling
…documentation generation
WalkthroughThis PR introduces Jira, Confluence, and Slack integrations into the workflow editor with new trigger and action nodes, alongside a Confluence Documentation Agent. It extends agent and integration services to include workflow-related metadata, adds integration selector components, and updates the node registry and rendering pipeline to support the new node types. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (21)
app/(main)/workflows/components/editor/hooks/useWorkflowEditor.ts (1)
963-963: LGTM! Good defensive coding for title sanitization.The title trimming and fallback to "Untitled Workflow" ensures that empty or whitespace-only titles are handled gracefully when persisting workflows. Since this payload is used by both create and update operations, the sanitization applies consistently across both paths.
Optional: Consider sanitizing in
onTitleChangeas well.The
onTitleChangehandler (line 770) currently updates the title directly without sanitization. While deferring sanitization to the save boundary is valid, applying.trim()inonTitleChangecould provide more immediate feedback to users and keep the local state cleaner.If desired, apply this diff to add consistent sanitization:
const onTitleChange = useCallback( (newTitle: string) => { - const updatedWorkflow = { ...localWorkflow, title: newTitle }; + const updatedWorkflow = { ...localWorkflow, title: newTitle.trim() }; updateLocalWorkflow(updatedWorkflow, true); }, [localWorkflow, updateLocalWorkflow] );app/(main)/workflows/components/editor/nodes/agents/agent.tsx (2)
50-56: Consider simplifying the effect dependency array.Since
availableAgentsis derived fromallAgentsvia filtering, you could simplify the dependency array to just[allAgents.length]. The current implementation is correct but slightly redundant.Apply this diff to simplify:
- }, [availableAgents.length, allAgents.length]); + }, [allAgents.length]);
316-316: Consider renaming for consistency.For consistency with
AgentConfigComponent(line 37), consider renamingagentstoallAgentshere as well. This makes it clearer that you're working with the complete agent list.Apply this diff:
- const { agents } = useAgentData(); + const { agents: allAgents } = useAgentData();Then update line 326:
- const resolvedAgent = agentId ? agents.find((a) => a.id === agentId) : null; + const resolvedAgent = agentId ? allAgents.find((a) => a.id === agentId) : null;app/(main)/workflows/page.tsx (1)
301-309: Use aSetforagentTypesfor more efficient lookup.Per static analysis, using
Set.has()is more performant thanArray.includes()for membership checks.- const agentTypes = [ - "custom_agent", - "action_agent", - "system_workflow_agent_confluence", - ]; - const agentNodes = Object.values(workflow.graph.nodes).filter((node) => - agentTypes.includes(node.type) - ); + const agentTypes = new Set([ + "custom_agent", + "action_agent", + "system_workflow_agent_confluence", + ]); + const agentNodes = Object.values(workflow.graph.nodes).filter((node) => + agentTypes.has(node.type) + );app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx (3)
21-24: Mark props interface as read-only.Using
Readonlyfor props improves type safety by preventing accidental mutation.-interface ConfluenceIntegrationSelectorProps { - selectedIntegrationId: string | null; - onIntegrationChange: (integrationId: string, cloudId: string) => void; -} +interface ConfluenceIntegrationSelectorProps { + readonly selectedIntegrationId: string | null; + readonly onIntegrationChange: (integrationId: string, cloudId: string) => void; +}
78-87: Associate label with control for accessibility.The label on line 81 is not associated with any form control. While this is an error state display without an interactive control, consider using a different semantic element.
if (error) { return ( <div className="space-y-2"> - <label className="text-sm font-medium">Confluence Workspace</label> + <span className="text-sm font-medium">Confluence Workspace</span> <div className="flex items-center gap-2 p-3 border rounded-md bg-destructive/10"> <span className="text-sm text-destructive">{error}</span> </div> </div> ); }
89-91: Associate label with control for accessibility.Similar to the error state, this label at line 91 should be associated with the popover trigger button using
htmlForand matchingid, or use a different semantic element since this is a custom combobox pattern.<div className="space-y-2"> - <label className="text-sm font-medium">Confluence Workspace</label> + <span className="text-sm font-medium" id="confluence-workspace-label">Confluence Workspace</span> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> ... <Button variant="outline" role="combobox" aria-expanded={open} + aria-labelledby="confluence-workspace-label" className="w-full justify-between" >app/(main)/workflows/components/editor/nodes/node.tsx (1)
39-48: Improve fallback UI for unknown action types.The fallback
<div>Unknown action type</div>is minimal and could confuse users. Consider adding styling consistent with the node UI or logging a warning for debugging.case "action": // Handle different action types switch (data.type) { case "action_confluence_create_page": return <ConfluenceCreatePageNode data={data} />; case "action_slack_send_message": return <SlackSendMessageNode data={data} />; default: - return <div>Unknown action type</div>; + return ( + <div className="p-4 text-center text-muted-foreground"> + <span className="text-sm">Unknown action type: {data.type}</span> + </div> + ); }app/(main)/workflows/components/editor/nodes/node-registry.ts (1)
88-102: Missing re-exports for new node metadata.
confluenceCreatePageNodeMetadataandslackSendMessageNodeMetadataare imported and added toavailableNodesbut not included in the explicit re-export block. Add them for consistency if direct imports are needed elsewhere.export { prOpenedTriggerNodeMetadata, prClosedTriggerNodeMetadata, prReopenedTriggerNodeMetadata, prMergedTriggerNodeMetadata, issueAddedTriggerNodeMetadata, linearTriggerNodeMetadata, jiraTriggerNodeMetadata, issueCreatedTriggerNodeMetadata, webhookTriggerNodeMetadata, agentNodeMetadata, actionAgentNodeMetadata, ifConditionNodeMetadata, confluenceAgentNodeMetadata, + confluenceCreatePageNodeMetadata, + slackSendMessageNodeMetadata, };app/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsx (3)
43-46: Remove debug console.log statements before merging.These logging statements are useful during development but should be removed for production code to avoid cluttering browser console output.
- console.log( - "JiraIntegrationSelector render with selectedIntegrationId:", - selectedIntegrationId - );And remove the logs inside
handleIntegrationChange:- console.log( - "JiraIntegrationSelector handleIntegrationChange called with:", - integrationId - ); // Find the integration to get the unique identifier const integration = integrations.find((int) => int.id === integrationId); const uniqueIdentifier = integration?.uniqueIdentifier; - console.log("JiraIntegrationSelector found integration:", integration); - console.log( - "JiraIntegrationSelector uniqueIdentifier:", - uniqueIdentifier - );Also applies to: 59-70
154-193: Extract nested ternary into helper function or separate components for readability.The nested ternary operator makes this code difficult to read and maintain. SonarCloud also flags this pattern.
Consider extracting the button rendering logic:
const renderTriggerButton = () => { if (loading) { return ( <Button className="flex gap-3 items-center font-semibold justify-start w-full" variant="outline" disabled > <Loader2 className="h-4 w-4 text-[#7A7A7A] animate-spin" strokeWidth={1.5} /> Loading Jira integrations... </Button> ); } const buttonStyle = readOnly ? { pointerEvents: "none" as const } : {}; const icon = <Ticket className="h-4 w-4 text-[#7A7A7A]" strokeWidth={1.5} />; if (!hasSelectedIntegration) { return ( <Button className="flex gap-3 items-center font-semibold justify-start w-full" variant="outline" style={buttonStyle}> {icon} Select Jira Integration </Button> ); } return ( <Button className="flex gap-3 items-center font-semibold justify-start w-full" variant="outline" style={buttonStyle}> {icon} <span className="truncate text-ellipsis whitespace-nowrap"> {selectedIntegration?.name || `Integration ${selectedIntegrationId}`} </span> </Button> ); };
38-42: UseFCtype annotation for consistency with coding guidelines.Per the coding guidelines, functional components should use TypeScript FC types.
+import { FC } from "react"; + -export const JiraIntegrationSelector = ({ +export const JiraIntegrationSelector: FC<JiraIntegrationSelectorProps> = ({ selectedIntegrationId, onIntegrationChange, readOnly = false, -}: JiraIntegrationSelectorProps) => { +}) => {app/(main)/workflows/components/editor/nodes/actions/slack-send-message.tsx (1)
7-11: Define a proper interface for the Slack config instead of usingany.Using
anyfor the config type loses TypeScript's type safety benefits and makes it harder to catch errors at compile time.+interface SlackConfig { + connection_id?: string; + channel_id?: string; + message?: string; +} + interface SlackConfigProps { - config: any; - onConfigChange: (config: any) => void; + config: SlackConfig; + onConfigChange: (config: SlackConfig) => void; readOnly?: boolean; }app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsx (1)
53-86: Consider using IntegrationService for API calls to maintain consistency.Other components in this PR (e.g.,
JiraIntegrationSelector,JiraProjectSelector) useIntegrationServicefor API calls. Using axios directly here breaks the abstraction and makes it harder to apply consistent error handling, caching, or authentication logic.Consider adding a
getConfluenceSpacesmethod toIntegrationServiceand using it here, similar to howgetJiraProjectsis used inJiraProjectSelector.app/(main)/workflows/components/editor/nodes/actions/confluence-create-page.tsx (1)
9-13: Define a proper interface for Confluence config.Same type safety concern as with
SlackConfigProps- usinganyloses compile-time type checking.+interface ConfluenceConfig { + connection_id?: string; + cloud_id?: string; + space_key?: string | null; + space_name?: string | null; + title?: string; + content?: string; + parent_page_id?: string; +} + interface ConfluenceConfigProps { - config: any; - onConfigChange: (config: any) => void; + config: ConfluenceConfig; + onConfigChange: (config: ConfluenceConfig) => void; readOnly?: boolean; }app/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsx (2)
168-173: Simplify the pluralization logic.SonarCloud flags the negated condition. The expression can be simplified for readability.
<p className="text-xs text-gray-500"> - {projects.length} project{projects.length !== 1 ? "s" : ""}{" "} - found + {projects.length} {projects.length === 1 ? "project" : "projects"} found </p>
29-34: UseFCtype annotation for consistency with coding guidelines.Per the coding guidelines, functional components should use TypeScript FC types.
+import { FC } from "react"; + -export const JiraProjectSelector = ({ +export const JiraProjectSelector: FC<JiraProjectSelectorProps> = ({ integrationId, selectedProjectKey, onProjectChange, readOnly = false, -}: JiraProjectSelectorProps) => { +}) => {app/(main)/workflows/components/editor/nodes/agents/confluence-agent.tsx (1)
9-14: Remove unusedworkflowprop or use it.The
workflowprop is declared in the interface but never used in the component. Either remove it from the props interface or utilize it if needed.interface ConfluenceAgentConfigProps { config: any; onConfigChange: (config: any) => void; readOnly?: boolean; - workflow?: any; }services/IntegrationService.ts (1)
272-297: Consider adding a typed response interface forgetJiraProjects.The method follows the established pattern in this service, but returns
Promise<any>. For better type safety and developer experience, consider defining an interface for the Jira projects response.interface JiraProjectsResponse { projects: Array<{ id: string; key: string; name: string; // other fields as needed }>; total: number; // pagination fields }Then update the return type:
- ): Promise<any> { + ): Promise<JiraProjectsResponse> {app/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsx (2)
35-83: Webhook check triggers on every config change due to broad dependencies.The
useEffectdepends onconfig.integrationId,config.integration_id, andconfig.cloud_id. If any of these change, the webhook check re-runs. This is correct for integration changes, butcloud_idis set alongsideintegrationIdin the same update, potentially causing unnecessary duplicate API calls.Consider using a single canonical identifier:
}, [config.integrationId, config.integration_id, config.cloud_id]); + // Note: cloud_id dependency may cause duplicate calls when integration changes + // Consider using only integrationId once config shape is normalizedAlternatively, normalize the config shape upstream to use a single
integrationIdfield.
117-173: Extract webhook status rendering into a helper component or variable.The nested ternary operations for webhook status (checking → not configured → configured) reduce readability. Extract into a helper function or separate component.
const WebhookStatus = () => { if (checkingWebhook) { return ( <div className="p-3 border border-blue-200 bg-blue-50 rounded-md flex items-start gap-2"> <Info className="h-4 w-4 text-blue-600 mt-0.5" /> <p className="text-sm text-blue-800">Checking webhook configuration...</p> </div> ); } if (hasWebhook === false) { return (/* ... not configured UI ... */); } if (hasWebhook === true) { return (/* ... configured UI ... */); } return null; };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
app/(main)/workflows/components/editor/contexts/AgentDataContext.tsx(2 hunks)app/(main)/workflows/components/editor/hooks/useWorkflowEditor.ts(1 hunks)app/(main)/workflows/components/editor/nodes/actions/confluence-create-page.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/actions/slack-send-message.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/agents/agent.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/agents/confluence-agent.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/color_utils.ts(3 hunks)app/(main)/workflows/components/editor/nodes/index.ts(1 hunks)app/(main)/workflows/components/editor/nodes/node-registry.ts(4 hunks)app/(main)/workflows/components/editor/nodes/node.tsx(4 hunks)app/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsx(1 hunks)app/(main)/workflows/components/editor/nodes/triggers/trigger.tsx(2 hunks)app/(main)/workflows/page.tsx(4 hunks)services/AgentService.ts(1 hunks)services/IntegrationService.ts(4 hunks)services/WorkflowService.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use PascalCase for component names and camelCase for utility names
Order imports: React/Next imports first, then components, then utilities
Use Next.js error boundaries, optional chaining, and nullish coalescing for error handling
Files:
app/(main)/workflows/components/editor/hooks/useWorkflowEditor.tsapp/(main)/workflows/components/editor/contexts/AgentDataContext.tsxservices/IntegrationService.tsapp/(main)/workflows/components/editor/nodes/triggers/trigger.tsxservices/WorkflowService.tsapp/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsxapp/(main)/workflows/components/editor/nodes/index.tsapp/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsxapp/(main)/workflows/components/editor/nodes/agents/agent.tsxapp/(main)/workflows/components/editor/nodes/color_utils.tsapp/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsxservices/AgentService.tsapp/(main)/workflows/components/editor/nodes/actions/slack-send-message.tsxapp/(main)/workflows/components/editor/nodes/agents/confluence-agent.tsxapp/(main)/workflows/components/editor/nodes/node.tsxapp/(main)/workflows/page.tsxapp/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsxapp/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsxapp/(main)/workflows/components/editor/nodes/actions/confluence-create-page.tsxapp/(main)/workflows/components/editor/nodes/node-registry.ts
**/*.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.tsx: Use Tailwind CSS for styling and thecnutility for conditional classes
Use functional components with TypeScript FC types
Prefer destructuring props at the component level
Use the "use client" directive for client components
Files:
app/(main)/workflows/components/editor/contexts/AgentDataContext.tsxapp/(main)/workflows/components/editor/nodes/triggers/trigger.tsxapp/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsxapp/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsxapp/(main)/workflows/components/editor/nodes/agents/agent.tsxapp/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsxapp/(main)/workflows/components/editor/nodes/actions/slack-send-message.tsxapp/(main)/workflows/components/editor/nodes/agents/confluence-agent.tsxapp/(main)/workflows/components/editor/nodes/node.tsxapp/(main)/workflows/page.tsxapp/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsxapp/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsxapp/(main)/workflows/components/editor/nodes/actions/confluence-create-page.tsx
🧬 Code graph analysis (9)
app/(main)/workflows/components/editor/contexts/AgentDataContext.tsx (1)
services/AgentService.ts (1)
AgentService(6-243)
services/IntegrationService.ts (1)
lib/utils.ts (1)
parseApiError(195-297)
app/(main)/workflows/components/editor/nodes/triggers/trigger.tsx (2)
app/(main)/workflows/components/editor/nodes/index.ts (1)
JiraTriggerNode(4-4)app/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsx (1)
JiraTriggerNode(229-267)
app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx (2)
services/IntegrationService.ts (2)
ConnectedIntegration(47-73)IntegrationService(75-356)lib/utils.ts (1)
cn(4-6)
app/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsx (8)
app/(main)/workflows/components/editor/nodes/index.ts (3)
JiraTriggerConfigComponent(24-24)jiraTriggerNodeMetadata(13-13)JiraTriggerNode(4-4)services/IntegrationService.ts (1)
IntegrationService(75-356)app/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsx (1)
JiraIntegrationSelector(38-290)app/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsx (1)
JiraProjectSelector(29-187)app/(main)/workflows/components/editor/nodes/node-registry.ts (1)
jiraTriggerNodeMetadata(95-95)services/WorkflowService.ts (1)
WorkflowNode(103-110)app/(main)/workflows/components/editor/nodes/color_utils.ts (1)
getNodeColors(17-74)app/(main)/workflows/components/editor/handles.tsx (1)
SourceHandle(7-36)
app/(main)/workflows/components/editor/nodes/agents/agent.tsx (1)
app/(main)/workflows/components/editor/contexts/AgentDataContext.tsx (1)
useAgentData(89-95)
app/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsx (2)
services/IntegrationService.ts (1)
IntegrationService(75-356)components/ui/select.tsx (5)
Select(150-150)SelectTrigger(153-153)SelectValue(152-152)SelectContent(154-154)SelectItem(156-156)
app/(main)/workflows/page.tsx (1)
services/AgentService.ts (1)
AgentService(6-243)
app/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsx (1)
services/IntegrationService.ts (2)
ConnectedIntegration(47-73)IntegrationService(75-356)
🪛 GitHub Check: SonarCloud Code Analysis
app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx
[warning] 107-112: Use , , , , , , or instead of the "combobox" role to ensure accessibility across all devices. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY3JPn89tq2rOV51&open=AZrfeY3JPn89tq2rOV51&pullRequest=237 [warning] 81-81: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY3JPn89tq2rOV5z&open=AZrfeY3JPn89tq2rOV5z&pullRequest=237 [warning] 26-29: Mark the props of the component as read-only. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY3JPn89tq2rOV5y&open=AZrfeY3JPn89tq2rOV5y&pullRequest=237 app/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsx [warning] 7-7: Remove this unused import of 'Input'. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY7HPn89tq2rOV6J&open=AZrfeY7HPn89tq2rOV6J&pullRequest=237 [warning] 12-12: 'lucide-react' imported multiple times. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY7HPn89tq2rOV6K&open=AZrfeY7HPn89tq2rOV6K&pullRequest=237 [warning] 142-142: Ambiguous spacing before next element code See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY7HPn89tq2rOV6M&open=AZrfeY7HPn89tq2rOV6M&pullRequest=237 [warning] 127-171: Extract this nested ternary operation into an independent statement. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY7HPn89tq2rOV6L&open=AZrfeY7HPn89tq2rOV6L&pullRequest=237 [warning] 164-171: Extract this nested ternary operation into an independent statement. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY7HPn89tq2rOV6O&open=AZrfeY7HPn89tq2rOV6O&pullRequest=237 [warning] 3-3: 'lucide-react' imported multiple times. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY7HPn89tq2rOV6I&open=AZrfeY7HPn89tq2rOV6I&pullRequest=237 app/(main)/workflows/components/editor/nodes/color_utils.ts [warning] 55-60: This case's code block is the same as the block for the case on line 37. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY8kPn89tq2rOV6V&open=AZrfeY8kPn89tq2rOV6V&pullRequest=237 app/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsx [warning] 171-171: Unexpected negated condition. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY67Pn89tq2rOV6H&open=AZrfeY67Pn89tq2rOV6H&pullRequest=237 app/(main)/workflows/components/editor/nodes/actions/slack-send-message.tsx [warning] 21-21: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6QPn89tq2rOV59&open=AZrfeY6QPn89tq2rOV59&pullRequest=237 [warning] 53-53: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6QPn89tq2rOV5_&open=AZrfeY6QPn89tq2rOV5_&pullRequest=237 [warning] 37-37: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6QPn89tq2rOV5-&open=AZrfeY6QPn89tq2rOV5-&pullRequest=237 app/(main)/workflows/components/editor/nodes/agents/confluence-agent.tsx [warning] 13-13: 'workflow' PropType is defined but prop is never used See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY76Pn89tq2rOV6Q&open=AZrfeY76Pn89tq2rOV6Q&pullRequest=237 [warning] 24-24: The empty object is useless. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY76Pn89tq2rOV6R&open=AZrfeY76Pn89tq2rOV6R&pullRequest=237 [warning] 83-83: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY76Pn89tq2rOV6S&open=AZrfeY76Pn89tq2rOV6S&pullRequest=237 app/(main)/workflows/page.tsx [warning] 302-302: agentTypes should be a Set, and use agentTypes.has() to check existence or non-existence. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY_9Pn89tq2rOV6X&open=AZrfeY_9Pn89tq2rOV6X&pullRequest=237 app/(main)/workflows/components/editor/nodes/triggers/jira/JiraIntegrationSelector.tsx [warning] 166-193: Extract this nested ternary operation into an independent statement. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6pPn89tq2rOV6G&open=AZrfeY6pPn89tq2rOV6G&pullRequest=237 app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsx [warning] 34-38: Mark the props of the component as read-only. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6CPn89tq2rOV52&open=AZrfeY6CPn89tq2rOV52&pullRequest=237 [warning] 146-151: Use , , , , , , or instead of the "combobox" role to ensure accessibility across all devices. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6CPn89tq2rOV58&open=AZrfeY6CPn89tq2rOV58&pullRequest=237 [warning] 106-106: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6CPn89tq2rOV54&open=AZrfeY6CPn89tq2rOV54&pullRequest=237 [warning] 93-93: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6CPn89tq2rOV53&open=AZrfeY6CPn89tq2rOV53&pullRequest=237 [warning] 131-131: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6CPn89tq2rOV56&open=AZrfeY6CPn89tq2rOV56&pullRequest=237 [warning] 143-143: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6CPn89tq2rOV57&open=AZrfeY6CPn89tq2rOV57&pullRequest=237 app/(main)/workflows/components/editor/nodes/actions/confluence-create-page.tsx [warning] 73-73: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6cPn89tq2rOV6B&open=AZrfeY6cPn89tq2rOV6B&pullRequest=237 [warning] 57-57: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6cPn89tq2rOV6A&open=AZrfeY6cPn89tq2rOV6A&pullRequest=237 [warning] 88-88: A form label must be associated with a control. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY6cPn89tq2rOV6C&open=AZrfeY6cPn89tq2rOV6C&pullRequest=237 app/(main)/workflows/components/editor/nodes/node-registry.ts [warning] 101-101: Use export…from to re-export confluenceAgentNodeMetadata. See more on https://sonarcloud.io/project/issues?id=getmomentum_potpie-ui&issues=AZrfeY8QPn89tq2rOV6U&open=AZrfeY8QPn89tq2rOV6U&pullRequest=237 🔇 Additional comments (30) app/(main)/workflows/components/editor/nodes/agents/agent.tsx (1) 36-47: LGTM! Filtering logic is clear and well-implemented. The rename to allAgents and the inline filtering of workflow and system agents improves code clarity. The comments clearly explain the distinction between system agents and custom agents. services/WorkflowService.ts (1) 87-98: LGTM! The new NodeType values for Jira trigger, Confluence agent, and Slack/Confluence actions are correctly added and align with the PR's integration goals. services/AgentService.ts (2) 24-28: LGTM! The new includeWorkflowAgents parameter is well-designed with a default value of false, maintaining backward compatibility with existing callers. 40-45: Verify whether both list_system_agents and include_workflow_agents params are required. Both query parameters are set to the same includeWorkflowAgents value. Confirm this is intentional for API compatibility, or if one is redundant. app/(main)/workflows/page.tsx (4) 166-215: LGTM! The new workflow template is well-structured with proper node definitions, positions, and adjacency list. The template demonstrates the new Confluence documentation agent and Slack notification action working together. 247-252: LGTM! The agent fetch correctly includes workflow agents by passing true for the new includeWorkflowAgents parameter. The debug log will help trace agent loading issues. 311-318: LGTM! The systemAgentNames mapping provides clean human-readable names for system workflow agents, with appropriate fallback logic for custom agents. 570-574: LGTM! Minor formatting change to the status indicator className. The conditional logic for the animated pulse on active workflows is correct. app/(main)/workflows/components/editor/contexts/AgentDataContext.tsx (2) 15-16: LGTM! The new optional fields is_workflow_agent and requires_repo_context extend the Agent interface appropriately to support workflow-specific agent metadata. 45-54: LGTM! The fetch logic correctly retrieves all agents including workflow agents and maps the required fields. The mapping is explicit and type-safe. app/(main)/workflows/components/editor/nodes/triggers/trigger.tsx (2) 26-26: LGTM! Import follows the established pattern for trigger components. 69-70: LGTM! The Jira trigger case follows the same pattern as LinearTriggerNode, passing the data prop directly without explicit type assertion. This is consistent with the existing codebase. app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx (1) 152-179: LGTM! The command list rendering with selection state, check icon visibility, and the search value composition for filtering are well implemented. app/(main)/workflows/components/editor/nodes/color_utils.ts (2) 37-42: Duplicate color palettes for Jira and Confluence are acceptable. Static analysis flags identical code blocks, but this is intentional since both are Atlassian products sharing brand colors. Keeping them separate maintains clarity and allows independent customization if branding needs diverge. Also applies to: 55-60 61-66: LGTM! Slack purple palette is correctly defined with appropriate contrast. app/(main)/workflows/components/editor/nodes/node.tsx (2) 4-10: LGTM! New node component imports are properly organized and follow the existing import pattern. 22-23: LGTM! New trigger and agent type cases are correctly integrated into the existing switch patterns. Also applies to: 32-33 app/(main)/workflows/components/editor/nodes/index.ts (2) 13-18: LGTM! New metadata and config component exports are properly organized and follow the existing export pattern. Also applies to: 24-27 4-9: Missing ConfluenceAgentNode export. ConfluenceAgentNode is imported in node.tsx but not exported from this index file. If this component should be accessible from other parts of the codebase, add the export. app/(main)/workflows/components/editor/nodes/node-registry.ts (2) 9-21: LGTM! New node types are properly added to the NodeType union, extending the type system correctly for Jira triggers, Confluence actions, Slack messaging, and Confluence agent. 76-84: LGTM! New node metadata entries are correctly added to the availableNodes registry array. app/(main)/workflows/components/editor/nodes/actions/slack-send-message.tsx (1) 81-113: LGTM! The SlackSendMessageNode component is well-structured with proper color theming, conditional rendering for channel display, and appropriate handles for the workflow diagram. app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsx (2) 72-76: Auto-selecting a space may trigger unexpected side effects. Automatically calling onSpaceChange when only one space exists might cause unexpected behavior if the parent component isn't prepared for an immediate callback during initial render. This could lead to state update warnings or re-render loops. Verify that the parent component (ConfluenceConfigComponent) handles this callback correctly during initial mount, and consider whether this auto-select should be opt-in via a prop. 141-201: LGTM on the popover implementation. The space selector UI with search functionality, selected state display, and proper conditional rendering is well-implemented. app/(main)/workflows/components/editor/nodes/actions/confluence-create-page.tsx (1) 117-173: LGTM on the node rendering component. The ConfluenceCreatePageNode component effectively displays integration status, space, and title with proper conditional rendering and visual indicators. app/(main)/workflows/components/editor/nodes/triggers/jira/JiraProjectSelector.tsx (2) 40-70: LGTM on the data fetching logic. The useEffect properly handles the loading state, error handling, and dependency array. Using IntegrationService.getJiraProjects maintains consistency with the service layer pattern. 121-128: Good use of sentinel value for "All projects" option. The __none__ sentinel pattern is a clean way to handle the "All projects" option in the Select component, which doesn't allow empty string values. app/(main)/workflows/components/editor/nodes/agents/confluence-agent.tsx (1) 112-172: LGTM! The ConfluenceAgentNode component is well-structured with proper conditional rendering, truncation for long text, and appropriate use of handles for the workflow editor graph. services/IntegrationService.ts (1) 32-37: LGTM on metadata extension. The addition of the webhooks array and index signature to the metadata interfaces properly supports the new Jira/Confluence integration workflows. The structure is consistent between ConnectedIntegrationResponse and ConnectedIntegration. Also applies to: 62-72 app/(main)/workflows/components/editor/nodes/triggers/jira/jira-trigger.tsx (1) 229-266: LGTM on the JiraTriggerNode component. The node component correctly handles both camelCase and snake_case property names for backward compatibility, uses appropriate styling from getNodeColors, and renders the connection handle properly.
| @@ -0,0 +1,187 @@ | |||
| import React, { useEffect, useState } from 'react'; | |||
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Missing "use client" directive.
This file uses React hooks (useState, useEffect) and browser APIs (useRouter), making it a client component. As per coding guidelines, add the directive at the top.
+"use client";
+
import React, { useEffect, useState } from 'react';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import React, { useEffect, useState } from 'react'; | |
| "use client"; | |
| import React, { useEffect, useState } from 'react'; |
🤖 Prompt for AI Agents
In
app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx
around lines 1 to 1, this file uses React hooks and browser APIs but is missing
the "use client" directive; add the line "use client" as the very first line of
the file (before any imports) so Next.js treats it as a client component, and
verify there are no server-only imports remaining.
| useEffect(() => { | ||
| loadIntegrations(); | ||
| }, []); |
There was a problem hiding this comment.
Missing dependencies in useEffect.
The useEffect that calls loadIntegrations references selectedIntegrationId and onIntegrationChange inside loadIntegrations, but these aren't in the dependency array. This could cause stale closure issues with the auto-select logic.
Consider wrapping loadIntegrations with useCallback or adding the dependencies:
+import React, { useEffect, useState, useCallback } from 'react';
+
+// Then wrap loadIntegrations:
+const loadIntegrations = useCallback(async () => {
+ // ... existing logic
+}, [selectedIntegrationId, onIntegrationChange]);
+
useEffect(() => {
loadIntegrations();
-}, []);
+}, [loadIntegrations]);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceIntegrationSelector.tsx
around lines 36 to 38, the useEffect calls loadIntegrations but omits
dependencies used inside loadIntegrations (selectedIntegrationId and
onIntegrationChange), which can lead to stale closures; fix by either wrapping
loadIntegrations in useCallback with selectedIntegrationId and
onIntegrationChange (and any other used values) in its dependency array and then
keeping useEffect dependent only on that memoized function, or by including
selectedIntegrationId and onIntegrationChange directly in the useEffect
dependency array so the effect reruns when they change.
| useEffect(() => { | ||
| if (integrationId) { | ||
| loadSpaces(); | ||
| } else { | ||
| setSpaces([]); | ||
| setError(null); | ||
| } | ||
| }, [integrationId]); |
There was a problem hiding this comment.
Add loadSpaces to useEffect dependencies or use useCallback.
The loadSpaces function is defined outside the effect but called within it, creating a dependency that React's exhaustive-deps rule would flag. This can cause stale closure issues.
+ const loadSpaces = useCallback(async () => {
+ // ... existing implementation
+ }, [integrationId, selectedSpaceKey, onSpaceChange]);
+
useEffect(() => {
if (integrationId) {
loadSpaces();
} else {
setSpaces([]);
setError(null);
}
- }, [integrationId]);
+ }, [integrationId, loadSpaces]);Alternatively, move loadSpaces inside the useEffect if it's only used there.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
app/(main)/workflows/components/editor/nodes/actions/confluence/ConfluenceSpaceSelector.tsx
around lines 44 to 51, the useEffect calls loadSpaces but doesn't include it in
the dependency array which can cause stale closures; fix by either (A) wrapping
loadSpaces in useCallback with the correct dependencies (e.g., integrationId and
any other values it uses) and then add that memoized loadSpaces to the useEffect
dependency array, or (B) move the loadSpaces function body inside the useEffect
so it uses current values and keep [integrationId] as the dependency; pick one
approach and update related imports/types if needed.
| useEffect(() => { | ||
| if ((!integrationsLoaded && integrations.length === 0) || refreshKey > 0) { | ||
| setLoading(true); | ||
|
|
||
| IntegrationService.getConnectedIntegrations() | ||
| .then((allIntegrations) => { | ||
| // Filter for Jira integrations only | ||
| const jiraIntegrations = allIntegrations.filter( | ||
| (integration) => integration.type === "jira" | ||
| ); | ||
| setIntegrations(jiraIntegrations); | ||
| setLoading(false); | ||
| setIntegrationsLoaded(true); | ||
| }) | ||
| .catch((error) => { | ||
| console.error("Failed to load Jira integrations:", error); | ||
| setLoading(false); | ||
| setIntegrationsLoaded(true); | ||
| }); | ||
| } | ||
| }, [integrationsLoaded, integrations.length, refreshKey]); | ||
|
|
||
| // Load integrations immediately if we have a selectedIntegrationId but no integrations loaded yet | ||
| useEffect(() => { | ||
| if ( | ||
| selectedIntegrationId && | ||
| !integrationsLoaded && | ||
| integrations.length === 0 | ||
| ) { | ||
| setLoading(true); | ||
|
|
||
| IntegrationService.getConnectedIntegrations() | ||
| .then((allIntegrations) => { | ||
| // Filter for Jira integrations only | ||
| const jiraIntegrations = allIntegrations.filter( | ||
| (integration) => integration.type === "jira" | ||
| ); | ||
| setIntegrations(jiraIntegrations); | ||
| setLoading(false); | ||
| setIntegrationsLoaded(true); | ||
| }) | ||
| .catch((error) => { | ||
| console.error("Failed to load Jira integrations:", error); | ||
| setLoading(false); | ||
| setIntegrationsLoaded(true); | ||
| }); | ||
| } | ||
| }, [selectedIntegrationId, integrationsLoaded, integrations.length]); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Consolidate duplicate useEffect logic.
Both useEffect hooks (lines 78-98 and 101-125) contain identical data fetching logic. This violates DRY and can lead to race conditions or redundant API calls.
Consider extracting the fetch logic and consolidating into a single effect:
+ const loadIntegrations = useCallback(() => {
+ setLoading(true);
+ IntegrationService.getConnectedIntegrations()
+ .then((allIntegrations) => {
+ const jiraIntegrations = allIntegrations.filter(
+ (integration) => integration.type === "jira"
+ );
+ setIntegrations(jiraIntegrations);
+ setLoading(false);
+ setIntegrationsLoaded(true);
+ })
+ .catch((error) => {
+ console.error("Failed to load Jira integrations:", error);
+ setLoading(false);
+ setIntegrationsLoaded(true);
+ });
+ }, []);
+
useEffect(() => {
- if ((!integrationsLoaded && integrations.length === 0) || refreshKey > 0) {
- setLoading(true);
-
- IntegrationService.getConnectedIntegrations()
- .then((allIntegrations) => {
- // Filter for Jira integrations only
- const jiraIntegrations = allIntegrations.filter(
- (integration) => integration.type === "jira"
- );
- setIntegrations(jiraIntegrations);
- setLoading(false);
- setIntegrationsLoaded(true);
- })
- .catch((error) => {
- console.error("Failed to load Jira integrations:", error);
- setLoading(false);
- setIntegrationsLoaded(true);
- });
+ if (!integrationsLoaded || refreshKey > 0) {
+ loadIntegrations();
}
- }, [integrationsLoaded, integrations.length, refreshKey]);
-
- // Load integrations immediately if we have a selectedIntegrationId but no integrations loaded yet
- useEffect(() => {
- if (
- selectedIntegrationId &&
- !integrationsLoaded &&
- integrations.length === 0
- ) {
- // ... duplicate code removed
- }
- }, [selectedIntegrationId, integrationsLoaded, integrations.length]);
+ }, [integrationsLoaded, refreshKey, loadIntegrations]);Committable suggestion skipped: line range outside the PR's diff.
| export type NodeGroup = "github" | "linear" | "sentry" | "default" | "jira" | "confluence" | "slack"; | ||
| export type NodeCategory = "trigger" | "agent" | "flow_control" | "manual_step" | "action"; |
There was a problem hiding this comment.
NodeGroup is missing "flow_control" compared to other definitions.
The NodeGroup type here differs from the definitions in color_utils.ts and node-registry.ts, which include "flow_control". This inconsistency could cause type errors when flow control nodes are used.
-export type NodeGroup = "github" | "linear" | "sentry" | "default" | "jira" | "confluence" | "slack";
+export type NodeGroup = "github" | "linear" | "sentry" | "default" | "jira" | "confluence" | "slack" | "flow_control";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export type NodeGroup = "github" | "linear" | "sentry" | "default" | "jira" | "confluence" | "slack"; | |
| export type NodeCategory = "trigger" | "agent" | "flow_control" | "manual_step" | "action"; | |
| export type NodeGroup = "github" | "linear" | "sentry" | "default" | "jira" | "confluence" | "slack" | "flow_control"; | |
| export type NodeCategory = "trigger" | "agent" | "flow_control" | "manual_step" | "action"; |
🤖 Prompt for AI Agents
In services/WorkflowService.ts around lines 100-101, the NodeGroup union is
missing the "flow_control" member which causes divergence from color_utils.ts
and node-registry.ts; update the NodeGroup type to include "flow_control" so it
matches the other definitions and avoids type errors when flow control nodes are
used.


Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.