Uecko_ERP/modules/customer-invoices/src/web/pages/update/update-commit-button-group.tsx
2025-10-19 21:04:16 +02:00

155 lines
4.5 KiB
TypeScript

import {
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@repo/shadcn-ui/components";
import { cn } from "@repo/shadcn-ui/lib/utils";
import {
ArrowLeftIcon,
CopyIcon,
EyeIcon,
MoreHorizontalIcon,
RotateCcwIcon,
Trash2Icon,
} from "lucide-react";
import { useFormContext } from "react-hook-form";
import { CancelFormButton, CancelFormButtonProps } from "./cancel-form-button";
import { SubmitButtonProps, SubmitFormButton } from "./submit-form-button";
type Align = "start" | "center" | "end" | "between";
type GroupSubmitButtonProps = Omit<SubmitButtonProps, "isLoading" | "preventDoubleSubmit">;
export type FormCommitButtonGroupProps = {
className?: string;
align?: Align; // default "end"
gap?: string; // default "gap-2"
reverseOrderOnMobile?: boolean; // default true (Cancel debajo en móvil)
isLoading?: boolean;
disabled?: boolean;
preventDoubleSubmit?: boolean; // Evita múltiples submits mientras loading
cancel?: CancelFormButtonProps & { show?: boolean };
submit?: GroupSubmitButtonProps; // props directas a SubmitButton
onReset?: () => void;
onDelete?: () => void;
onPreview?: () => void;
onDuplicate?: () => void;
onBack?: () => void;
};
const alignToJustify: Record<Align, string> = {
start: "justify-start",
center: "justify-center",
end: "justify-end",
between: "justify-between",
};
export const FormCommitButtonGroup = ({
className,
align = "end",
gap = "gap-2",
reverseOrderOnMobile = true,
isLoading,
disabled = false,
preventDoubleSubmit = true,
cancel,
submit,
onReset,
onDelete,
onPreview,
onDuplicate,
onBack,
}: FormCommitButtonGroupProps) => {
const showCancel = cancel?.show ?? true;
const hasSecondaryActions = onReset || onPreview || onDuplicate || onBack || onDelete;
// ⛳️ RHF opcional: auto-detectar isSubmitting si no se pasó isLoading
let rhfIsSubmitting = false;
try {
const ctx = useFormContext();
rhfIsSubmitting = !!ctx?.formState?.isSubmitting;
} catch {
// No hay provider de RHF; ignorar
}
const busy = isLoading ?? rhfIsSubmitting;
const computedDisabled = !!(disabled || (preventDoubleSubmit && busy));
return (
<div
className={cn(
"flex",
reverseOrderOnMobile ? "flex-col-reverse sm:flex-row" : "flex-row",
alignToJustify[align],
gap,
className
)}
>
{submit && <SubmitFormButton {...submit} />}
{showCancel && <CancelFormButton {...cancel} />}
{/* Menú de acciones adicionales */}
{hasSecondaryActions && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='ghost' size='sm' disabled={computedDisabled} className='px-2'>
<MoreHorizontalIcon className='h-4 w-4' />
<span className='sr-only'>Más acciones</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='end' className='w-48'>
{onReset && (
<DropdownMenuItem
onClick={onReset}
disabled={computedDisabled}
className='text-muted-foreground'
>
<RotateCcwIcon className='mr-2 h-4 w-4' />
Deshacer cambios
</DropdownMenuItem>
)}
{onPreview && (
<DropdownMenuItem onClick={onPreview} className='text-muted-foreground'>
<EyeIcon className='mr-2 h-4 w-4' />
Vista previa
</DropdownMenuItem>
)}
{onDuplicate && (
<DropdownMenuItem onClick={onDuplicate} className='text-muted-foreground'>
<CopyIcon className='mr-2 h-4 w-4' />
Duplicar
</DropdownMenuItem>
)}
{onBack && (
<DropdownMenuItem onClick={onBack} className='text-muted-foreground'>
<ArrowLeftIcon className='mr-2 h-4 w-4' />
Volver
</DropdownMenuItem>
)}
{onDelete && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={onDelete}
className='text-destructive focus:text-destructive'
>
<Trash2Icon className='mr-2 h-4 w-4' />
Eliminar
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
);
};