Files
pn-new-crm/src/components/documents/cancel-document-dialog.tsx

147 lines
5.2 KiB
TypeScript
Raw Normal View History

'use client';
import { useState } from 'react';
import { Loader2, XCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Textarea } from '@/components/ui/textarea';
export type CancelMode = 'delete' | 'keep_remote';
interface CancelDocumentDialogProps {
open: boolean;
onOpenChange: (next: boolean) => void;
/** Label used in the dialog ("Cancel reservation", "Cancel contract", "Cancel EOI"). */
documentLabel: string;
/** Fires when the rep confirms. Caller invokes the mutation with the
* chosen `cancelMode` (and optional reason). The dialog stays open
* until `onOpenChange(false)` is called by the parent - typically on
* mutation success/failure. */
onConfirm: (params: { cancelMode: CancelMode; reason: string }) => void;
/** When true, disables the confirm action + shows a spinner. */
isSubmitting?: boolean;
}
/**
* Cancel-confirm dialog with an explicit "what to do with Documenso?"
* choice. Default `'delete'` mirrors the prior behaviour - DELETE the
* upstream envelope to keep the Documenso log uncluttered. `keep_remote`
* leaves the envelope intact so admins can later inspect it for audit /
* forensics; only the local CRM row flips to `cancelled`.
*
* Used by the Reservation / Contract / EOI tabs (any signing-doc
* surface that exposes a Cancel CTA). Replaces the previous
* `useConfirmation()` flow which had no way to surface this choice.
*/
export function CancelDocumentDialog({
open,
onOpenChange,
documentLabel,
onConfirm,
isSubmitting = false,
}: CancelDocumentDialogProps) {
const [cancelMode, setCancelMode] = useState<CancelMode>('delete');
const [reason, setReason] = useState('');
function reset() {
setCancelMode('delete');
setReason('');
}
return (
<Dialog
open={open}
onOpenChange={(next) => {
if (!next) reset();
onOpenChange(next);
}}
>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Cancel {documentLabel.toLowerCase()}</DialogTitle>
<DialogDescription>
Signers will no longer be able to sign. Choose how to handle the document on Documenso.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<RadioGroup
value={cancelMode}
onValueChange={(value) => setCancelMode(value as CancelMode)}
className="gap-3"
>
<label
htmlFor="cancel-mode-delete"
className="flex cursor-pointer items-start gap-3 rounded-md border p-3 hover:bg-accent/40"
>
<RadioGroupItem id="cancel-mode-delete" value="delete" className="mt-0.5" />
<span className="space-y-0.5">
<span className="block text-sm font-medium">Delete from Documenso</span>
<span className="block text-xs text-muted-foreground">
Frees the envelope slot upstream. Use this when the draft was abandoned and the
upstream record is no longer useful.
</span>
</span>
</label>
<label
htmlFor="cancel-mode-keep"
className="flex cursor-pointer items-start gap-3 rounded-md border p-3 hover:bg-accent/40"
>
<RadioGroupItem id="cancel-mode-keep" value="keep_remote" className="mt-0.5" />
<span className="space-y-0.5">
<span className="block text-sm font-medium">Keep on Documenso for audit</span>
<span className="block text-xs text-muted-foreground">
Marks the local copy cancelled but leaves the envelope visible on Documenso so an
admin can review it later.
</span>
</span>
</label>
</RadioGroup>
<div className="space-y-1.5">
<Label htmlFor="cancel-reason" className="text-xs font-medium text-muted-foreground">
Reason (optional)
</Label>
<Textarea
id="cancel-reason"
value={reason}
onChange={(e) => setReason(e.target.value)}
rows={2}
placeholder="What changed? Inlined into the cancellation audit log."
className="text-sm"
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
Keep open
</Button>
<Button
variant="destructive"
onClick={() => onConfirm({ cancelMode, reason: reason.trim() })}
disabled={isSubmitting}
>
{isSubmitting ? (
<Loader2 className="mr-1.5 h-4 w-4 animate-spin" aria-hidden />
) : (
<XCircle className="mr-1.5 h-4 w-4" aria-hidden />
)}
Cancel {documentLabel.toLowerCase()}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}