diff --git a/src/components/frontend/transfers/TransferActionDialog.tsx b/src/components/frontend/transfers/TransferActionDialog.tsx new file mode 100644 index 0000000..4a407f0 --- /dev/null +++ b/src/components/frontend/transfers/TransferActionDialog.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { useEffect, useId, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; + +export interface TransferActionResult { + readonly success: boolean; + readonly error?: string; +} + +interface TransferActionDialogProps { + readonly open: boolean; + readonly onOpenChange: (open: boolean) => void; + readonly title: string; + readonly description: string; + readonly label: string; + readonly placeholder: string; + readonly submitLabel: string; + readonly pendingLabel: string; + readonly successMessage: string; + readonly required?: boolean; + readonly onSubmit: (note: string) => Promise; + readonly onSuccess?: () => Promise; +} + +export function TransferActionDialog({ + open, + onOpenChange, + title, + description, + label, + placeholder, + submitLabel, + pendingLabel, + successMessage, + required = false, + onSubmit, + onSuccess, +}: TransferActionDialogProps) { + const noteId = useId(); + const [note, setNote] = useState(""); + const [pending, setPending] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (!open) { + setNote(""); + setError(null); + setPending(false); + } + }, [open]); + + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + const trimmedNote = note.trim(); + if (required && !trimmedNote) { + setError("Add a reason before continuing."); + return; + } + + setPending(true); + setError(null); + try { + const result = await onSubmit(trimmedNote); + if (!result.success) { + setError(result.error ?? "Could not complete this transfer action."); + return; + } + + toast.success(successMessage); + await onSuccess?.(); + onOpenChange(false); + } catch (caughtError) { + setError( + caughtError instanceof Error + ? caughtError.message + : "Could not complete this transfer action.", + ); + } finally { + setPending(false); + } + } + + return ( + + + + {title} + {description} + +
+
+ +