|
16 | 16 | import { normalizeCanvasSpec } from "./canvasSpec"; |
17 | 17 | import { |
18 | 18 | classifyOutboundSendError, |
19 | | - connectorDraftStateForStatus |
| 19 | + connectorDraftStateForStatus, |
| 20 | + DUPLICATE_RISK_ACK_COPY |
20 | 21 | } from "./connectorDraftStatus"; |
21 | 22 |
|
22 | 23 | type Props = { |
|
1159 | 1160 | if (connectorDraftSendState === "sent") return "Sent"; |
1160 | 1161 | if (connectorDraftSendState === "cancelled") return "Cancelled"; |
1161 | 1162 | if (connectorDraftSendState === "expired") return "Expired"; |
| 1163 | + if (connectorDraftSendState === "uncertain") return "Confirm no duplicate & retry"; |
1162 | 1164 | return "Approve and send"; |
1163 | 1165 | } |
1164 | 1166 |
|
|
1213 | 1215 | }); |
1214 | 1216 |
|
1215 | 1217 | async function sendConnectorDraft(draft: ConnectorDraftRender) { |
1216 | | - // `uncertain` blocks approve too: the server demands duplicate_risk_ack and |
1217 | | - // the operator must cancel (still allowed) or resolve out-of-band first. |
1218 | | - if ( |
1219 | | - ["sending", "cancelling", "sent", "cancelled", "expired", "uncertain"].includes( |
1220 | | - connectorDraftSendState |
1221 | | - ) |
1222 | | - ) |
1223 | | - return; |
| 1218 | + const isDuplicateRiskRetry = connectorDraftSendState === "uncertain"; |
| 1219 | + if (["sending", "cancelling", "sent", "cancelled", "expired"].includes(connectorDraftSendState)) return; |
| 1220 | + if (isDuplicateRiskRetry) { |
| 1221 | + const confirmed = window.confirm(DUPLICATE_RISK_ACK_COPY); |
| 1222 | + if (!confirmed) return; |
| 1223 | + } |
1224 | 1224 | connectorDraftSendState = "sending"; |
1225 | 1225 | connectorDraftSendError = ""; |
1226 | 1226 | try { |
| 1227 | + const version = isDuplicateRiskRetry |
| 1228 | + ? (await outboundActionStatus({ |
| 1229 | + actionId: draft.draftId, |
| 1230 | + version: draft.version |
| 1231 | + })).version |
| 1232 | + : draft.version; |
1227 | 1233 | const result = await executeOutboundAction({ |
1228 | 1234 | actionId: draft.draftId, |
1229 | | - version: draft.version, |
| 1235 | + version, |
1230 | 1236 | approvedMessage: draft.message, |
1231 | | - clientRequestId: clientRequestId(draft.draftId) |
| 1237 | + clientRequestId: clientRequestId(draft.draftId), |
| 1238 | + duplicateRiskAck: isDuplicateRiskRetry |
1232 | 1239 | }); |
1233 | 1240 | applyConnectorDraftStatus(result.status); |
1234 | 1241 | if (result.status !== "sent") { |
|
1338 | 1345 | class="sc-btn pf-connector-draft-send" |
1339 | 1346 | data-size="sm" |
1340 | 1347 | disabled={connectorDraftIsBusy() || |
1341 | | - connectorDraftIsTerminal() || |
1342 | | - connectorDraftSendState === "uncertain"} |
| 1348 | + connectorDraftIsTerminal()} |
1343 | 1349 | onclick={() => void sendConnectorDraft(toolRender)} |
1344 | 1350 | > |
1345 | 1351 | <Icon name={connectorDraftPrimaryIcon()} size={12} /> |
|
1797 | 1803 | .pf-connector-draft-send:disabled { |
1798 | 1804 | opacity: 0.72; |
1799 | 1805 | } |
| 1806 | + .pf-connector-draft[data-state="uncertain"] .pf-connector-draft-send { |
| 1807 | + border-color: color-mix(in oklab, var(--destructive) 70%, var(--border)); |
| 1808 | + background: color-mix(in oklab, var(--destructive) 88%, black); |
| 1809 | + color: white; |
| 1810 | + } |
| 1811 | + .pf-connector-draft[data-state="uncertain"] .pf-connector-draft-send:hover:not(:disabled) { |
| 1812 | + background: color-mix(in oklab, var(--destructive) 78%, black); |
| 1813 | + } |
1800 | 1814 | .pf-connector-draft-cancel { |
1801 | 1815 | color: var(--muted-foreground); |
1802 | 1816 | } |
|
0 commit comments