// * Packages import { Link, useForm } from "@inertiajs/react"; import { route } from "ziggy-js"; import { FormEvent, useState } from "react"; import { MoreHorizontalIcon } from "lucide-react"; // * Components shadcn import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { DropdownMenuSeparator } from "@/components/ui/dropdown-menu"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; // * Other import { useFormatedDate } from "@/hooks/use-formated-value"; import { useToast } from "@/hooks/use-toast"; import useTrans from "@/hooks/use-translations"; import str from "@/hooks/use-string"; import { Model, Views as ViewsType } from "@/back/types/global"; import { Views } from "@/lib/utils"; interface ModelActionsProps { actions?: ViewsType; modelRoute: string; model: Model; modelUpdatedAt?: string; inDialog?: boolean; message?: string; } export default function ModelActions({ actions = [], modelRoute, model, modelUpdatedAt = "", inDialog = false, message = "back.model_action_updated_at", }: ModelActionsProps) { const [open, setOpen] = useState(false); const messageTrans = useTrans(message); const formattedDate = useFormatedDate(modelUpdatedAt); const allowedActions = actions.filter((action) => model.policies[action.policy as keyof typeof model.policies]) const { delete: destroy, patch } = useForm(); const { toast } = useToast(); // Translations. const successfulArchivation = useTrans('crud.messages.successful_archivation'); const successfulArchivationDetails = useTrans("crud.messages.successful_archivation_details"); const canceledArchivation = useTrans('crud.messages.canceled_archivation'); const canceledArchivationDetails = useTrans("crud.messages.canceled_archivation_details"); const successfulRestoration = useTrans('crud.messages.successful_restoration'); const successfulRestorationDetails = useTrans("crud.messages.successful_restoration_details"); const canceledRestoration = useTrans('crud.messages.canceled_restoration'); const canceledRestorationDetails = useTrans("crud.messages.canceled_restoration_details"); const successfulDeletion = useTrans('crud.messages.successful_deletion'); const successfulDeletionDetails = useTrans("crud.messages.successful_deletion_details"); const canceledDeletion = useTrans('crud.messages.canceled_deletion'); const canceledDeletionDetails = useTrans("crud.messages.canceled_deletion_details"); /** * Action call on model. * @param event FormEvent * @param action ViewsType[number] */ const actionOnModel = (event: FormEvent, action: ViewsType[number]) => { event.preventDefault(); const routeAction = route(`back.${modelRoute}.${action.value}`, model.id); // All post actions mapping. const actionMap: Record = { [Views.ARCHIVATION.label]: { method: destroy, success: { title: successfulArchivation, description: successfulArchivationDetails, }, error: { title: canceledArchivation, description: canceledArchivationDetails, }, }, [Views.RESTORATION.label]: { method: patch, success: { title: successfulRestoration, description: successfulRestorationDetails, }, error: { title: canceledRestoration, description: canceledRestorationDetails, }, }, [Views.DELETION.label || Views.FORCEDELETION.label]: { method: destroy, success: { title: successfulDeletion, description: successfulDeletionDetails, }, error: { title: canceledDeletion, description: canceledDeletionDetails, }, }, }; // Check if route exist. const config = actionMap[action.label]; if (!config) { throw new Error(`Unknown action: ${action.label}`); } // Close the dropdown. setOpen(false); // Execute the call. config.method(routeAction, { preserveScroll: true, onSuccess: () => toast(config.success), onError: () => toast({ variant: "destructive", ...config.error }), }); }; /** * Get all action buttons. * @returns Element[] */ const AllBtnActions = () => { return allowedActions.map((action, index) => (
{ action.value === Views.ARCHIVATION.value || action.value === Views.RESTORATION.value || action.value === Views.DELETION.value || action.value === Views.FORCEDELETION.value ? inDialog && index != 0 ? ( <> ) : : }
)) } /** * Button for POST action. * @param action ViewsType[number] * @returns Element */ const PostAction = ({ action }: { action: ViewsType[number] }) => { const isRestoreAction = action.label === Views.RESTORATION.label; const isArchiveAction = action.label === Views.ARCHIVATION.label; const btnVariantType = isRestoreAction ? "ghost" : "destructive"; const btnConfirm = useTrans(isRestoreAction ? "back.yes_restore" : isArchiveAction ? "back.yes_archive" : "back.yes_delete"); const btnDesc = useTrans(isRestoreAction ? "back.restore_data" : isArchiveAction ? "back.trash_data" : "back.lost_data"); return ( {useTrans("back.are_you_sure")} {btnDesc}
actionOnModel(event, action)} className="w-fit mb-2 sm:mb-0" >
) } /** * Button for GET action. * @param action ViewsType[number] * @returns Element */ const GetAction = ({ action }: { action: ViewsType[number] }) => ( ) return (
{!inDialog && modelUpdatedAt.length > 0 && (

{messageTrans}

{formattedDate}
)} {Boolean(allowedActions.length) && (
{inDialog ? ( ) : ( )}
)}
); }