Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ packages/db/prisma/migrations/migration_lock.toml
apps/http-backend/tsconfig.tsbuildinfo
packages/common/tsconfig.tsbuildinfo
packages/db/tsconfig.tsbuildinfo
apps/http-backend/tsconfig.tsbuildinfo
31 changes: 28 additions & 3 deletions apps/http-backend/src/routes/userRoutes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,18 +372,43 @@ router.get(
userId: userId,
},
include: {
Trigger: true,
nodes: { orderBy: { stage: "asc" } },
Trigger: {
include: {
triggerType: {
select:{icon : true}
}
}
},
nodes: { orderBy: { stage: "asc" },
include: {
AvailableNode: {
select: { icon: true}
}
}
},
Comment on lines +375 to +388

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep type in the workflow payload.

These selects now keep only icon from triggerType and AvailableNode, but Trigger and Node themselves do not have a type column in packages/db/prisma/schema.prisma. After this transform, /workflow/:workflowId returns icon but drops the runtime trigger.type / node.type information that apps/web/store/slices/workflowSlice.ts still expects. Reloading a saved workflow will lose the node/trigger kind needed to rebuild the editor state.

Also applies to: 396-408

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts` around lines 375 -
388, The workflow payload is dropping trigger/node runtime kinds by only
selecting icon; update the query in userRoutes.ts so the Trigger -> triggerType
select and nodes -> AvailableNode select also include the type field (e.g.,
select: { icon: true, type: true }) so the returned payload contains
trigger.type and node.type that apps/web/store/slices/workflowSlice.ts expects;
apply the same change to the analogous block around lines 396-408.

},
});
if (!getWorkflow) {
return res.status(statusCodes.UNAUTHORIZED).json({
message: "Workflow Not found or not authorized",
});
}
const workflow = {
...getWorkflow,
Trigger: getWorkflow.Trigger ? {
...getWorkflow.Trigger,
icon: getWorkflow.Trigger.triggerType.icon || null,
AvailableTrigger: undefined
} : null,
nodes: getWorkflow.nodes.map(node=> ({
...node,
icon: node.AvailableNode.icon || null,
AvailableNode: undefined
}))
}
return res.status(statusCodes.OK).json({
message: "workflow Fetched succesfully",
Data: getWorkflow,
Data: workflow,
});
} catch (error: any) {
console.log("Error Fetching the workflow ", error.message);
Expand Down
1 change: 0 additions & 1 deletion apps/http-backend/tsconfig.tsbuildinfo

This file was deleted.

4 changes: 3 additions & 1 deletion apps/web/app/components/Actions/ActionSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ export const ActionSideBar = ({ isOpen, onClose, onSelectAction }: SideBarProps)
{availableActions.length ? (
availableActions.map((action: any) => (
<SelectItem key={action.id} value={String(action.id)}>
{'icon' in action && action.icon ? action.icon : '⚡'} {action.name}
<div className="flex items-center gap-2">
{'icon' in action && action.icon ? <img src={action.icon} className="w-8 h-4" /> : '⚡'} {action.name}
</div>
</SelectItem>
))
) : (
Expand Down
21 changes: 14 additions & 7 deletions apps/web/app/components/nodes/BaseNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,39 +89,46 @@ export default function BaseNode({ id, type, data }: BaseNodeProps) {
}

return (
<div className="text-black bg-white border-2 border-gray-200 rounded-lg shadow-sm min-w-[200px] relative">
<div className="text-black bg-gray-50 border-gray-200 rounded-lg shadow-[gray_0px_0px_2px_0.1px] min-w-[200px] relative">
<div className="px-4 py-3">
{/* Icon + Label */}
<div className="flex items-center gap-2 mb-1">
<span className="text-xl">{icon || "📦"}</span>
<div className="flex flex-col items-center gap-2 mb-1">
<span className="text-xl p-2 rounded-full object-center">
{ icon ?
<img src={icon ? icon : "⚡"} className="w-16 h-16 object-cover"
/> : ("⚡")}
Comment on lines +95 to +99

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Don’t treat the fallback glyph as an image URL.

This branch renders <img> for any truthy icon, including the "⚡" fallback currently passed by other UI paths. That produces a broken image instead of the fallback glyph on nodes without a real icon asset.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/nodes/BaseNode.tsx` around lines 95 - 99, The current
JSX in BaseNode.tsx treats any truthy icon as an image and renders <img
src={icon} />, which breaks when icon is a glyph like "⚡"; update the render
logic in the BaseNode component so that it only uses an <img> when icon is a
real image URL/data URI/path (e.g., startsWith('http'), startsWith('/'),
startsWith('data:'), or has an image extension like .png/.jpg/.svg), otherwise
render the icon value directly as text inside the span as the fallback glyph.
Locate the span rendering the icon (variable name icon) and change the
conditional to distinguish image URLs from plain glyph strings.


</span>
<span className="font-semibold text-sm">{label}</span>
</div>
<div className="flex justify-center w-full">
{data.isConfigured ? (
<span className="ml-2 px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
<span className="ml-2 px-2 py-1 bg-green-100 text-green-800 text-center text-xs rounded-full">
✓ Configured
</span>
) : (
<span className="ml-2 px-2 py-1 bg-red-100 text-red-800 text-xs rounded-full">
Not Configured
</span>
)}
</div>

{/* Buttons */}
<div className="flex gap-2 mt-2">
{onConfigure && (
<button
onClick={onConfigure}
className="text-xs px-2 py-1 bg-gray-100 rounded hover:bg-gray-200"
className="text-xs px-2 py-1 bg-blue-100 rounded hover:bg-blue-200"
>
⚙️ Settings
<svg width="10px" height="10px" viewBox="0 0 32.00 32.00" xmlns="http://www.w3.org/2000/svg" fill="#000000"><g id="SVGRepo_bgCarrier" strokeWidth="0"></g><g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round" stroke="#000000" strokeWidth="0.576"></g><g id="SVGRepo_iconCarrier"><title>file_type_config</title><path d="M23.265,24.381l.9-.894c4.164.136,4.228-.01,4.411-.438l1.144-2.785L29.805,20l-.093-.231c-.049-.122-.2-.486-2.8-2.965V15.5c3-2.89,2.936-3.038,2.765-3.461L28.538,9.225c-.171-.422-.236-.587-4.37-.474l-.9-.93a20.166,20.166,0,0,0-.141-4.106l-.116-.263-2.974-1.3c-.438-.2-.592-.272-3.4,2.786l-1.262-.019c-2.891-3.086-3.028-3.03-3.461-2.855L9.149,3.182c-.433.175-.586.237-.418,4.437l-.893.89c-4.162-.136-4.226.012-4.407.438L2.285,11.733,2.195,12l.094.232c.049.12.194.48,2.8,2.962l0,1.3c-3,2.89-2.935,3.038-2.763,3.462l1.138,2.817c.174.431.236.584,4.369.476l.9.935a20.243,20.243,0,0,0,.137,4.1l.116.265,2.993,1.308c.435.182.586.247,3.386-2.8l1.262.016c2.895,3.09,3.043,3.03,3.466,2.859l2.759-1.115C23.288,28.644,23.44,28.583,23.265,24.381ZM11.407,17.857a4.957,4.957,0,1,1,6.488,2.824A5.014,5.014,0,0,1,11.407,17.857Z" fill="#000000"></path></g></svg>
</button>
)}
{onTest && (
<button
onClick={onTest}
className="text-xs px-2 py-1 bg-blue-100 rounded hover:bg-blue-200"
>
▶️ Test
<svg width="10px" height="10px" viewBox="0 0 24.00 24.00" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#000000" strokeWidth="0.00024000000000000003" transform="rotate(0)"><g id="SVGRepo_bgCarrier" strokeWidth="0"></g><g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M21.4086 9.35258C23.5305 10.5065 23.5305 13.4935 21.4086 14.6474L8.59662 21.6145C6.53435 22.736 4 21.2763 4 18.9671L4 5.0329C4 2.72368 6.53435 1.26402 8.59661 2.38548L21.4086 9.35258Z" fill="#000000"></path> </g></svg>
</button>
)}
</div>
Expand Down
4 changes: 3 additions & 1 deletion apps/web/app/components/nodes/TriggerSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export const TriggerSideBar = ({ isOpen, onClose, onSelectTrigger }: SideBarPro
{triggers.map((trigger) => (
<SelectItem key={trigger.id} value={trigger.type}>
{/* Display a placeholder icon if 'icon' is missing */}
{"icon" in trigger ? (trigger as any).icon : "⚡"} {trigger.name}
<div className="flex items-center gap-2">
{'icon' in trigger && trigger.icon ? <img src={trigger.icon as string} className="w-8 h-4" /> : '⚡'} {trigger.name}
</div>
</SelectItem>
))}
</SelectContent>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/components/ui/variable-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function VariablePanel({ previousNodes, onInsert, activeField }: Variable
{/* Node Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 text-sm font-medium text-gray-300">
<span>{node.icon || "⚙️"}</span>
<span><img src={node.icon} className="w-12 h-8"/></span>
<span>{node.nodeName}</span>
Comment on lines 81 to 84

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard the image fallback here.

node.icon is nullable, and some paths still use an emoji fallback when no icon exists. Rendering <img src={node.icon}> unconditionally turns both cases into a broken image in the variable panel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/ui/variable-panel.tsx` around lines 81 - 84, The
variable panel currently always renders an <img src={node.icon} className="w-12
h-8"/> even though node.icon can be null, causing broken image icons; update the
JSX in the component (around the image/emoji rendering in variable-panel.tsx
where node.icon and node.nodeName are used) to conditionally render the <img>
only when node.icon is a non-empty string and otherwise render the existing
emoji/text fallback (the current emoji fallback used elsewhere in the app),
ensuring you keep the same sizing/styling for the fallback so layout doesn’t
shift.

</div>
{isTested && (
Expand Down
4 changes: 3 additions & 1 deletion apps/web/app/hooks/useAutoSave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export function useAutoSave(workflowId: string){
const { isChanged } = store.getState().workflow
if(isChanged.edges || isChanged.nodes || isChanged.trigger){
e.preventDefault()
batchSave();
// batchSave();
e.returnValue = true;
return true
}
}

Expand Down
33 changes: 7 additions & 26 deletions apps/web/app/workflows/[id]/components/ConfigModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ interface ConfigModalProps {
isOpen: boolean;
selectedNode: any | null;
onClose: () => void;
// onSave: (selectedNode: string, config: any, userId: string) => Promise<void>;
workflowId?: string;
previousNodes: PreviousNodeOutput[];
}
Expand All @@ -32,7 +31,6 @@ export default function ConfigModal({
isOpen,
selectedNode,
onClose,
// onSave,
workflowId,
previousNodes,
}: ConfigModalProps) {
Expand Down Expand Up @@ -407,28 +405,18 @@ export default function ConfigModal({

return (
<div
className="fixed inset-0 flex items-center justify-center z-50 p-4"
style={{
background: "linear-gradient(135deg, #000 80%, #333 100%)",
overflowY: "auto",
}}
>
className="fixed inset-0 flex gap-2 items-center justify-between z-50 p-4 bg-white/80">
<VariablePanel
previousNodes={previousNodes}
onInsert={handleVariableInsert}
activeField={activeField}
/>
<div
className="rounded-lg shadow-xl max-w-md w-full max-h-[90vh] flex flex-col"
style={{
background: "linear-gradient(160deg, #131313 70%, #191919 100%)",
color: "white",
}}
>
className="rounded-lg shadow-xl max-w-md m-10 w-full h-[90%] overflow-y-scroll py-2 flex flex-col bg-black/80 ">
{/* Header */}
<div className="p-6 border-b border-gray-900 flex items-center justify-between flex-shrink-0">
<h2 className="text-xl font-semibold text-white">
Configure {selectedNode.name}
<h2 className="text-xl font-semibold flex gap-1 text-white">
<img src={selectedNode.icon} className="w-12 h-8"/> {selectedNode.name}
Comment on lines +418 to +419

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Render the header icon defensively.

selectedNode.icon is not always an image URL here. The workflow page still uses emoji fallbacks like and ⚙️, and it can also surface an empty string from store state, so Line 419 will show a broken image for common nodes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/workflows/`[id]/components/ConfigModal.tsx around lines 418 -
419, The header should render selectedNode.icon defensively: update the
ConfigModal header (where selectedNode.icon and selectedNode.name are used) to
only render an <img> when selectedNode.icon is a non-empty, valid image URL
(e.g. starts with http(s):, data:, or a local path) and otherwise render the
icon value as plain text/emoji or a default fallback element; add an alt
attribute and maintain the existing className for the image. Implement a small
predicate (e.g. isValidImageUrl(icon)) or inline checks before using <img> so
empty strings or emoji won’t produce a broken image.

</h2>
<button
onClick={onClose}
Expand Down Expand Up @@ -576,6 +564,7 @@ export default function ConfigModal({
className="p-6 border-t border-gray-900 flex justify-between gap-3"
style={{ background: "#181818" }}
>
{ !selectedNode.name.includes("webhook") &&
<button
onClick={handleTestNode}
disabled={isTestingNode || loading}
Expand All @@ -593,17 +582,9 @@ export default function ConfigModal({
</>
)}
</button>

}
<div className="flex gap-3">
{/* <button
onClick={onClose}
className="px-4 py-2 text-white hover:bg-gray-700 rounded border border-gray-900"
style={{ background: "#111" }}
disabled={loading}
type="button"
>
Cancel
</button> */}

<button
onClick={()=> {
onClose();
Expand Down
Loading