feat(frontend): Add trigger agent banner for webhook-based flows (#11480)

This PR addresses the need for better user awareness when building
trigger-based agents. When a user adds webhook/trigger nodes to their
flow, a prominent banner now appears at the bottom of the builder
informing them they're creating a "Trigger Agent" and providing a direct
link to monitor its activity in the Agent Library.

### Changes 🏗️

- **Added TriggerAgentBanner component**: New banner that displays at
the bottom of the builder when the flow contains webhook/trigger nodes
- **Implemented webhook node detection**: Added `hasWebhookNodes` method
to nodeStore that checks if any nodes are of type WEBHOOK or
WEBHOOK_MANUAL
- **Conditional banner display**: Builder now shows TriggerAgentBanner
instead of BuilderActions when webhook nodes are present
- **Dynamic library link**: Banner includes a link to the specific agent
in the library (if found) or defaults to the general library page
- **Integrated with existing flow context**: Uses the current flowID to
fetch the corresponding library agent for proper linking

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Add a webhook node to a flow and verify the banner appears
  - [x] Remove all webhook nodes and verify the banner disappears
  - [x] Test with both WEBHOOK and WEBHOOK_MANUAL node types
- [x] Click the library link and verify it navigates to the correct
agent page
  - [x] Test library link fallback when agent is not found in library
- [x] Verify banner styling and positioning at bottom center of builder
This commit is contained in:
Abhimanyu Yadav
2025-12-01 11:13:04 +05:30
committed by GitHub
parent cd6a8cbd47
commit 321ab8a48a
3 changed files with 60 additions and 1 deletions

View File

@@ -16,6 +16,7 @@ import { useCopyPaste } from "./useCopyPaste";
import { FloatingReviewsPanel } from "@/components/organisms/FloatingReviewsPanel/FloatingReviewsPanel";
import { parseAsString, useQueryStates } from "nuqs";
import { CustomControls } from "./components/CustomControl";
import { TriggerAgentBanner } from "./components/TriggerAgentBanner";
export const Flow = () => {
const [{ flowExecutionID }] = useQueryStates({
@@ -27,6 +28,9 @@ export const Flow = () => {
const onNodesChange = useNodeStore(
useShallow((state) => state.onNodesChange),
);
const hasWebhookNodes = useNodeStore(
useShallow((state) => state.hasWebhookNodes()),
);
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
const edgeTypes = useMemo(() => ({ custom: CustomEdge }), []);
const { edges, onConnect, onEdgesChange } = useCustomEdge();
@@ -76,7 +80,7 @@ export const Flow = () => {
<Background />
<CustomControls setIsLocked={setIsLocked} isLocked={isLocked} />
<NewControlPanel />
<BuilderActions />
{hasWebhookNodes ? <TriggerAgentBanner /> : <BuilderActions />}
{<GraphLoadingBox flowContentLoading={isFlowContentLoading} />}
{isGraphRunning && <RunningBackground />}
</ReactFlow>

View File

@@ -0,0 +1,49 @@
import {
Alert,
AlertDescription,
AlertTitle,
} from "@/components/molecules/Alert/Alert";
import Link from "next/link";
import { useGetV2GetLibraryAgentByGraphId } from "@/app/api/__generated__/endpoints/library/library";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { useQueryStates, parseAsString } from "nuqs";
export const TriggerAgentBanner = () => {
const [{ flowID }] = useQueryStates({
flowID: parseAsString,
});
const { data: libraryAgent } = useGetV2GetLibraryAgentByGraphId(
flowID ?? "",
{},
{
query: {
select: (x) => {
return x.data as LibraryAgent;
},
enabled: !!flowID,
},
},
);
return (
<Alert className="absolute bottom-4 left-1/2 z-20 w-auto -translate-x-1/2 select-none rounded-xlarge">
<AlertTitle>You are building a Trigger Agent</AlertTitle>
<AlertDescription>
Your agent will listen for its trigger and will run when the time is
right.
<br />
You can view its activity in your{" "}
<Link
href={
libraryAgent ? `/library/agents/${libraryAgent.id}` : "/library"
}
className="underline"
>
Agent Library
</Link>
.
</AlertDescription>
</Alert>
);
};

View File

@@ -44,6 +44,7 @@ type NodeStore = {
) => void;
getNodeExecutionResult: (nodeId: string) => NodeExecutionResult | undefined;
getNodeBlockUIType: (nodeId: string) => BlockUIType;
hasWebhookNodes: () => boolean;
};
export const useNodeStore = create<NodeStore>((set, get) => ({
@@ -204,4 +205,9 @@ export const useNodeStore = create<NodeStore>((set, get) => ({
BlockUIType.STANDARD
);
},
hasWebhookNodes: () => {
return get().nodes.some((n) =>
[BlockUIType.WEBHOOK, BlockUIType.WEBHOOK_MANUAL].includes(n.data.uiType),
);
},
}));