From fd970c800cc058cb43750df53fd1b963d55bb2e4 Mon Sep 17 00:00:00 2001 From: Zamil Majdy Date: Thu, 22 Jan 2026 21:38:17 -0500 Subject: [PATCH] feat(frontend): implement per-review auto-approval toggles - Replace global auto_approve_future_actions with per-review auto_approve_future - Add individual toggle for each review in PendingReviewCard - Track per-review auto-approval state in autoApproveFutureMap - Send auto_approve_future field with each review item - Update UI to show per-review toggle with explanation - Automatically reset data to original when auto-approve is enabled per review --- .../PendingReviewCard/PendingReviewCard.tsx | 27 +++++++ .../PendingReviewsList/PendingReviewsList.tsx | 73 ++++++++----------- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/autogpt_platform/frontend/src/components/organisms/PendingReviewCard/PendingReviewCard.tsx b/autogpt_platform/frontend/src/components/organisms/PendingReviewCard/PendingReviewCard.tsx index 3ac636060c..c15583fcd8 100644 --- a/autogpt_platform/frontend/src/components/organisms/PendingReviewCard/PendingReviewCard.tsx +++ b/autogpt_platform/frontend/src/components/organisms/PendingReviewCard/PendingReviewCard.tsx @@ -44,6 +44,8 @@ interface PendingReviewCardProps { onReviewMessageChange?: (nodeExecId: string, message: string) => void; isDisabled?: boolean; onToggleDisabled?: (nodeExecId: string) => void; + autoApproveFuture?: boolean; + onAutoApproveFutureChange?: (nodeExecId: string, enabled: boolean) => void; } export function PendingReviewCard({ @@ -53,6 +55,8 @@ export function PendingReviewCard({ onReviewMessageChange, isDisabled = false, onToggleDisabled, + autoApproveFuture = false, + onAutoApproveFutureChange, }: PendingReviewCardProps) { const extractedData = extractReviewData(review.payload); const isDataEditable = review.editable; @@ -210,6 +214,29 @@ export function PendingReviewCard({ )} + {/* Auto-approve toggle for this review */} + {!showSimplified && !isDisabled && onAutoApproveFutureChange && ( +
+
+ + onAutoApproveFutureChange(review.node_exec_id, enabled) + } + /> + + Auto-approve future executions of this block + +
+ {autoApproveFuture && ( + + Editing disabled. Original data will be used for this and all + future reviews from this block. + + )} +
+ )} + {!showSimplified && isDisabled && (
diff --git a/autogpt_platform/frontend/src/components/organisms/PendingReviewsList/PendingReviewsList.tsx b/autogpt_platform/frontend/src/components/organisms/PendingReviewsList/PendingReviewsList.tsx index 2cf695eb3a..7c83fa304c 100644 --- a/autogpt_platform/frontend/src/components/organisms/PendingReviewsList/PendingReviewsList.tsx +++ b/autogpt_platform/frontend/src/components/organisms/PendingReviewsList/PendingReviewsList.tsx @@ -41,7 +41,10 @@ export function PendingReviewsList({ "approve" | "reject" | null >(null); - const [autoApproveFuture, setAutoApproveFuture] = useState(false); + // Track per-review auto-approval state + const [autoApproveFutureMap, setAutoApproveFutureMap] = useState< + Record + >({}); const { toast } = useToast(); @@ -95,25 +98,24 @@ export function PendingReviewsList({ setReviewMessageMap((prev) => ({ ...prev, [nodeExecId]: message })); } - // Reset data to original values when toggling auto-approve - const handleAutoApproveFutureToggle = useCallback( - (enabled: boolean) => { - setAutoApproveFuture(enabled); - if (enabled) { - // Reset all data to original values - const originalData: Record = {}; - reviews.forEach((review) => { - originalData[review.node_exec_id] = JSON.stringify( - review.payload, - null, - 2, - ); - }); - setReviewDataMap(originalData); + // Handle per-review auto-approval toggle + function handleAutoApproveFutureToggle(nodeExecId: string, enabled: boolean) { + setAutoApproveFutureMap((prev) => ({ + ...prev, + [nodeExecId]: enabled, + })); + + if (enabled) { + // Reset this review's data to original value + const review = reviews.find((r) => r.node_exec_id === nodeExecId); + if (review) { + setReviewDataMap((prev) => ({ + ...prev, + [nodeExecId]: JSON.stringify(review.payload, null, 2), + })); } - }, - [reviews], - ); + } + } function processReviews(approved: boolean) { if (reviews.length === 0) { @@ -131,12 +133,13 @@ export function PendingReviewsList({ for (const review of reviews) { const reviewData = reviewDataMap[review.node_exec_id]; const reviewMessage = reviewMessageMap[review.node_exec_id]; + const autoApproveThisReview = autoApproveFutureMap[review.node_exec_id]; - // When auto-approving future actions, send undefined (use original data) + // When auto-approving future actions for this review, send undefined (use original data) // Otherwise, parse and send the edited data if available let parsedData: any = undefined; - if (!autoApproveFuture) { + if (!autoApproveThisReview) { // For regular approve/reject, use edited data if available if (review.editable && reviewData) { try { @@ -155,7 +158,7 @@ export function PendingReviewsList({ parsedData = review.payload; } } - // When autoApproveFuture is true, parsedData stays undefined + // When autoApproveThisReview is true, parsedData stays undefined // Backend will use the original payload stored in the database reviewItems.push({ @@ -163,13 +166,13 @@ export function PendingReviewsList({ approved, reviewed_data: parsedData, message: reviewMessage || undefined, + auto_approve_future: autoApproveThisReview && approved, }); } reviewActionMutation.mutate({ data: { reviews: reviewItems, - auto_approve_future_actions: autoApproveFuture && approved, }, }); } @@ -215,35 +218,19 @@ export function PendingReviewsList({
{reviews.map((review) => ( ))}
- {/* Auto-approve toggle */} -
- - - Auto-approve all future actions from these blocks - -
- - {autoApproveFuture && ( - - Editing is disabled. Original data will be used for this and all - future reviews from these blocks. - - )}