import React, { useMemo, useState } from 'react'; import { ChevronDown, ShieldCheck } from 'lucide-react'; import { useIsProposeChangesModalOpen, useProposeChangesRequest, useUISelector, } from '../../contexts/UIContext'; import { ASSISTANT_BRAND_NAME } from '../../constants'; import { Button, ModalShell } from '../ui'; import { normalizeV1ApprovalRequestView, normalizeWriteApprovalView } from '../../utils/approvalViewModels'; import ApprovalItemList from './ApprovalItemList'; const prettyJson = (value: unknown) => { try { return JSON.stringify(value, null, 2); } catch { return String(value); } }; const ProposeChangesModal = function ProposeChangesModal() { const open = useIsProposeChangesModalOpen(); const req = useProposeChangesRequest(); const resolve = useUISelector((state) => state.resolveProposeChanges); const [alternative, setAlternative] = useState(''); const [showAlternativeError, setShowAlternativeError] = useState(false); const view = useMemo(() => { const record = req as Record | null; if (record && record.protocol === 'v1') { return normalizeV1ApprovalRequestView(record); } return normalizeWriteApprovalView(req); }, [req]); const reset = () => { setAlternative(''); setShowAlternativeError(false); }; const resolveDecision = (approved: boolean, userReason?: string) => { resolve({ approved, user_reason: approved ? undefined : (userReason?.trim() || undefined), }); reset(); }; const submitAlternative = () => { const trimmed = alternative.trim(); if (!trimmed) { setShowAlternativeError(true); return; } resolveDecision(false, trimmed); }; return ( resolveDecision(false)} footer={(
)} >
Permission required {view.operationCount} operation{view.operationCount === 1 ? '' : 's'}

{view.goal}

{view.approvalDescription}

{view.items.length > 0 ? ( ) : (

No operation details were provided.

)} {view.checks.length > 0 && (

Checks after approval

)}

Prefer a different approach?

{`Reject and tell ${ASSISTANT_BRAND_NAME} what to do instead.`}

{ setAlternative(event.target.value); setShowAlternativeError(false); }} placeholder="e.g. Save it as a draft instead" className="min-w-0 flex-1 rounded-md border border-app-border bg-app-surface px-3 py-2 text-sm text-app-text outline-none transition placeholder:text-app-muted focus:border-brand focus:ring-2 focus:ring-brand/20" />
{showAlternativeError && (

Add instructions before sending.

)}
Technical details
            {prettyJson(view.technicalDetails ?? view.rawRequest)}
          
); }; export default React.memo(ProposeChangesModal);