mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 17:26:23 +02:00
feat: add pending edits functionality to ApprovalCard across all HITL tools
This commit is contained in:
parent
4bd2071a8d
commit
9b38626723
9 changed files with 246 additions and 286 deletions
|
|
@ -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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3 pb-2 space-y-1.5 select-none">
|
||||
{args.to && (
|
||||
{(pendingEdits?.to ?? args.to) && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UserIcon className="size-3 shrink-0" />
|
||||
<span>To: {args.to}</span>
|
||||
<span>To: {pendingEdits?.to ?? args.to}</span>
|
||||
</div>
|
||||
)}
|
||||
{args.cc && args.cc.trim() !== "" && (
|
||||
{(pendingEdits?.cc ?? args.cc) && (pendingEdits?.cc ?? args.cc)?.trim() !== "" && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UsersIcon className="size-3 shrink-0" />
|
||||
<span>CC: {args.cc}</span>
|
||||
<span>CC: {pendingEdits?.cc ?? args.cc}</span>
|
||||
</div>
|
||||
)}
|
||||
{args.bcc && args.bcc.trim() !== "" && (
|
||||
{(pendingEdits?.bcc ?? args.bcc) && (pendingEdits?.bcc ?? args.bcc)?.trim() !== "" && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UsersIcon className="size-3 shrink-0" />
|
||||
<span>BCC: {args.bcc}</span>
|
||||
<span>BCC: {pendingEdits?.bcc ?? args.bcc}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="px-5 pt-1">
|
||||
{args.subject != null && (
|
||||
<p className="text-sm font-medium text-foreground">{args.subject}</p>
|
||||
{(pendingEdits?.subject ?? args.subject) != null && (
|
||||
<p className="text-sm font-medium text-foreground">{pendingEdits?.subject ?? args.subject}</p>
|
||||
)}
|
||||
{args.body != null && (
|
||||
{(pendingEdits?.body ?? args.body) != null && (
|
||||
<div
|
||||
className="mt-2 max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -330,7 +332,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(args.body)}
|
||||
markdown={String(pendingEdits?.body ?? args.body)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -133,6 +133,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);
|
||||
|
|
@ -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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3 pb-2 space-y-1.5 select-none">
|
||||
{args.to && (
|
||||
{(pendingEdits?.to ?? args.to) && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UserIcon className="size-3 shrink-0" />
|
||||
<span>To: {args.to}</span>
|
||||
<span>To: {pendingEdits?.to ?? args.to}</span>
|
||||
</div>
|
||||
)}
|
||||
{args.cc && args.cc.trim() !== "" && (
|
||||
{(pendingEdits?.cc ?? args.cc) && (pendingEdits?.cc ?? args.cc)!.trim() !== "" && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UsersIcon className="size-3 shrink-0" />
|
||||
<span>CC: {args.cc}</span>
|
||||
<span>CC: {pendingEdits?.cc ?? args.cc}</span>
|
||||
</div>
|
||||
)}
|
||||
{args.bcc && args.bcc.trim() !== "" && (
|
||||
{(pendingEdits?.bcc ?? args.bcc) && (pendingEdits?.bcc ?? args.bcc)!.trim() !== "" && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UsersIcon className="size-3 shrink-0" />
|
||||
<span>BCC: {args.bcc}</span>
|
||||
<span>BCC: {pendingEdits?.bcc ?? args.bcc}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="px-5 pt-1">
|
||||
{args.subject != null && (
|
||||
<p className="text-sm font-medium text-foreground">{args.subject}</p>
|
||||
{(pendingEdits?.subject ?? args.subject) != null && (
|
||||
<p className="text-sm font-medium text-foreground">{pendingEdits?.subject ?? args.subject}</p>
|
||||
)}
|
||||
{args.body != null && (
|
||||
{(pendingEdits?.body ?? args.body) != null && (
|
||||
<div
|
||||
className="mt-2 max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -332,7 +334,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(args.body)}
|
||||
markdown={String(pendingEdits?.body ?? args.body)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -160,6 +160,10 @@ function ApprovalCard({
|
|||
const wasAlreadyDecided = interruptData.__decided__ != null;
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
||||
const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom);
|
||||
const [pendingEdits, setPendingEdits] = useState<{
|
||||
summary: string; description: string; start_datetime: string;
|
||||
end_datetime: string; location: string; attendees: string;
|
||||
} | 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<string, unknown> = {
|
||||
...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 (
|
||||
<div className="my-4 max-w-lg overflow-hidden rounded-2xl border bg-muted/30 transition-all duration-300">
|
||||
|
|
@ -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<string, unknown> = {
|
||||
...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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3 pb-3 space-y-2">
|
||||
{args.summary && (
|
||||
<p className="text-sm font-medium text-foreground">{args.summary}</p>
|
||||
{(pendingEdits?.summary ?? args.summary) && (
|
||||
<p className="text-sm font-medium text-foreground">{pendingEdits?.summary ?? args.summary}</p>
|
||||
)}
|
||||
|
||||
{(args.start_datetime || args.end_datetime) && (
|
||||
{((pendingEdits?.start_datetime ?? args.start_datetime) || (pendingEdits?.end_datetime ?? args.end_datetime)) && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<ClockIcon className="size-3.5 shrink-0" />
|
||||
<span>
|
||||
{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) : ""}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{args.location && (
|
||||
{(pendingEdits?.location ?? args.location) && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<MapPinIcon className="size-3.5 shrink-0" />
|
||||
<span>{args.location}</span>
|
||||
<span>{pendingEdits?.location ?? args.location}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{attendeesList.length > 0 && (
|
||||
{displayAttendees.length > 0 && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<UsersIcon className="size-3.5 shrink-0" />
|
||||
<span>{attendeesList.join(", ")}</span>
|
||||
<span>{displayAttendees.join(", ")}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{args.description && (
|
||||
{(pendingEdits?.description ?? args.description) && (
|
||||
<div
|
||||
className="mt-2 max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -423,7 +428,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(args.description)}
|
||||
markdown={String(pendingEdits?.description ?? args.description)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@ function ApprovalCard({
|
|||
const wasAlreadyDecided = interruptData.__decided__ != null;
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
||||
const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom);
|
||||
const [pendingEdits, setPendingEdits] = useState<{
|
||||
summary: string; description: string; start_datetime: string;
|
||||
end_datetime: string; location: string; attendees: string;
|
||||
} | null>(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<string, unknown> = {
|
||||
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,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3">
|
||||
{args.name != null && (
|
||||
<p className="text-sm font-medium text-foreground">{args.name}</p>
|
||||
{(pendingEdits?.name ?? args.name) != null && (
|
||||
<p className="text-sm font-medium text-foreground">{String(pendingEdits?.name ?? args.name)}</p>
|
||||
)}
|
||||
{args.content != null && (
|
||||
{(pendingEdits?.content ?? args.content) != null && (
|
||||
<div
|
||||
className="mt-2 max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -367,7 +356,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(args.content)}
|
||||
markdown={String(pendingEdits?.content ?? args.content)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ function ApprovalCard({
|
|||
const wasAlreadyDecided = interruptData.__decided__ != null;
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
||||
const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom);
|
||||
const [pendingEdits, setPendingEdits] = useState<{ title: string; description: string } | 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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3">
|
||||
{args.title != null && (
|
||||
<p className="text-sm font-medium text-foreground">{args.title}</p>
|
||||
{(pendingEdits?.title ?? args.title) != null && (
|
||||
<p className="text-sm font-medium text-foreground">{pendingEdits?.title ?? args.title}</p>
|
||||
)}
|
||||
{args.description != null && (
|
||||
{(pendingEdits?.description ?? args.description) != null && (
|
||||
<div
|
||||
className="max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -473,7 +468,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={args.description}
|
||||
markdown={pendingEdits?.description ?? args.description ?? ""}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ function ApprovalCard({
|
|||
const wasAlreadyDecided = interruptData.__decided__ != null;
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
||||
const [editedArgs, setEditedArgs] = useState(initialEditState);
|
||||
const [hasPanelEdits, setHasPanelEdits] = useState(false);
|
||||
const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom);
|
||||
|
||||
const reviewConfig = interruptData.review_configs[0];
|
||||
|
|
@ -255,15 +256,16 @@ function ApprovalCard({
|
|||
const handleApprove = useCallback(() => {
|
||||
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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3">
|
||||
{hasProposedChanges ? (
|
||||
{(hasProposedChanges || hasPanelEdits) ? (
|
||||
<>
|
||||
{actionArgs.new_title && (
|
||||
<p className="text-sm font-medium text-foreground">{String(actionArgs.new_title)}</p>
|
||||
{(hasPanelEdits ? editedArgs.title : actionArgs.new_title) && (
|
||||
<p className="text-sm font-medium text-foreground">{String(hasPanelEdits ? editedArgs.title : actionArgs.new_title)}</p>
|
||||
)}
|
||||
{actionArgs.new_description && (
|
||||
{(hasPanelEdits ? editedArgs.description : actionArgs.new_description) && (
|
||||
<div
|
||||
className="max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -550,7 +541,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(actionArgs.new_description)}
|
||||
markdown={String(hasPanelEdits ? editedArgs.description : actionArgs.new_description)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ function ApprovalCard({
|
|||
const wasAlreadyDecided = interruptData.__decided__ != null;
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
||||
const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom);
|
||||
const [pendingEdits, setPendingEdits] = useState<{ title: string; content: string } | null>(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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3">
|
||||
{args.title != null && (
|
||||
<p className="text-sm font-medium text-foreground">{String(args.title)}</p>
|
||||
{(pendingEdits?.title ?? args.title) != null && (
|
||||
<p className="text-sm font-medium text-foreground">{String(pendingEdits?.title ?? args.title)}</p>
|
||||
)}
|
||||
{args.content != null && (
|
||||
{(pendingEdits?.content ?? args.content) != null && (
|
||||
<div
|
||||
className="max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -336,7 +326,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(args.content)}
|
||||
markdown={String(pendingEdits?.content ?? args.content)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ function ApprovalCard({
|
|||
const wasAlreadyDecided = interruptData.__decided__ != null;
|
||||
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
||||
const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom);
|
||||
const [pendingEdits, setPendingEdits] = useState<{ content: string } | 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 */}
|
||||
<div className="mx-5 h-px bg-border/50" />
|
||||
<div className="px-5 pt-3">
|
||||
{args.content != null ? (
|
||||
{(pendingEdits?.content ?? args.content) != null ? (
|
||||
<div
|
||||
className="max-h-[7rem] overflow-hidden text-sm"
|
||||
style={{
|
||||
|
|
@ -265,7 +256,7 @@ function ApprovalCard({
|
|||
}}
|
||||
>
|
||||
<PlateEditor
|
||||
markdown={String(args.content)}
|
||||
markdown={String(pendingEdits?.content ?? args.content)}
|
||||
readOnly
|
||||
preset="readonly"
|
||||
editorVariant="none"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue