v0.10.1
This commit is contained in:
parent
84bb21e527
commit
10a5c25fc1
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/factuges-server",
|
"name": "@erp/factuges-server",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup src/index.ts --config tsup.config.ts",
|
"build": "tsup src/index.ts --config tsup.config.ts",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/factuges-web",
|
"name": "@erp/factuges-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/auth",
|
"name": "@erp/auth",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/catalogs",
|
"name": "@erp/catalogs",
|
||||||
"description": "Catalogs module",
|
"description": "Catalogs module",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/companies",
|
"name": "@erp/companies",
|
||||||
"description": "Companies module",
|
"description": "Companies module",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/core",
|
"name": "@erp/core",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/customer-invoices",
|
"name": "@erp/customer-invoices",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -193,6 +193,27 @@
|
|||||||
"title": "Customer proformas",
|
"title": "Customer proformas",
|
||||||
"description": "List non-archived and non-deleted customer proformas",
|
"description": "List non-archived and non-deleted customer proformas",
|
||||||
"loadErrorTitle": "Unable to load the proformas list",
|
"loadErrorTitle": "Unable to load the proformas list",
|
||||||
|
"actions": {
|
||||||
|
"view": "View",
|
||||||
|
"edit": "Edit",
|
||||||
|
"change_status": "Change status",
|
||||||
|
"issue": "Issue invoice",
|
||||||
|
"duplicate": "Duplicate",
|
||||||
|
"archive": "Archive",
|
||||||
|
"unarchive": "Unarchive",
|
||||||
|
"download_pdf": "Download PDF",
|
||||||
|
"delete": "Delete",
|
||||||
|
"view_linked_invoice": "View linked invoice",
|
||||||
|
"disabled_reasons": {
|
||||||
|
"archived_scope": "This action is not available from the archived proformas list.",
|
||||||
|
"deleted_scope": "This action is not available from the deleted proformas list.",
|
||||||
|
"archived_only": "This action is only available for archived proformas.",
|
||||||
|
"edit_issued": "Issued proformas cannot be edited.",
|
||||||
|
"status_issued": "Issued proformas do not allow status changes.",
|
||||||
|
"linked_invoice_unavailable": "This action is only available when the issued proforma has a linked invoice.",
|
||||||
|
"operation_in_progress": "The operation is already in progress."
|
||||||
|
}
|
||||||
|
},
|
||||||
"empty": {
|
"empty": {
|
||||||
"normal": "There are no proformas.",
|
"normal": "There are no proformas.",
|
||||||
"archived": "There are no archived proformas.",
|
"archived": "There are no archived proformas.",
|
||||||
|
|||||||
@ -194,6 +194,27 @@
|
|||||||
"title": "Proformas",
|
"title": "Proformas",
|
||||||
"description": "Lista las proformas no archivadas ni eliminadas",
|
"description": "Lista las proformas no archivadas ni eliminadas",
|
||||||
"loadErrorTitle": "No se pudo cargar el listado de proformas",
|
"loadErrorTitle": "No se pudo cargar el listado de proformas",
|
||||||
|
"actions": {
|
||||||
|
"view": "Ver",
|
||||||
|
"edit": "Editar",
|
||||||
|
"change_status": "Cambiar estado",
|
||||||
|
"issue": "Emitir factura",
|
||||||
|
"duplicate": "Duplicar",
|
||||||
|
"archive": "Archivar",
|
||||||
|
"unarchive": "Desarchivar",
|
||||||
|
"download_pdf": "Descargar PDF",
|
||||||
|
"delete": "Eliminar",
|
||||||
|
"view_linked_invoice": "Ver factura vinculada",
|
||||||
|
"disabled_reasons": {
|
||||||
|
"archived_scope": "No se puede ejecutar desde el listado de proformas archivadas.",
|
||||||
|
"deleted_scope": "No se puede ejecutar desde el listado de proformas eliminadas.",
|
||||||
|
"archived_only": "Solo está disponible en proformas archivadas.",
|
||||||
|
"edit_issued": "No se puede editar una proforma emitida.",
|
||||||
|
"status_issued": "Una proforma emitida no admite cambios de estado.",
|
||||||
|
"linked_invoice_unavailable": "Solo está disponible cuando la proforma emitida tiene una factura vinculada.",
|
||||||
|
"operation_in_progress": "La operación está en curso."
|
||||||
|
}
|
||||||
|
},
|
||||||
"empty": {
|
"empty": {
|
||||||
"normal": "No hay proformas.",
|
"normal": "No hay proformas.",
|
||||||
"archived": "No hay proformas archivadas.",
|
"archived": "No hay proformas archivadas.",
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
import type { ColumnDef } from "@tanstack/react-table";
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
import {
|
import {
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
|
EyeIcon,
|
||||||
ExternalLinkIcon,
|
ExternalLinkIcon,
|
||||||
FileDownIcon,
|
FileDownIcon,
|
||||||
FileTextIcon,
|
FileTextIcon,
|
||||||
@ -44,7 +45,6 @@ import { ProformaStatusBadge } from "../../components";
|
|||||||
|
|
||||||
type GridActionHandlers = {
|
type GridActionHandlers = {
|
||||||
onViewClick?: (proforma: ProformaListRow) => void;
|
onViewClick?: (proforma: ProformaListRow) => void;
|
||||||
onPreviewClick?: (proforma: ProformaListRow) => void;
|
|
||||||
onEditClick?: (proforma: ProformaListRow) => void;
|
onEditClick?: (proforma: ProformaListRow) => void;
|
||||||
onIssueClick?: (proforma: ProformaListRow) => void;
|
onIssueClick?: (proforma: ProformaListRow) => void;
|
||||||
onArchiveClick?: (proforma: ProformaListRow) => void;
|
onArchiveClick?: (proforma: ProformaListRow) => void;
|
||||||
@ -64,6 +64,29 @@ type GridActionHandlers = {
|
|||||||
isPdfDownloading?: boolean;
|
isPdfDownloading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type RowAction = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
disabled: boolean;
|
||||||
|
disabledReason?: string;
|
||||||
|
destructive?: boolean;
|
||||||
|
loading?: boolean;
|
||||||
|
href?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopRowPropagation = (event: React.MouseEvent | React.KeyboardEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopRowPropagationAndPreventDefault = (
|
||||||
|
event: React.MouseEvent | React.KeyboardEvent
|
||||||
|
) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
export function useProformasGridColumns(
|
export function useProformasGridColumns(
|
||||||
scope: ProformaListScope,
|
scope: ProformaListScope,
|
||||||
actionHandlers: GridActionHandlers = {}
|
actionHandlers: GridActionHandlers = {}
|
||||||
@ -297,16 +320,8 @@ export function useProformasGridColumns(
|
|||||||
? (PROFORMA_STATUS_TRANSITIONS[proforma.status as ProformaStatus] ?? [])
|
? (PROFORMA_STATUS_TRANSITIONS[proforma.status as ProformaStatus] ?? [])
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const canEdit = isNormalScope && !isIssued && actionHandlers.onEditClick;
|
|
||||||
const canView = !isDeletedScope && actionHandlers.onViewClick;
|
|
||||||
const canDuplicate = !isDeletedScope && actionHandlers.onDuplicateClick;
|
|
||||||
const canIssue = isNormalScope && !isIssued && isApproved && actionHandlers.onIssueClick;
|
|
||||||
const canDelete = !isDeletedScope && isDraft && actionHandlers.onDeleteClick;
|
|
||||||
const canDownloadPdf = !isDeletedScope && actionHandlers.onDownloadPdf;
|
|
||||||
|
|
||||||
const isPDFLoading =
|
const isPDFLoading =
|
||||||
actionHandlers.isPdfDownloading && actionHandlers.pdfDownloadingId === proforma.id;
|
actionHandlers.isPdfDownloading && actionHandlers.pdfDownloadingId === proforma.id;
|
||||||
const stop = (e: React.MouseEvent | React.KeyboardEvent) => e.stopPropagation();
|
|
||||||
const nextTransition = availableTransitions[0] ?? null;
|
const nextTransition = availableTransitions[0] ?? null;
|
||||||
|
|
||||||
const handleView = () => actionHandlers.onViewClick?.(proforma);
|
const handleView = () => actionHandlers.onViewClick?.(proforma);
|
||||||
@ -325,263 +340,259 @@ export function useProformasGridColumns(
|
|||||||
actionHandlers.onChangeStatusClick?.(proforma, nextTransition);
|
actionHandlers.onChangeStatusClick?.(proforma, nextTransition);
|
||||||
};
|
};
|
||||||
|
|
||||||
const primaryAction = (() => {
|
const getScopeReason = () => {
|
||||||
if (isIssued && proforma.linkedInvoiceId) {
|
if (isArchivedScope) {
|
||||||
return {
|
return t("pages.proformas.list.actions.disabled_reasons.archived_scope");
|
||||||
href: `/issed-invoices/${proforma.linkedInvoiceId}`,
|
|
||||||
icon: ExternalLinkIcon,
|
|
||||||
label: "Ver factura",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canIssue) {
|
if (isDeletedScope) {
|
||||||
return {
|
return t("pages.proformas.list.actions.disabled_reasons.deleted_scope");
|
||||||
icon: FileTextIcon,
|
|
||||||
label: "Emitir factura",
|
|
||||||
onClick: handleIssue,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUnarchivable) {
|
return undefined;
|
||||||
return {
|
};
|
||||||
icon: Undo2Icon,
|
|
||||||
label: "Desarchivar",
|
|
||||||
onClick: handleUnarchive,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDraft && nextTransition && actionHandlers.onChangeStatusClick) {
|
const visibleActions: RowAction[] = [
|
||||||
return {
|
{
|
||||||
icon: RefreshCwIcon,
|
key: "view",
|
||||||
label: "Cambiar estado",
|
label: t("pages.proformas.list.actions.view"),
|
||||||
onClick: handleChangeStatus,
|
icon: EyeIcon,
|
||||||
};
|
disabled: !actionHandlers.onViewClick || isDeletedScope,
|
||||||
}
|
disabledReason: isDeletedScope
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.deleted_scope")
|
||||||
|
: undefined,
|
||||||
|
onClick: handleView,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "edit",
|
||||||
|
label: t("pages.proformas.list.actions.edit"),
|
||||||
|
icon: PencilIcon,
|
||||||
|
disabled: !actionHandlers.onEditClick || !isNormalScope || isIssued,
|
||||||
|
disabledReason: !isNormalScope
|
||||||
|
? getScopeReason()
|
||||||
|
: isIssued
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.edit_issued")
|
||||||
|
: undefined,
|
||||||
|
onClick: handleEdit,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if (canEdit) {
|
const menuActions: RowAction[] = [
|
||||||
return {
|
{
|
||||||
icon: PencilIcon,
|
key: "linked-invoice",
|
||||||
label: "Editar",
|
label: t("pages.proformas.list.actions.view_linked_invoice"),
|
||||||
onClick: handleEdit,
|
icon: ExternalLinkIcon,
|
||||||
};
|
disabled: !isIssued || !proforma.linkedInvoiceId,
|
||||||
}
|
disabledReason:
|
||||||
|
!isIssued || !proforma.linkedInvoiceId
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.linked_invoice_unavailable")
|
||||||
|
: undefined,
|
||||||
|
href: proforma.linkedInvoiceId
|
||||||
|
? `/issed-invoices/${proforma.linkedInvoiceId}`
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "change-status",
|
||||||
|
label: t("pages.proformas.list.actions.change_status"),
|
||||||
|
icon: RefreshCwIcon,
|
||||||
|
disabled:
|
||||||
|
!actionHandlers.onChangeStatusClick || !isNormalScope || !nextTransition || isIssued,
|
||||||
|
disabledReason: !isNormalScope
|
||||||
|
? getScopeReason()
|
||||||
|
: isIssued
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.status_issued")
|
||||||
|
: !nextTransition
|
||||||
|
? t("proformas.change_proforma_status_dialog.empty_available_statuses")
|
||||||
|
: undefined,
|
||||||
|
onClick: handleChangeStatus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "issue",
|
||||||
|
label: t("pages.proformas.list.actions.issue"),
|
||||||
|
icon: FileTextIcon,
|
||||||
|
disabled: !actionHandlers.onIssueClick || !isNormalScope || !isApproved || isIssued,
|
||||||
|
disabledReason: !isNormalScope
|
||||||
|
? getScopeReason()
|
||||||
|
: !isApproved
|
||||||
|
? t("proformas.issue_proforma_dialog.not_allowed_description")
|
||||||
|
: undefined,
|
||||||
|
onClick: handleIssue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "duplicate",
|
||||||
|
label: t("pages.proformas.list.actions.duplicate"),
|
||||||
|
icon: CopyIcon,
|
||||||
|
disabled: !actionHandlers.onDuplicateClick || isDeletedScope,
|
||||||
|
disabledReason: isDeletedScope
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.deleted_scope")
|
||||||
|
: undefined,
|
||||||
|
onClick: handleDuplicate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "archive",
|
||||||
|
label: t("pages.proformas.list.actions.archive"),
|
||||||
|
icon: FolderArchiveIcon,
|
||||||
|
disabled: !actionHandlers.onArchiveClick || !isArchivable,
|
||||||
|
disabledReason: !actionHandlers.onArchiveClick
|
||||||
|
? undefined
|
||||||
|
: !isNormalScope
|
||||||
|
? getScopeReason()
|
||||||
|
: t("proformas.archive.error_conflict_message"),
|
||||||
|
onClick: handleArchive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "unarchive",
|
||||||
|
label: t("pages.proformas.list.actions.unarchive"),
|
||||||
|
icon: Undo2Icon,
|
||||||
|
disabled: !actionHandlers.onUnarchiveClick || !isUnarchivable,
|
||||||
|
disabledReason: !actionHandlers.onUnarchiveClick
|
||||||
|
? undefined
|
||||||
|
: !isArchivedScope
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.archived_only")
|
||||||
|
: t("proformas.unarchive.error_conflict_message"),
|
||||||
|
onClick: handleUnarchive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "download-pdf",
|
||||||
|
label: t("pages.proformas.list.actions.download_pdf"),
|
||||||
|
icon: FileDownIcon,
|
||||||
|
disabled: !actionHandlers.onDownloadPdf || isDeletedScope || isPDFLoading,
|
||||||
|
disabledReason: isDeletedScope
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.deleted_scope")
|
||||||
|
: isPDFLoading
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.operation_in_progress")
|
||||||
|
: undefined,
|
||||||
|
loading: isPDFLoading,
|
||||||
|
onClick: handleDownloadPdf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "delete",
|
||||||
|
label: t("pages.proformas.list.actions.delete"),
|
||||||
|
icon: Trash2Icon,
|
||||||
|
destructive: true,
|
||||||
|
disabled: !actionHandlers.onDeleteClick || isDeletedScope || !isDraft,
|
||||||
|
disabledReason: isDeletedScope
|
||||||
|
? t("pages.proformas.list.actions.disabled_reasons.deleted_scope")
|
||||||
|
: !isDraft
|
||||||
|
? t("proformas.delete_proforma_dialog.error_conflict_message")
|
||||||
|
: undefined,
|
||||||
|
onClick: handleDelete,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if (nextTransition && actionHandlers.onChangeStatusClick) {
|
const renderIconButton = (action: RowAction) => {
|
||||||
return {
|
const button = (
|
||||||
icon: RefreshCwIcon,
|
<Button
|
||||||
label: "Cambiar estado",
|
aria-label={action.label}
|
||||||
onClick: handleChangeStatus,
|
className="size-8 text-muted-foreground hover:text-foreground"
|
||||||
};
|
disabled={action.disabled}
|
||||||
}
|
onClick={(event) => {
|
||||||
|
stopRowPropagationAndPreventDefault(event);
|
||||||
|
if (!action.disabled) {
|
||||||
|
action.onClick?.();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size="icon-sm"
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
<action.icon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
if (isArchivable && actionHandlers.onArchiveClick) {
|
return (
|
||||||
return {
|
<Tooltip key={action.key}>
|
||||||
icon: FolderArchiveIcon,
|
<TooltipTrigger
|
||||||
label: "Archivar",
|
render={action.disabled ? <span className="inline-flex">{button}</span> : button}
|
||||||
onClick: handleArchive,
|
/>
|
||||||
};
|
<TooltipContent>{action.disabledReason ?? action.label}</TooltipContent>
|
||||||
}
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return null;
|
const renderMenuItem = (action: RowAction) => {
|
||||||
})();
|
const item = (
|
||||||
|
<DropdownMenuItem
|
||||||
|
aria-label={action.label}
|
||||||
|
className={
|
||||||
|
action.destructive ? "text-destructive focus:text-destructive" : undefined
|
||||||
|
}
|
||||||
|
disabled={action.disabled}
|
||||||
|
onClick={(event) => {
|
||||||
|
stopRowPropagationAndPreventDefault(event);
|
||||||
|
if (action.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const quickAction = (() => {
|
if (action.href) {
|
||||||
if (primaryAction?.label !== "Editar" && canEdit) {
|
window.location.href = action.href;
|
||||||
return {
|
return;
|
||||||
icon: PencilIcon,
|
}
|
||||||
label: "Editar",
|
|
||||||
loading: false,
|
|
||||||
onClick: handleEdit,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
action.onClick?.();
|
||||||
})();
|
}}
|
||||||
|
variant={action.destructive ? "destructive" : "default"}
|
||||||
|
>
|
||||||
|
{action.loading ? (
|
||||||
|
<Spinner className="mr-2 size-4" />
|
||||||
|
) : (
|
||||||
|
<action.icon className="mr-2 size-4" />
|
||||||
|
)}
|
||||||
|
<span>{action.label}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
|
||||||
const menuActions = [
|
return (
|
||||||
canView
|
<Tooltip key={action.key}>
|
||||||
? {
|
<TooltipTrigger render={action.disabled ? <div className="w-full">{item}</div> : item} />
|
||||||
icon: ExternalLinkIcon,
|
<TooltipContent side="left">{action.disabledReason ?? action.label}</TooltipContent>
|
||||||
label: "Ver",
|
</Tooltip>
|
||||||
onClick: handleView,
|
);
|
||||||
}
|
};
|
||||||
: null,
|
|
||||||
!isIssued &&
|
|
||||||
nextTransition &&
|
|
||||||
actionHandlers.onChangeStatusClick &&
|
|
||||||
primaryAction?.label !== "Cambiar estado"
|
|
||||||
? {
|
|
||||||
icon: RefreshCwIcon,
|
|
||||||
label: "Cambiar estado",
|
|
||||||
onClick: handleChangeStatus,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
canIssue && primaryAction?.label !== "Emitir factura"
|
|
||||||
? {
|
|
||||||
icon: FileTextIcon,
|
|
||||||
label: "Emitir factura",
|
|
||||||
onClick: handleIssue,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
canEdit && primaryAction?.label !== "Editar" && quickAction?.label !== "Editar"
|
|
||||||
? {
|
|
||||||
icon: PencilIcon,
|
|
||||||
label: "Editar",
|
|
||||||
onClick: handleEdit,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
canDuplicate
|
|
||||||
? {
|
|
||||||
icon: CopyIcon,
|
|
||||||
label: "Duplicar",
|
|
||||||
onClick: handleDuplicate,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
isArchivable && actionHandlers.onArchiveClick && primaryAction?.label !== "Archivar"
|
|
||||||
? {
|
|
||||||
icon: FolderArchiveIcon,
|
|
||||||
label: "Archivar",
|
|
||||||
onClick: handleArchive,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
isUnarchivable &&
|
|
||||||
actionHandlers.onUnarchiveClick &&
|
|
||||||
primaryAction?.label !== "Desarchivar"
|
|
||||||
? {
|
|
||||||
icon: Undo2Icon,
|
|
||||||
label: "Desarchivar",
|
|
||||||
onClick: handleUnarchive,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
canDownloadPdf
|
|
||||||
? {
|
|
||||||
icon: FileDownIcon,
|
|
||||||
label: "Descargar PDF",
|
|
||||||
onClick: handleDownloadPdf,
|
|
||||||
loading: isPDFLoading,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
canDelete
|
|
||||||
? {
|
|
||||||
destructive: true,
|
|
||||||
icon: Trash2Icon,
|
|
||||||
label: "Eliminar",
|
|
||||||
onClick: handleDelete,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
].filter(Boolean);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-end gap-1 [@media(hover:hover)_and_(pointer:fine)]:pointer-events-none [@media(hover:hover)_and_(pointer:fine)]:opacity-0 [@media(hover:hover)_and_(pointer:fine)]:transition-opacity [@media(hover:hover)_and_(pointer:fine)]:duration-150 [@media(hover:hover)_and_(pointer:fine)]:group-hover:pointer-events-auto [@media(hover:hover)_and_(pointer:fine)]:group-hover:opacity-100 [@media(hover:hover)_and_(pointer:fine)]:group-focus-within:pointer-events-auto [@media(hover:hover)_and_(pointer:fine)]:group-focus-within:opacity-100">
|
<TooltipProvider>
|
||||||
{primaryAction && "href" in primaryAction ? (
|
<div className="flex items-center justify-end gap-1">
|
||||||
<Button className="h-8 gap-2 px-3" size="sm" variant="outline">
|
{visibleActions.map(renderIconButton)}
|
||||||
<a
|
<DropdownMenu>
|
||||||
href={primaryAction.href}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<primaryAction.icon className="size-4" />
|
|
||||||
<span>{primaryAction.label}</span>
|
|
||||||
</a>
|
|
||||||
</Button>
|
|
||||||
) : primaryAction ? (
|
|
||||||
<Button
|
|
||||||
className="h-8 gap-2 px-3"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
primaryAction.onClick?.();
|
|
||||||
}}
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
<primaryAction.icon className="size-4" />
|
|
||||||
<span>{primaryAction.label}</span>
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{quickAction ? (
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger
|
<TooltipTrigger
|
||||||
render={
|
render={
|
||||||
<Button
|
<DropdownMenuTrigger
|
||||||
className="size-8 text-muted-foreground hover:text-primary"
|
render={
|
||||||
onClick={(event) => {
|
<Button
|
||||||
event.preventDefault();
|
aria-label={t("common.more_actions")}
|
||||||
event.stopPropagation();
|
className="size-8 text-muted-foreground hover:text-foreground"
|
||||||
quickAction.onClick();
|
onClick={stopRowPropagation}
|
||||||
}}
|
size="icon-sm"
|
||||||
size="icon"
|
variant="ghost"
|
||||||
variant="ghost"
|
>
|
||||||
>
|
<MoreHorizontalIcon className="size-4" />
|
||||||
{quickAction.loading ? (
|
</Button>
|
||||||
<Spinner className="size-4 cursor-progress" />
|
}
|
||||||
) : (
|
/>
|
||||||
<quickAction.icon className="size-4" />
|
|
||||||
)}
|
|
||||||
<span className="sr-only">{quickAction.label}</span>
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<TooltipContent>{quickAction.label}</TooltipContent>
|
<TooltipContent>{t("common.more_actions")}</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
<DropdownMenuContent
|
||||||
) : null}
|
align="end"
|
||||||
|
className="w-56"
|
||||||
{menuActions.length > 0 ? (
|
onClick={stopRowPropagation}
|
||||||
<DropdownMenu>
|
>
|
||||||
<DropdownMenuTrigger
|
|
||||||
render={
|
|
||||||
<Button
|
|
||||||
className="size-8 text-muted-foreground hover:text-primary"
|
|
||||||
onClick={stop}
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<MoreHorizontalIcon className="size-4" />
|
|
||||||
<span className="sr-only">Más acciones</span>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<DropdownMenuContent align="end" className="w-52" onClick={stop}>
|
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuLabel>Acciones</DropdownMenuLabel>
|
<DropdownMenuLabel>{t("common.actions")}</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
{menuActions.length &&
|
{menuActions.map((action, index) => (
|
||||||
menuActions.map((action, index) =>
|
<React.Fragment key={action.key}>
|
||||||
action ? (
|
{action.destructive && index > 0 ? <DropdownMenuSeparator /> : null}
|
||||||
<React.Fragment key={action.label}>
|
{renderMenuItem(action)}
|
||||||
{action.destructive && index > 0 ? <DropdownMenuSeparator /> : null}
|
</React.Fragment>
|
||||||
<DropdownMenuItem
|
))}
|
||||||
className={
|
|
||||||
action.destructive
|
|
||||||
? "text-destructive focus:text-destructive"
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
action.onClick();
|
|
||||||
}}
|
|
||||||
variant={action.destructive ? "destructive" : "default"}
|
|
||||||
>
|
|
||||||
{"loading" in action && action.loading ? (
|
|
||||||
<Spinner className="mr-2 size-4" />
|
|
||||||
) : (
|
|
||||||
<action.icon className="mr-2 size-4" />
|
|
||||||
)}
|
|
||||||
<span>{action.label}</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</React.Fragment>
|
|
||||||
) : null
|
|
||||||
)}
|
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
) : null}
|
</div>
|
||||||
</div>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/customers",
|
"name": "@erp/customers",
|
||||||
"description": "Customers",
|
"description": "Customers",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/document-series",
|
"name": "@erp/document-series",
|
||||||
"description": "Document series module",
|
"description": "Document series module",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/factuges",
|
"name": "@erp/factuges",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/identity",
|
"name": "@erp/identity",
|
||||||
"description": "Identity module",
|
"description": "Identity module",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/supplier-invoices",
|
"name": "@erp/supplier-invoices",
|
||||||
"description": "Supplier invoices",
|
"description": "Supplier invoices",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/suppliers",
|
"name": "@erp/suppliers",
|
||||||
"description": "Suppliers",
|
"description": "Suppliers",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "uecko-erp-2025",
|
"name": "uecko-erp-2025",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"modules/*",
|
"modules/*",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/i18next",
|
"name": "@repo/i18next",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-criteria",
|
"name": "@repo/rdx-criteria",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-ddd",
|
"name": "@repo/rdx-ddd",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-logger",
|
"name": "@repo/rdx-logger",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-ui",
|
"name": "@repo/rdx-ui",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/rdx-utils",
|
"name": "@repo/rdx-utils",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/shadcn-ui",
|
"name": "@repo/shadcn-ui",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@repo/typescript-config",
|
"name": "@repo/typescript-config",
|
||||||
"version": "0.10.0",
|
"version": "0.10.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user