diff --git a/.changeset/fn-7839-done-card-actions-dropdown.md b/.changeset/fn-7839-done-card-actions-dropdown.md new file mode 100644 index 0000000000..66f50e5682 --- /dev/null +++ b/.changeset/fn-7839-done-card-actions-dropdown.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Done task cards group Archive and Revert into one dropdown. +category: feature +dev: Reuses the in-progress "Send back" .card-send-back* dropdown pattern in TaskCard; new i18n key tasks.doneActions. diff --git a/packages/dashboard/app/components/TaskCard.tsx b/packages/dashboard/app/components/TaskCard.tsx index 1b0a93482c..5e22c9f794 100644 --- a/packages/dashboard/app/components/TaskCard.tsx +++ b/packages/dashboard/app/components/TaskCard.tsx @@ -906,6 +906,7 @@ function TaskCardComponent({ const [missionTitle, setMissionTitle] = useState(null); const [agentName, setAgentName] = useState(null); const [showSendBackMenu, setShowSendBackMenu] = useState(false); + const [showDoneActionsMenu, setShowDoneActionsMenu] = useState(false); const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null); const [isRetrying, setIsRetrying] = useState(false); const [isPrCreateOpen, setIsPrCreateOpen] = useState(false); @@ -925,6 +926,7 @@ function TaskCardComponent({ */ const menuButtonRef = useRef(null); const sendBackRef = useRef(null); + const doneActionsRef = useRef(null); const [isInViewport, setIsInViewport] = useState(false); const { badgeUpdates, subscribeToBadge, unsubscribeFromBadge } = useBadgeWebSocket(projectId); const { agentsMap } = useAgentsMapCache(projectId); @@ -969,6 +971,18 @@ function TaskCardComponent({ return () => document.removeEventListener("click", handleClick); }, [showSendBackMenu]); + // Close done-actions menu on outside click + useEffect(() => { + if (!showDoneActionsMenu) return; + const handleClick = (e: MouseEvent) => { + if (doneActionsRef.current && !doneActionsRef.current.contains(e.target as Node)) { + setShowDoneActionsMenu(false); + } + }; + document.addEventListener("click", handleClick); + return () => document.removeEventListener("click", handleClick); + }, [showDoneActionsMenu]); + // Fetch mission title when missionId is set useEffect(() => { if (!task.missionId) { @@ -2410,6 +2424,7 @@ function TaskCardComponent({ const openContextMenuAt = useCallback((clientX: number, clientY: number) => { if (!hasContextMenuActions || isEditing) return; setShowSendBackMenu(false); + setShowDoneActionsMenu(false); setContextMenuPosition({ x: Math.max(CONTEXT_MENU_VIEWPORT_MARGIN, Math.min(clientX, window.innerWidth - CONTEXT_MENU_VIEWPORT_MARGIN)), y: Math.max(CONTEXT_MENU_VIEWPORT_MARGIN, Math.min(clientY, window.innerHeight - CONTEXT_MENU_VIEWPORT_MARGIN)), @@ -2564,6 +2579,11 @@ function TaskCardComponent({ setShowSendBackMenu((current) => !current); }, []); + const handleDoneActionsToggle = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + setShowDoneActionsMenu((current) => !current); + }, []); + const handleSendBackOptionClick = useCallback(async (e: React.MouseEvent, column: Column) => { e.stopPropagation(); setShowSendBackMenu(false); @@ -3143,15 +3163,56 @@ function TaskCardComponent({ )} - {task.column === "done" && onArchiveTask && ( - + {task.column === "done" && (onArchiveTask || (onRevertTask && isRevertable)) && ( +
+ {/* + FNXC:BoardCardActions 2026-07-11-00:00 (FN-7839): + Done-card Archive + Revert are grouped behind one dropdown that mirrors the + in-progress "Send back" control, replacing two standalone inline buttons. + Revert only appears when the task is revertable (isRevertable); the trigger + only renders when at least one action is available so no empty shell is left. + Reuses .card-send-back* styling so no new one-off CSS/colors are introduced. + */} + + {showDoneActionsMenu && ( +
+ {onArchiveTask && ( + + )} + {onRevertTask && isRevertable && ( + + )} +
+ )} +
)} {task.column === "archived" && onUnarchiveTask && (