diff --git a/surfsense_web/components/tool-ui/gmail/create-draft.tsx b/surfsense_web/components/tool-ui/gmail/create-draft.tsx index 2f7b72009..7cd6a6c27 100644 --- a/surfsense_web/components/tool-ui/gmail/create-draft.tsx +++ b/surfsense_web/components/tool-ui/gmail/create-draft.tsx @@ -132,6 +132,9 @@ function ApprovalCard({ const wasAlreadyDecided = interruptData.__decided__ != null; const [isPanelOpen, setIsPanelOpen] = useState(false); const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom); + const [pendingEdits, setPendingEdits] = useState<{ + subject: string; body: string; to: string; cc: string; bcc: string; + } | null>(null); const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter((a) => !a.auth_expired); @@ -153,18 +156,26 @@ function ApprovalCard({ const handleApprove = useCallback(() => { if (decided || isPanelOpen || !canApprove) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: { ...args, + ...(pendingEdits && { + subject: pendingEdits.subject, + body: pendingEdits.body, + to: pendingEdits.to, + cc: pendingEdits.cc, + bcc: pendingEdits.bcc, + }), connector_id: selectedAccountId ? Number(selectedAccountId) : null, }, }, }); - }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedAccountId]); + }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedAccountId, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -214,33 +225,24 @@ function ApprovalCard({ onClick={() => { setIsPanelOpen(true); const extraFields: ExtraField[] = [ - { key: "to", label: "To", type: "email", value: args.to || "" }, - { key: "cc", label: "CC", type: "email", value: args.cc || "" }, - { key: "bcc", label: "BCC", type: "email", value: args.bcc || "" }, + { key: "to", label: "To", type: "email", value: pendingEdits?.to ?? args.to ?? "" }, + { key: "cc", label: "CC", type: "email", value: pendingEdits?.cc ?? args.cc ?? "" }, + { key: "bcc", label: "BCC", type: "email", value: pendingEdits?.bcc ?? args.bcc ?? "" }, ]; openHitlEditPanel({ - title: args.subject ?? "", - content: args.body ?? "", + title: pendingEdits?.subject ?? (args.subject ?? ""), + content: pendingEdits?.body ?? (args.body ?? ""), toolName: "Gmail Draft", extraFields, onSave: (newTitle, newContent, extraFieldValues) => { setIsPanelOpen(false); - setDecided("edit"); const extras = extraFieldValues ?? {}; - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: { - ...args, - subject: newTitle, - body: newContent, - to: extras.to ?? args.to, - cc: extras.cc ?? args.cc, - bcc: extras.bcc ?? args.bcc, - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - }, - }, + setPendingEdits({ + subject: newTitle, + body: newContent, + to: extras.to ?? pendingEdits?.to ?? args.to ?? "", + cc: extras.cc ?? pendingEdits?.cc ?? args.cc ?? "", + bcc: extras.bcc ?? pendingEdits?.bcc ?? args.bcc ?? "", }); }, }); @@ -297,31 +299,31 @@ function ApprovalCard({ {/* Email headers + body preview */}
- {args.to && ( + {(pendingEdits?.to ?? args.to) && (
- To: {args.to} + To: {pendingEdits?.to ?? args.to}
)} - {args.cc && args.cc.trim() !== "" && ( + {(pendingEdits?.cc ?? args.cc) && (pendingEdits?.cc ?? args.cc)?.trim() !== "" && (
- CC: {args.cc} + CC: {pendingEdits?.cc ?? args.cc}
)} - {args.bcc && args.bcc.trim() !== "" && ( + {(pendingEdits?.bcc ?? args.bcc) && (pendingEdits?.bcc ?? args.bcc)?.trim() !== "" && (
- BCC: {args.bcc} + BCC: {pendingEdits?.bcc ?? args.bcc}
)}
- {args.subject != null && ( -

{args.subject}

+ {(pendingEdits?.subject ?? args.subject) != null && ( +

{pendingEdits?.subject ?? args.subject}

)} - {args.body != null && ( + {(pendingEdits?.body ?? args.body) != null && (
(null); const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter((a) => !a.auth_expired); @@ -154,18 +157,26 @@ function ApprovalCard({ const handleApprove = useCallback(() => { if (decided || isPanelOpen || !canApprove) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: { ...args, + ...(pendingEdits && { + subject: pendingEdits.subject, + body: pendingEdits.body, + to: pendingEdits.to, + cc: pendingEdits.cc, + bcc: pendingEdits.bcc, + }), connector_id: selectedAccountId ? Number(selectedAccountId) : null, }, }, }); - }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedAccountId]); + }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedAccountId, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -216,33 +227,24 @@ function ApprovalCard({ onClick={() => { setIsPanelOpen(true); const extraFields: ExtraField[] = [ - { key: "to", label: "To", type: "email", value: args.to || "" }, - { key: "cc", label: "CC", type: "email", value: args.cc || "" }, - { key: "bcc", label: "BCC", type: "email", value: args.bcc || "" }, + { key: "to", label: "To", type: "email", value: pendingEdits?.to ?? args.to ?? "" }, + { key: "cc", label: "CC", type: "email", value: pendingEdits?.cc ?? args.cc ?? "" }, + { key: "bcc", label: "BCC", type: "email", value: pendingEdits?.bcc ?? args.bcc ?? "" }, ]; openHitlEditPanel({ - title: args.subject ?? "", - content: args.body ?? "", + title: pendingEdits?.subject ?? (args.subject ?? ""), + content: pendingEdits?.body ?? (args.body ?? ""), toolName: "Send Email", extraFields, onSave: (newTitle, newContent, extraFieldValues) => { setIsPanelOpen(false); - setDecided("edit"); const extras = extraFieldValues ?? {}; - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: { - ...args, - subject: newTitle, - body: newContent, - to: extras.to ?? args.to, - cc: extras.cc ?? args.cc, - bcc: extras.bcc ?? args.bcc, - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - }, - }, + setPendingEdits({ + subject: newTitle, + body: newContent, + to: extras.to ?? pendingEdits?.to ?? args.to ?? "", + cc: extras.cc ?? pendingEdits?.cc ?? args.cc ?? "", + bcc: extras.bcc ?? pendingEdits?.bcc ?? args.bcc ?? "", }); }, }); @@ -299,31 +301,31 @@ function ApprovalCard({ {/* Email headers + body preview */}
- {args.to && ( + {(pendingEdits?.to ?? args.to) && (
- To: {args.to} + To: {pendingEdits?.to ?? args.to}
)} - {args.cc && args.cc.trim() !== "" && ( + {(pendingEdits?.cc ?? args.cc) && (pendingEdits?.cc ?? args.cc)!.trim() !== "" && (
- CC: {args.cc} + CC: {pendingEdits?.cc ?? args.cc}
)} - {args.bcc && args.bcc.trim() !== "" && ( + {(pendingEdits?.bcc ?? args.bcc) && (pendingEdits?.bcc ?? args.bcc)!.trim() !== "" && (
- BCC: {args.bcc} + BCC: {pendingEdits?.bcc ?? args.bcc}
)}
- {args.subject != null && ( -

{args.subject}

+ {(pendingEdits?.subject ?? args.subject) != null && ( +

{pendingEdits?.subject ?? args.subject}

)} - {args.body != null && ( + {(pendingEdits?.body ?? args.body) != null && (
(null); const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter((a) => !a.auth_expired); @@ -189,24 +193,42 @@ function ApprovalCard({ const canApprove = !!selectedAccountId && !!selectedCalendarId && - !!args.summary?.trim(); + !!(pendingEdits?.summary ?? args.summary)?.trim(); const handleApprove = useCallback(() => { if (decided || isPanelOpen || !canApprove) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); + + const finalArgs: Record = { + ...args, + connector_id: selectedAccountId ? Number(selectedAccountId) : null, + calendar_id: selectedCalendarId || null, + }; + + if (pendingEdits) { + finalArgs.summary = pendingEdits.summary; + finalArgs.description = pendingEdits.description; + if (pendingEdits.start_datetime) finalArgs.start_datetime = pendingEdits.start_datetime; + if (pendingEdits.end_datetime) finalArgs.end_datetime = pendingEdits.end_datetime; + if (pendingEdits.location !== undefined) finalArgs.location = pendingEdits.location; + if (pendingEdits.attendees !== undefined) { + finalArgs.attendees = pendingEdits.attendees + .split(",") + .map((e) => e.trim()) + .filter(Boolean); + } + } + onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, - args: { - ...args, - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - calendar_id: selectedCalendarId || null, - }, + args: finalArgs, }, }); - }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedAccountId, selectedCalendarId]); + }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedAccountId, selectedCalendarId, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -219,6 +241,9 @@ function ApprovalCard({ }, [handleApprove]); const attendeesList = (args.attendees as string[]) ?? []; + const displayAttendees = pendingEdits?.attendees + ? pendingEdits.attendees.split(",").map((e) => e.trim()).filter(Boolean) + : attendeesList; return (
@@ -259,46 +284,26 @@ function ApprovalCard({ onClick={() => { setIsPanelOpen(true); const extraFields: ExtraField[] = [ - { key: "start_datetime", label: "Start", type: "datetime-local", value: args.start_datetime || "" }, - { key: "end_datetime", label: "End", type: "datetime-local", value: args.end_datetime || "" }, - { key: "location", label: "Location", type: "text", value: args.location || "" }, - { key: "attendees", label: "Attendees (comma-separated emails)", type: "text", value: attendeesList.join(", ") }, + { key: "start_datetime", label: "Start", type: "datetime-local", value: pendingEdits?.start_datetime ?? args.start_datetime ?? "" }, + { key: "end_datetime", label: "End", type: "datetime-local", value: pendingEdits?.end_datetime ?? args.end_datetime ?? "" }, + { key: "location", label: "Location", type: "text", value: pendingEdits?.location ?? args.location ?? "" }, + { key: "attendees", label: "Attendees (comma-separated emails)", type: "text", value: pendingEdits?.attendees ?? attendeesList.join(", ") }, ]; openHitlEditPanel({ - title: args.summary ?? "", - content: args.description ?? "", + title: pendingEdits?.summary ?? (args.summary ?? ""), + content: pendingEdits?.description ?? (args.description ?? ""), toolName: "Calendar Event", extraFields, onSave: (newTitle, newContent, extraFieldValues) => { setIsPanelOpen(false); - setDecided("edit"); - - const editedArgs: Record = { - ...args, + const extras = extraFieldValues ?? {}; + setPendingEdits({ summary: newTitle, description: newContent, - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - calendar_id: selectedCalendarId || null, - }; - - if (extraFieldValues) { - if (extraFieldValues.start_datetime) editedArgs.start_datetime = extraFieldValues.start_datetime; - if (extraFieldValues.end_datetime) editedArgs.end_datetime = extraFieldValues.end_datetime; - if (extraFieldValues.location !== undefined) editedArgs.location = extraFieldValues.location; - if (extraFieldValues.attendees !== undefined) { - editedArgs.attendees = extraFieldValues.attendees - .split(",") - .map((e) => e.trim()) - .filter(Boolean); - } - } - - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: editedArgs, - }, + start_datetime: extras.start_datetime ?? pendingEdits?.start_datetime ?? args.start_datetime ?? "", + end_datetime: extras.end_datetime ?? pendingEdits?.end_datetime ?? args.end_datetime ?? "", + location: extras.location ?? pendingEdits?.location ?? args.location ?? "", + attendees: extras.attendees ?? pendingEdits?.attendees ?? attendeesList.join(", "), }); }, }); @@ -385,36 +390,36 @@ function ApprovalCard({ {/* Content preview */}
- {args.summary && ( -

{args.summary}

+ {(pendingEdits?.summary ?? args.summary) && ( +

{pendingEdits?.summary ?? args.summary}

)} - {(args.start_datetime || args.end_datetime) && ( + {((pendingEdits?.start_datetime ?? args.start_datetime) || (pendingEdits?.end_datetime ?? args.end_datetime)) && (
- {args.start_datetime ? formatDateTime(args.start_datetime) : ""} - {args.start_datetime && args.end_datetime ? " — " : ""} - {args.end_datetime ? formatDateTime(args.end_datetime) : ""} + {(pendingEdits?.start_datetime ?? args.start_datetime) ? formatDateTime(pendingEdits?.start_datetime ?? args.start_datetime) : ""} + {(pendingEdits?.start_datetime ?? args.start_datetime) && (pendingEdits?.end_datetime ?? args.end_datetime) ? " — " : ""} + {(pendingEdits?.end_datetime ?? args.end_datetime) ? formatDateTime(pendingEdits?.end_datetime ?? args.end_datetime) : ""}
)} - {args.location && ( + {(pendingEdits?.location ?? args.location) && (
- {args.location} + {pendingEdits?.location ?? args.location}
)} - {attendeesList.length > 0 && ( + {displayAttendees.length > 0 && (
- {attendeesList.join(", ")} + {displayAttendees.join(", ")}
)} - {args.description && ( + {(pendingEdits?.description ?? args.description) && (
(null); const reviewConfig = interruptData.review_configs[0]; const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"]; @@ -216,6 +220,22 @@ function ApprovalCard({ String(actionArgs.new_description ?? "") !== (event?.description ?? ""); const buildFinalArgs = useCallback(() => { + if (pendingEdits) { + const attendeesArr = pendingEdits.attendees + ? pendingEdits.attendees.split(",").map((e) => e.trim()).filter(Boolean) + : null; + return { + event_id: event?.event_id, + document_id: event?.document_id, + connector_id: account?.id, + new_summary: pendingEdits.summary || null, + new_description: pendingEdits.description || null, + new_start_datetime: pendingEdits.start_datetime || null, + new_end_datetime: pendingEdits.end_datetime || null, + new_location: pendingEdits.location || null, + new_attendees: attendeesArr, + }; + } return { event_id: event?.event_id, document_id: event?.document_id, @@ -227,20 +247,21 @@ function ApprovalCard({ new_location: actionArgs.new_location ?? null, new_attendees: proposedAttendees ?? null, }; - }, [event, account, actionArgs, proposedAttendees]); + }, [event, account, actionArgs, proposedAttendees, pendingEdits]); const handleApprove = useCallback(() => { if (decided || isPanelOpen) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: buildFinalArgs(), }, }); - }, [decided, isPanelOpen, allowedDecisions, onDecision, interruptData, buildFinalArgs]); + }, [decided, isPanelOpen, allowedDecisions, onDecision, interruptData, buildFinalArgs, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -290,24 +311,18 @@ function ApprovalCard({ className="rounded-lg text-muted-foreground -mt-1 -mr-2" onClick={() => { setIsPanelOpen(true); - const proposedSummary = actionArgs.new_summary - ? String(actionArgs.new_summary) - : (event?.summary ?? ""); - const proposedDescription = actionArgs.new_description - ? String(actionArgs.new_description) - : (event?.description ?? ""); - const proposedStart = actionArgs.new_start_datetime - ? String(actionArgs.new_start_datetime) - : (event?.start ?? ""); - const proposedEnd = actionArgs.new_end_datetime - ? String(actionArgs.new_end_datetime) - : (event?.end ?? ""); - const proposedLocation = actionArgs.new_location !== undefined - ? String(actionArgs.new_location ?? "") - : (event?.location ?? ""); - const proposedAttendeesStr = proposedAttendees - ? proposedAttendees.join(", ") - : currentAttendees.join(", "); + const proposedSummary = pendingEdits?.summary + ?? (actionArgs.new_summary ? String(actionArgs.new_summary) : (event?.summary ?? "")); + const proposedDescription = pendingEdits?.description + ?? (actionArgs.new_description ? String(actionArgs.new_description) : (event?.description ?? "")); + const proposedStart = pendingEdits?.start_datetime + ?? (actionArgs.new_start_datetime ? String(actionArgs.new_start_datetime) : (event?.start ?? "")); + const proposedEnd = pendingEdits?.end_datetime + ?? (actionArgs.new_end_datetime ? String(actionArgs.new_end_datetime) : (event?.end ?? "")); + const proposedLocation = pendingEdits?.location + ?? (actionArgs.new_location !== undefined ? String(actionArgs.new_location ?? "") : (event?.location ?? "")); + const proposedAttendeesStr = pendingEdits?.attendees + ?? (proposedAttendees ? proposedAttendees.join(", ") : currentAttendees.join(", ")); const extraFields: ExtraField[] = [ { key: "start_datetime", label: "Start", type: "datetime-local", value: proposedStart }, @@ -322,34 +337,14 @@ function ApprovalCard({ extraFields, onSave: (newTitle, newContent, extraFieldValues) => { setIsPanelOpen(false); - setDecided("edit"); - - const editedArgs: Record = { - event_id: event?.event_id, - document_id: event?.document_id, - connector_id: account?.id, - new_summary: newTitle || null, - new_description: newContent || null, - }; - - if (extraFieldValues) { - editedArgs.new_start_datetime = extraFieldValues.start_datetime || null; - editedArgs.new_end_datetime = extraFieldValues.end_datetime || null; - editedArgs.new_location = extraFieldValues.location || null; - if (extraFieldValues.attendees !== undefined) { - editedArgs.new_attendees = extraFieldValues.attendees - .split(",") - .map((e) => e.trim()) - .filter(Boolean); - } - } - - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: editedArgs, - }, + const extras = extraFieldValues ?? {}; + setPendingEdits({ + summary: newTitle, + description: newContent, + start_datetime: extras.start_datetime ?? proposedStart, + end_datetime: extras.end_datetime ?? proposedEnd, + location: extras.location ?? proposedLocation, + attendees: extras.attendees ?? proposedAttendeesStr, }); }, }); diff --git a/surfsense_web/components/tool-ui/google-drive/create-file.tsx b/surfsense_web/components/tool-ui/google-drive/create-file.tsx index cca93ce17..e0a92b984 100644 --- a/surfsense_web/components/tool-ui/google-drive/create-file.tsx +++ b/surfsense_web/components/tool-ui/google-drive/create-file.tsx @@ -137,6 +137,7 @@ function ApprovalCard({ const wasAlreadyDecided = interruptData.__decided__ != null; const [isPanelOpen, setIsPanelOpen] = useState(false); const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom); + const [pendingEdits, setPendingEdits] = useState<{ name: string; content: string } | null>(null); const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter(a => !a.auth_expired); @@ -164,10 +165,10 @@ function ApprovalCard({ const fileTypeLabel = FILE_TYPE_LABELS[selectedFileType] ?? FILE_TYPE_LABELS[args.file_type] ?? "Google Drive File"; - const isNameValid = useMemo( - () => args.name && typeof args.name === "string" && args.name.trim().length > 0, - [args.name] - ); + const isNameValid = useMemo(() => { + const name = pendingEdits?.name ?? args.name; + return name && typeof name === "string" && name.trim().length > 0; + }, [pendingEdits?.name, args.name]); const canApprove = !!selectedAccountId && isNameValid; @@ -178,20 +179,22 @@ function ApprovalCard({ const handleApprove = useCallback(() => { if (decided || isPanelOpen || !canApprove) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: { ...args, + ...(pendingEdits && { name: pendingEdits.name, content: pendingEdits.content }), file_type: selectedFileType, connector_id: selectedAccountId ? Number(selectedAccountId) : null, parent_folder_id: parentFolderId === "__root__" ? null : parentFolderId, }, }, }); - }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedFileType, selectedAccountId, parentFolderId]); + }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedFileType, selectedAccountId, parentFolderId, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -239,27 +242,13 @@ function ApprovalCard({ onClick={() => { setIsPanelOpen(true); openHitlEditPanel({ - title: args.name ?? "", - content: args.content ?? "", + title: pendingEdits?.name ?? (args.name ?? ""), + content: pendingEdits?.content ?? (args.content ?? ""), toolName: fileTypeLabel, - onSave: (newName, newContent) => { - setIsPanelOpen(false); - setDecided("edit"); - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: { - ...args, - name: newName, - content: newContent, - file_type: selectedFileType, - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - parent_folder_id: parentFolderId === "__root__" ? null : parentFolderId, - }, - }, - }); - }, + onSave: (newName, newContent) => { + setIsPanelOpen(false); + setPendingEdits({ name: newName, content: newContent }); + }, }); }} > @@ -355,10 +344,10 @@ function ApprovalCard({ {/* Content preview */}
- {args.name != null && ( -

{args.name}

+ {(pendingEdits?.name ?? args.name) != null && ( +

{String(pendingEdits?.name ?? args.name)}

)} - {args.content != null && ( + {(pendingEdits?.content ?? args.content) != null && (
(null); const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(""); const [selectedTeamId, setSelectedTeamId] = useState(""); @@ -171,7 +172,7 @@ function ApprovalCard({ [selectedWorkspace, selectedTeamId] ); - const isTitleValid = (args.title ?? "").trim().length > 0; + const isTitleValid = (pendingEdits?.title ?? args.title ?? "").trim().length > 0; const canApprove = !!selectedWorkspaceId && !!selectedTeamId && isTitleValid; const reviewConfig = interruptData.review_configs[0]; @@ -180,8 +181,8 @@ function ApprovalCard({ const buildFinalArgs = useCallback((overrides?: { title?: string; description?: string }) => { return { - title: overrides?.title ?? args.title, - description: overrides?.description ?? args.description ?? null, + title: overrides?.title ?? pendingEdits?.title ?? args.title, + description: overrides?.description ?? pendingEdits?.description ?? args.description ?? null, connector_id: selectedWorkspaceId ? Number(selectedWorkspaceId) : null, team_id: selectedTeamId || null, state_id: selectedStateId === "__none__" ? null : selectedStateId, @@ -189,20 +190,21 @@ function ApprovalCard({ priority: Number(selectedPriority), label_ids: selectedLabelIds, }; - }, [args.title, args.description, selectedWorkspaceId, selectedTeamId, selectedStateId, selectedAssigneeId, selectedPriority, selectedLabelIds]); + }, [args.title, args.description, selectedWorkspaceId, selectedTeamId, selectedStateId, selectedAssigneeId, selectedPriority, selectedLabelIds, pendingEdits]); const handleApprove = useCallback(() => { if (decided || isPanelOpen || !canApprove) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: buildFinalArgs(), }, }); - }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, buildFinalArgs]); + }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, buildFinalArgs, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -250,19 +252,12 @@ function ApprovalCard({ onClick={() => { setIsPanelOpen(true); openHitlEditPanel({ - title: args.title ?? "", - content: args.description ?? "", + title: pendingEdits?.title ?? (args.title ?? ""), + content: pendingEdits?.description ?? (args.description ?? ""), toolName: "Linear Issue", onSave: (newTitle, newDescription) => { setIsPanelOpen(false); - setDecided("edit"); - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: buildFinalArgs({ title: newTitle, description: newDescription }), - }, - }); + setPendingEdits({ title: newTitle, description: newDescription }); }, }); }} @@ -461,10 +456,10 @@ function ApprovalCard({ {/* Content preview */}
- {args.title != null && ( -

{args.title}

+ {(pendingEdits?.title ?? args.title) != null && ( +

{pendingEdits?.title ?? args.title}

)} - {args.description != null && ( + {(pendingEdits?.description ?? args.description) != null && (
{ if (decided || isPanelOpen) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = hasPanelEdits; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: buildFinalArgs(), }, }); - }, [decided, isPanelOpen, allowedDecisions, onDecision, interruptData, buildFinalArgs]); + }, [decided, isPanelOpen, allowedDecisions, onDecision, interruptData, buildFinalArgs, hasPanelEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -321,18 +323,7 @@ function ApprovalCard({ title: newTitle, description: newDescription, })); - setDecided("edit"); - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: { - ...buildFinalArgs(), - new_title: newTitle || null, - new_description: newDescription || null, - }, - }, - }); + setHasPanelEdits(true); }, }); }} @@ -536,12 +527,12 @@ function ApprovalCard({ {/* Content preview — proposed changes */}
- {hasProposedChanges ? ( + {(hasProposedChanges || hasPanelEdits) ? ( <> - {actionArgs.new_title && ( -

{String(actionArgs.new_title)}

+ {(hasPanelEdits ? editedArgs.title : actionArgs.new_title) && ( +

{String(hasPanelEdits ? editedArgs.title : actionArgs.new_title)}

)} - {actionArgs.new_description && ( + {(hasPanelEdits ? editedArgs.description : actionArgs.new_description) && (
(null); const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter(a => !a.auth_expired); @@ -144,8 +145,9 @@ function ApprovalCard({ }, [selectedAccountId, parentPages]); const isTitleValid = useMemo(() => { - return args.title && typeof args.title === "string" && (args.title as string).trim().length > 0; - }, [args.title]); + const title = pendingEdits?.title ?? args.title; + return title && typeof title === "string" && title.trim().length > 0; + }, [pendingEdits?.title, args.title]); const reviewConfig = interruptData.review_configs[0]; const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"]; @@ -154,20 +156,22 @@ function ApprovalCard({ const handleApprove = useCallback(() => { if (decided || isPanelOpen || !selectedAccountId || !isTitleValid) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: { ...args, + ...(pendingEdits && { title: pendingEdits.title, content: pendingEdits.content }), connector_id: selectedAccountId ? Number(selectedAccountId) : null, parent_page_id: selectedParentPageId === "__none__" ? null : selectedParentPageId, }, }, }); - }, [decided, isPanelOpen, selectedAccountId, isTitleValid, allowedDecisions, onDecision, interruptData, args, selectedParentPageId]); + }, [decided, isPanelOpen, selectedAccountId, isTitleValid, allowedDecisions, onDecision, interruptData, args, selectedParentPageId, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -217,26 +221,12 @@ function ApprovalCard({ onClick={() => { setIsPanelOpen(true); openHitlEditPanel({ - title: String(args.title ?? ""), - content: String(args.content ?? ""), + title: pendingEdits?.title ?? String(args.title ?? ""), + content: pendingEdits?.content ?? String(args.content ?? ""), toolName: "Notion Page", onSave: (newTitle, newContent) => { setIsPanelOpen(false); - setDecided("edit"); - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: { - ...args, - title: newTitle, - content: newContent, - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - parent_page_id: - selectedParentPageId === "__none__" ? null : selectedParentPageId, - }, - }, - }); + setPendingEdits({ title: newTitle, content: newContent }); }, }); }} @@ -324,10 +314,10 @@ function ApprovalCard({ {/* Content preview */}
- {args.title != null && ( -

{String(args.title)}

+ {(pendingEdits?.title ?? args.title) != null && ( +

{String(pendingEdits?.title ?? args.title)}

)} - {args.content != null && ( + {(pendingEdits?.content ?? args.content) != null && (
(null); const account = interruptData.context?.account; const currentTitle = interruptData.context?.current_title; @@ -134,19 +135,20 @@ function ApprovalCard({ const handleApprove = useCallback(() => { if (decided || isPanelOpen) return; if (!allowedDecisions.includes("approve")) return; - setDecided("approve"); + const isEdited = pendingEdits !== null; + setDecided(isEdited ? "edit" : "approve"); onDecision({ - type: "approve", + type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: { page_id: args.page_id, - content: args.content, + content: pendingEdits?.content ?? args.content, connector_id: account?.id, }, }, }); - }, [decided, isPanelOpen, allowedDecisions, onDecision, interruptData, args, account?.id]); + }, [decided, isPanelOpen, allowedDecisions, onDecision, interruptData, args, account?.id, pendingEdits]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -195,22 +197,11 @@ function ApprovalCard({ setIsPanelOpen(true); openHitlEditPanel({ title: currentTitle ?? "", - content: String(args.content ?? ""), + content: pendingEdits?.content ?? String(args.content ?? ""), toolName: "Notion Page", onSave: (_, newContent) => { setIsPanelOpen(false); - setDecided("edit"); - onDecision({ - type: "edit", - edited_action: { - name: interruptData.action_requests[0].name, - args: { - page_id: args.page_id, - content: newContent, - connector_id: account?.id, - }, - }, - }); + setPendingEdits({ content: newContent }); }, }); }} @@ -256,7 +247,7 @@ function ApprovalCard({ {/* Content preview */}
- {args.content != null ? ( + {(pendingEdits?.content ?? args.content) != null ? (