"use client";
import { useActionState, useRef, useState, type ReactNode } from "react";
import { AlertDialog, Spinner, useToast } from "@unlocalhosted/metalui";
import type { FormState } from "@/app/actions/types";
import { Button, type ButtonVariant } from "./ui";
/**
* One button that runs a server action, with its outcome on the toast deck: for actions that fail
* for reasons worth reading (a conflict, a branch rule). With `confirm`, it asks first, like
* ConfirmButton.
*/
export function ActionButton({
action,
children,
icon,
variant = "default",
className = "",
confirm,
}: {
action: (prev: FormState, formData: FormData) => Promise<FormState>;
children: ReactNode;
icon?: ReactNode;
variant?: ButtonVariant;
className?: string;
confirm?: { title: string; description: ReactNode; label: ReactNode; destructive?: boolean };
}) {
const toast = useToast();
const form = useRef<HTMLFormElement>(null);
const [open, setOpen] = useState(false);
const [, formAction, pending] = useActionState(async (prev: FormState, formData: FormData) => {
const result = await action(prev, formData);
if (result?.error) toast.show({ title: result.error, tone: "error" });
else if (result?.success) toast.show({ title: result.success, tone: "success" });
return result;
}, undefined);
return (
<form ref={form} action={formAction} className={className}>
<Button
type={confirm ? "button" : "submit"}
onClick={confirm ? () => setOpen(true) : undefined}
variant={variant}
disabled={pending}
aria-busy={pending || undefined}
icon={pending ? <Spinner size="small" label="Working" /> : icon}
>
{children}
</Button>
{confirm && (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialog.Popup>
<AlertDialog.Title>{confirm.title}</AlertDialog.Title>
<AlertDialog.Description>{confirm.description}</AlertDialog.Description>
<AlertDialog.Actions>
<AlertDialog.Cancel />
<AlertDialog.Confirm tone={confirm.destructive || variant === "danger" ? "destructive" : "primary"} onClick={() => form.current?.requestSubmit()}>
{confirm.label}
</AlertDialog.Confirm>
</AlertDialog.Actions>
</AlertDialog.Popup>
</AlertDialog>
)}
</form>
);
}