"use client"; import { useRouter } from "next/navigation"; import { useTransition } from "react"; import { toast } from "sonner"; import type { ActionResult } from "@/lib/safe-action-shared"; interface UseServerActionOptions { /** Where to redirect after a successful action (optional) */ redirectOnSuccess?: string; } interface RunOptions { successMessage?: string; errorMessage?: string; /** Override redirect path for this specific call */ redirectTo?: string; /** Called on success with the result data. Overrides default router.refresh(). */ onSuccess?: (data: Record) => void; } /** * Hook that wraps a server action with loading state, toast notifications, * and optional redirect/refresh. Drop-in replacement for useAdminForm. * * @example * const { run, isPending } = useServerAction() * * // Submit * run(() => updateNews({ id, ...data }), { successMessage: 'Saved!' }) * * // Delete with redirect * run(() => deleteNews(id), { redirectTo: '/admin/news' }) */ export function useServerAction(options: UseServerActionOptions = {}) { const [isPending, startTransition] = useTransition(); const router = useRouter(); function run(action: () => Promise, opts: RunOptions = {}) { const { successMessage = "Saved successfully.", errorMessage = "An error occurred.", redirectTo, onSuccess, } = opts; startTransition(async () => { try { const result = (await action()) as ActionResult | undefined; if (!result || result.ok) { toast.success(successMessage); if (onSuccess) { onSuccess((result?.data ?? {}) as Record); } else { const target = redirectTo || options.redirectOnSuccess; if (target) router.push(target); else router.refresh(); } } else { toast.error(result.error || errorMessage); } } catch (err) { console.error("[useServerAction] Unexpected error:", err); toast.error(errorMessage); } }); } return { run, isPending }; }