From 323860b5b8e4771d8113d1e94aa009f213b5c936 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 22 Nov 2025 17:09:37 +0100 Subject: [PATCH] Facturas de cliente --- .../src/common/locales/en.json | 36 ++- .../src/common/locales/es.json | 37 ++- .../use-change-status-dialog-controller.ts | 20 +- .../change-status/ui/change-status-dialog.tsx | 228 +++++------------- .../change-status/ui/components/index.ts | 3 + .../ui/components/status-legend.tsx | 31 +++ .../ui/components/status-node.tsx | 106 ++++++++ .../ui/components/timeline-connector.tsx | 90 +++++++ .../use-delete-proforma-dialog-controller.ts | 69 ++++-- .../delete/hooks/use-delete-proforma.ts | 30 ++- .../delete/ui/delete-proforma-dialog.tsx | 73 +++++- .../use-proforma-list-page.controller.ts.ts | 5 +- .../use-proforma-grid-columns.tsx | 45 ++-- .../ui/components/proforma-status-badge.tsx | 24 +- .../list/ui/pages/proforma-list-page.tsx | 28 +-- .../web/proformas/types/proforma-status.ts | 26 ++ .../src/web/proformas/ui/blocks/index.ts | 3 - 17 files changed, 567 insertions(+), 287 deletions(-) create mode 100644 modules/customer-invoices/src/web/proformas/change-status/ui/components/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/change-status/ui/components/status-legend.tsx create mode 100644 modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx create mode 100644 modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx diff --git a/modules/customer-invoices/src/common/locales/en.json b/modules/customer-invoices/src/common/locales/en.json index bffa8f2d..2355c973 100644 --- a/modules/customer-invoices/src/common/locales/en.json +++ b/modules/customer-invoices/src/common/locales/en.json @@ -11,29 +11,43 @@ "remove_selected_rows_tooltip": "Remove selected row(s)", "download_pdf": "Download PDF", "send_email": "Send email", - "insert_row_above": "Insert row above", "insert_row_below": "Insert row below", "delete_row": "Delete", "actions": "Actions", - "rows_selected": "{{count}} fila(s) seleccionadas.", "rows_selected_of_total": "{{count}} de {{total}} fila(s) seleccionadas.", - "search_placeholder": "Type for search...", "search": "Search", "clear": "Clear" }, - "catalog": { "proformas": { "status": { - "all": "All", - "draft": "Draft", - "sent": "Sent", - "approved": "Approved", - "rejected": "Rejected", - "issued": "Issued" + "all": { + "label": "All", + "description": "" + }, + "draft": { + "label": "Draft", + "description": "Proforma in preparation" + }, + "sent": { + "label": "Sent", + "description": "Sent to the client" + }, + "approved": { + "label": "Approved", + "description": "Approved by the client" + }, + "rejected": { + "label": "Rejected", + "description": "Rejected by the client" + }, + "issued": { + "label": "Issued", + "description": "Converted to invoice" + } } }, "issued_invoices": { @@ -294,4 +308,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/customer-invoices/src/common/locales/es.json b/modules/customer-invoices/src/common/locales/es.json index 359757fa..64c91431 100644 --- a/modules/customer-invoices/src/common/locales/es.json +++ b/modules/customer-invoices/src/common/locales/es.json @@ -11,15 +11,12 @@ "remove_selected_rows_tooltip": "Eliminar fila(s) seleccionada(s)", "download_pdf": "Descargar en PDF", "send_email": "Enviar por email", - "insert_row_above": "Insertar fila arriba", "insert_row_below": "Insertar fila abajo", "delete_row": "Eliminar", "actions": "Acciones", - "rows_selected": "{{count}} fila(s) seleccionadas.", "rows_selected_of_total": "{{count}} de {{total}} fila(s) seleccionadas.", - "search_placeholder": "Escribe aquí para buscar...", "search": "Buscar", "clear": "Limpiar" @@ -27,12 +24,30 @@ "catalog": { "proformas": { "status": { - "all": "Todas", - "draft": "Borrador", - "sent": "Enviada", - "approved": "Aprobada", - "rejected": "Rechazada", - "issued": "Emitida" + "all": { + "label": "Todas", + "description": "" + }, + "draft": { + "label": "Borrador", + "description": "Proforma en preparación" + }, + "sent": { + "label": "Enviada", + "description": "Enviada al cliente" + }, + "approved": { + "label": "Aprobada", + "description": "Aprobada por el cliente" + }, + "rejected": { + "label": "Rechazada", + "description": "Rechazada por el cliente" + }, + "issued": { + "label": "Emitida", + "description": "Convertida a factura" + } } }, "issued_invoices": { @@ -138,7 +153,6 @@ } } }, - "form_groups": { "customer": { "title": "Cliente", @@ -187,7 +201,6 @@ "placeholder": "Referencia de la proforma", "description": "Referencia de la proforma" }, - "description": { "label": "Descripción", "placeholder": "Descripción de la proforma", @@ -286,4 +299,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts index 5befb45a..0ab44ebc 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts @@ -1,12 +1,11 @@ -import { showErrorToast } from "@repo/rdx-ui/helpers"; +import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; import * as React from "react"; -import type { ProformaSummaryData } from "../../types"; +import type { PROFORMA_STATUS, ProformaSummaryData } from "../../types"; import { useChangeProformaStatus } from "../hooks/use-change-status"; interface ChangeStatusDialogState { open: boolean; - targetStatus: string | null; proformas: ProformaSummaryData[]; loading: boolean; } @@ -16,15 +15,13 @@ export function useChangeStatusDialogController() { const [state, setState] = React.useState({ open: false, - targetStatus: null, - proformas: [], + proformas: [] as ProformaSummaryData[], loading: false, }); const openDialog = (proformas: ProformaSummaryData[]) => { setState({ open: true, - targetStatus: null, proformas, loading: false, }); @@ -38,13 +35,16 @@ export function useChangeStatusDialogController() { setState((s) => ({ ...s, targetStatus: status })); }; - const confirmChangeStatus = async () => { - if (!state.targetStatus || state.proformas.length === 0) return; + const confirmChangeStatus = async (status: PROFORMA_STATUS) => { + if (!state.proformas.length) return; setState((s) => ({ ...s, loading: true })); for (const proforma of state.proformas) { - await changeStatus(proforma.id, state.targetStatus, { + await changeStatus(proforma.id, status, { + onSuccess: () => { + showSuccessToast("Estado cambiado"); + }, onError: (err: unknown) => { const error = err as Error; showErrorToast("Error cambiando estado", error.message); @@ -59,12 +59,10 @@ export function useChangeStatusDialogController() { return { open: state.open, proformas: state.proformas, - targetStatus: state.targetStatus, isSubmitting: state.loading, openDialog, closeDialog, - setTargetStatus, confirmChangeStatus, }; } diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx index 11787a82..eed5bfd3 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx @@ -8,64 +8,62 @@ import { DialogTitle, Spinner, } from "@repo/shadcn-ui/components"; -import { cn } from "@repo/shadcn-ui/lib/utils"; -import { - CheckCircle2Icon, - ChevronRightIcon, - FileCheckIcon, - FileTextIcon, - SendIcon, - XCircleIcon, -} from "lucide-react"; import { useState } from "react"; import { useTranslation } from "../../../i18n"; -import { PROFORMA_STATUS, PROFORMA_STATUS_TRANSITIONS } from "../../types"; +import { + PROFORMA_STATUS, + PROFORMA_STATUS_TRANSITIONS, + type ProformaSummaryData, + getProformaStatusIcon, +} from "../../types"; + +import { StatusNode, TimelineConnector } from "./components"; interface ChangeStatusDialogProps { open: boolean; onOpenChange: (open: boolean) => void; - proformas: { id: string; status: string }[]; + proformas: ProformaSummaryData[]; targetStatus?: string; isSubmitting: boolean; - onConfirm: () => void; + onConfirm: (status: PROFORMA_STATUS) => void; } const STATUS_FLOW = [ { status: PROFORMA_STATUS.DRAFT, - icon: FileTextIcon, - color: "text-gray-500", - bgColor: "bg-gray-100", - borderColor: "border-gray-300", + activeColor: "text-gray-700", + activeBg: "bg-gray-100", + activeBorder: "border-gray-300", + activeRing: "ring-gray-200", }, { status: PROFORMA_STATUS.SENT, - icon: SendIcon, - color: "text-blue-600", - bgColor: "bg-blue-100", - borderColor: "border-blue-300", - }, - { - status: PROFORMA_STATUS.APPROVED, - icon: CheckCircle2Icon, - color: "text-green-600", - bgColor: "bg-green-100", - borderColor: "border-green-300", + activeColor: "text-yellow-700", + activeBg: "bg-yellow-100", + activeBorder: "border-yellow-300", + activeRing: "ring-yellow-200", }, { status: PROFORMA_STATUS.REJECTED, - icon: XCircleIcon, - color: "text-red-600", - bgColor: "bg-red-100", - borderColor: "border-red-300", + activeColor: "text-red-700", + activeBg: "bg-red-100", + activeBorder: "border-red-300", + activeRing: "ring-red-200", + }, + { + status: PROFORMA_STATUS.APPROVED, + activeColor: "text-green-700", + activeBg: "bg-green-100", + activeBorder: "border-green-300", + activeRing: "ring-green-200", }, { status: PROFORMA_STATUS.ISSUED, - icon: FileCheckIcon, - color: "text-purple-600", - bgColor: "bg-purple-100", - borderColor: "border-purple-300", + activeColor: "text-blue-700", + activeBg: "bg-blue-100", + activeBorder: "border-blue-300", + activeRing: "ring-blue-200", }, ] as const; @@ -122,12 +120,12 @@ export function ChangeStatusDialog({ return ( - + Cambiar estado de la proforma {proformas.length === 1 - ? `Selecciona el nuevo estado para la proforma #${proformas[0].id}` + ? `Selecciona el nuevo estado para la proforma #${proformas[0].reference}` : `Selecciona el nuevo estado para ${proformas.length} proformas seleccionadas`} @@ -139,150 +137,56 @@ export function ChangeStatusDialog({ ) : (
- {/* Línea de conexión */} -
- - {/* Estados */} -
+
{STATUS_FLOW.map((statusConfig, index) => { const statusType = getStatusType(statusConfig.status); - const Icon = statusConfig.icon; const isSelected = selectedStatus === statusConfig.status; const isClickable = statusType === "available"; return ( -
- {/* Botón de estado */} - - - {/* Etiqueta y descripción */} -
-

- {t(`catalog.proformas.status.${statusConfig.status}`)} -

-

- {t(`catalog.proformas.status.${statusConfig.status}.description`)} -

-
- - {/* Flecha de conexión (excepto el último) */} - {index < STATUS_FLOW.length - 1 && ( - - )} -
+ { + if (isClickable) { + setSelectedStatus(statusConfig.status); + } + }} + status={statusConfig.status} + statusType={statusType} + /> ); })}
-
-
-
- {currentStatus && ( -
-
- Estado actual -
- )} -
-
- Estados disponibles -
-
-
- Estados no disponibles -
-
+ ({ + bgClass: statusConfig.activeBg, + statusType: getStatusType(statusConfig.status), + }))} + />
)} - + + +
+

+ {label} +

+

{description}

+
+
+ ); +}; diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx new file mode 100644 index 00000000..6753d239 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { cn } from "@repo/shadcn-ui/lib/utils"; + +interface TimelineConnectorProps { + nodeCount: number; + statusColors: Array<{ + bgClass: string; + statusType: "past" | "current" | "available" | "unavailable"; + }>; +} +export function TimelineConnector({ nodeCount, statusColors }: TimelineConnectorProps) { + const segments = Array.from({ length: nodeCount - 1 }).map((_, index) => { + const fromStatus = statusColors[index]; + const toStatus = statusColors[index + 1]; + + return { + from: fromStatus, + to: toStatus, + }; + }); + + return ( +
+
+ +
+ {segments.map((segment, index) => { + const segmentWidth = `${100 / (nodeCount - 1)}%`; + + // Extraer el color del bgClass (ej: "bg-gray-100" -> "gray") + const getColorFromBgClass = (bgClass: string) => { + const match = bgClass.match(/bg-(\w+)-/); + return match ? match[1] : "gray"; + }; + + const fromColor = getColorFromBgClass(segment.from.bgClass); + const toColor = getColorFromBgClass(segment.to.bgClass); + + // Determinar opacidad basada en el tipo de estado + const getOpacity = (statusType: string) => { + if (statusType === "past") return 0.3; + if (statusType === "unavailable") return 0.15; + return 1; + }; + + const fromOpacity = getOpacity(segment.from.statusType); + const toOpacity = getOpacity(segment.to.statusType); + + return ( +
+
+
+ ); + })} +
+ +
+ {Array.from({ length: nodeCount }).map((_, index) => { + const statusColor = statusColors[index]; + const isPast = statusColor.statusType === "past"; + const isUnavailable = statusColor.statusType === "unavailable"; + + return ( +
+ ); + })} +
+
+ ); +} diff --git a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts index ca4f3cc6..e12a6710 100644 --- a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts @@ -5,55 +5,76 @@ import { useTranslation } from "../../../i18n"; import type { ProformaSummaryData } from "../../types"; import { useDeleteProforma } from "../hooks"; -interface ProformaState { +interface DeleteProformaDialogState { open: boolean; - proforma: ProformaSummaryData | null; + proformas: ProformaSummaryData[]; + loading: boolean; + requireSecondConfirm: boolean; } export function useDeleteProformaDialogController() { const { t } = useTranslation(); - const { mutate, isPending } = useDeleteProforma(); + const { deleteProforma } = useDeleteProforma(); - const [state, setState] = React.useState({ + const [state, setState] = React.useState({ open: false, - proforma: null, + proformas: [], + loading: false, + requireSecondConfirm: false, }); - const openDialog = (proforma: ProformaSummaryData) => { - setState({ open: true, proforma }); + const openDialog = (proformas: ProformaSummaryData[]) => { + const needDoubleCheck = proformas.length > 5; + setState({ + open: true, + proformas, + loading: false, + requireSecondConfirm: needDoubleCheck, + }); }; const closeDialog = () => { setState((s) => ({ ...s, open: false })); }; - const confirmDelete = () => { - if (!state.proforma) return; + const confirmDelete = async () => { + if (state.proformas.length === 0) return; - mutate( - { proformaId: state.proforma.id }, - { - onSuccess(data, variables, onMutateResult, context) { - console.log("adios"); - console.log(data); - showSuccessToast(t("proformas.delete_proforma_dialog.success_title")); - closeDialog(); + if (state.requireSecondConfirm) { + setState((s) => ({ ...s, requireSecondConfirm: false })); + return; // ahora el UI mostrará un segundo mensaje de confirmación + } + + setState((s) => ({ ...s, loading: true })); + + for (const p of state.proformas) { + await deleteProforma(p.id, { + onSuccess: () => { + showSuccessToast( + "Proforma eliminada", + `La proforma ${p.reference ?? `#${p.id}`} ha sido eliminada.` + ); }, - onError(error, variables, onMutateResult, context) { - showErrorToast(t("proformas.delete_proforma_dialog.error_title"), error.message); + onError: (err) => { + showErrorToast( + "Error al eliminar", + err instanceof Error ? err.message : "Ocurrió un error al eliminar la proforma" + ); }, - } - ); + }); + } + + setState((s) => ({ ...s, loading: false })); + closeDialog(); }; return { open: state.open, - proforma: state.proforma, - isSubmitting: isPending, + proformas: state.proformas, + isSubmitting: state.loading, openDialog, closeDialog, - confirmDelete, }; } diff --git a/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts b/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts index 39efd1d5..6ce74b1e 100644 --- a/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts +++ b/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts @@ -3,7 +3,13 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { deleteProformaApi } from "../api"; -interface useDeleteProformaPayload { +interface DeleteProformaOptions { + onSuccess?: () => void; + onError?: (err: unknown) => void; + onLoadingChange?: (loading: boolean) => void; +} + +interface DeleteProformaPayload { proformaId: string; } @@ -11,13 +17,29 @@ export function useDeleteProforma() { const dataSource = useDataSource(); const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ proformaId }: useDeleteProformaPayload) => + const mutation = useMutation({ + mutationFn: ({ proformaId }: DeleteProformaPayload) => deleteProformaApi(dataSource, proformaId), onSuccess() { - console.log("hola"); queryClient.invalidateQueries({ queryKey: ["proformas"] }); }, }); + + async function deleteProforma(proformaId: string, opts?: DeleteProformaOptions) { + try { + opts?.onLoadingChange?.(true); + await mutation.mutateAsync({ proformaId }); + opts?.onSuccess?.(); + } catch (err) { + opts?.onError?.(err); + } finally { + opts?.onLoadingChange?.(false); + } + } + + return { + deleteProforma, + isPending: mutation.isPending, + }; } diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx b/modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx index 03ec1918..920b46a1 100644 --- a/modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx @@ -8,40 +8,83 @@ import { Button, Spinner, } from "@repo/shadcn-ui/components"; -import { Trans } from "react-i18next"; +import { useEffect } from "react"; import { useTranslation } from "../../../i18n"; +import type { ProformaSummaryData } from "../../types"; interface DeleteProformaDialogProps { open: boolean; onOpenChange: (open: boolean) => void; - proformaRef?: string; + proformas: ProformaSummaryData[]; isSubmitting: boolean; onConfirm: () => void; + requireSecondConfirm: boolean; } export function DeleteProformaDialog({ open, onOpenChange, - proformaRef, + proformas, isSubmitting, onConfirm, + requireSecondConfirm, }: DeleteProformaDialogProps) { const { t } = useTranslation(); + const total = proformas.length; + const isSingle = total === 1; + + const title = requireSecondConfirm + ? "Confirmación adicional" + : isSingle + ? `Eliminar proforma ${proformas[0].reference ?? `#${proformas[0].id}`}` + : `Eliminar ${total} proformas`; + + const description = requireSecondConfirm + ? `Estás a punto de borrar ${total} proformas. Esta acción masiva no se puede deshacer. ¿Seguro que quieres continuar?` + : isSingle + ? "¿Seguro que deseas eliminar esta proforma? Esta acción no se puede deshacer." + : `¿Seguro que deseas eliminar las ${total} proformas seleccionadas? Esta acción no se puede deshacer.`; + + // Usar teclado para confirmar (Enter / Escape) + useEffect(() => { + if (!open) return; + + const handleKey = (e: KeyboardEvent) => { + if (e.key === "Enter") { + e.preventDefault(); + onConfirm(); + } + if (e.key === "Escape") { + onOpenChange(false); + } + }; + + window.addEventListener("keydown", handleKey); + return () => window.removeEventListener("keydown", handleKey); + }, [open, onConfirm, onOpenChange]); + return ( - + - {t("proformas.delete_proforma_dialog.title")} - - - + {title} + {description} - + {!requireSecondConfirm && total > 1 && ( +
+
    + {proformas.map((p) => ( +
  • + Proforma {p.reference ?? `#${p.id}`} +
  • + ))} +
+
+ )} + + @@ -52,8 +95,12 @@ export function DeleteProformaDialog({ {t("proformas.delete_proforma_dialog.deleting")} - ) : ( + ) : requireSecondConfirm ? ( + <>{t("proformas.delete_proforma_dialog.mass_delete")} + ) : isSingle ? ( <>{t("proformas.delete_proforma_dialog.delete")} + ) : ( + <>{t("proformas.delete_proforma_dialog.delete_plural")} )} diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts index dff07ef3..eba7c503 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts @@ -37,15 +37,14 @@ export function useProformaListPageController() { return; } - changeStatusDialogCtrl.openDialog(proforma, nextStatus); + changeStatusDialogCtrl.openDialog([proforma]); }, [changeStatusDialogCtrl] ); const handleDeleteProforma = React.useCallback( (proforma: ProformaSummaryData) => { - console.log(proforma); - deleteDialogCtrl.openDialog(proforma); + deleteDialogCtrl.openDialog([proforma]); }, [deleteDialogCtrl] ); diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx index c460732c..136ba582 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx @@ -244,29 +244,28 @@ export function useProformasGridColumns( )} {/* Cambiar estado */} - {!isIssued && - availableTransitions.map((next_status) => ( - - - - - - - Cambiar a {t(`catalog.proformas.status.${next_status}`)} - - - - ))} + {!isIssued && availableTransitions.length && ( + + + + + + + Cambiar a {t(`catalog.proformas.status.${availableTransitions[0]}.label`)} + + + + )} {/* Emitir factura: solo si approved */} {!isIssued && proforma.status === "approved" && ( diff --git a/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx b/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx index e524c7bc..590e732d 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx @@ -1,4 +1,4 @@ -import { Badge } from "@repo/shadcn-ui/components"; +import { Badge, Tooltip, TooltipContent, TooltipTrigger } from "@repo/shadcn-ui/components"; import { cn } from "@repo/shadcn-ui/lib/utils"; import { useTranslation } from "../../../../i18n"; @@ -6,6 +6,7 @@ import { type ProformaStatus, getProformaStatusButtonVariant, getProformaStatusColor, + getProformaStatusIcon, } from "../../../types"; export type ProformaStatusBadgeProps = { @@ -16,14 +17,23 @@ export type ProformaStatusBadgeProps = { export const ProformaStatusBadge = ({ status, className }: ProformaStatusBadgeProps) => { const { t } = useTranslation(); const normalizedStatus = status.toLowerCase() as ProformaStatus; + const Icon = getProformaStatusIcon(normalizedStatus); return ( - - {t(`catalog.proformas.status.${normalizedStatus}`, { defaultValue: status })} - + + + + + {t(`catalog.proformas.status.${normalizedStatus}.label`, { defaultValue: status })} + + + +

{t(`catalog.proformas.status.${normalizedStatus}.description`)}

+
+
); }; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx b/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx index 177144d4..44cf7754 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx @@ -86,12 +86,16 @@ export const ProformaListPage = () => { - {t("catalog.proformas.status.all")} - {t("catalog.proformas.status.draft")} - {t("catalog.proformas.status.sent")} - {t("catalog.proformas.status.approved")} - {t("catalog.proformas.status.rejected")} - {t("catalog.proformas.status.issued")} + {t("catalog.proformas.status.all.label")} + {t("catalog.proformas.status.draft.label")} + {t("catalog.proformas.status.sent.label")} + + {t("catalog.proformas.status.approved.label")} + + + {t("catalog.proformas.status.rejected.label")} + + {t("catalog.proformas.status.issued.label")}
@@ -125,22 +129,18 @@ export const ProformaListPage = () => { { - if (!open) changeStatusDialogCtrl.closeDialog(); - }} + onOpenChange={(open) => !open && changeStatusDialogCtrl.closeDialog()} open={changeStatusDialogCtrl.open} - proformaRef={changeStatusDialogCtrl.proforma?.reference} - proformas={changeStatusDialogCtrl.proformas} - targetStatus={changeStatusDialogCtrl.targetStatus ?? undefined} + proformas={changeStatusDialogCtrl.proformas} // ← recibe el status seleccionado /> {/* Eliminar */} !o && deleteDialogCtrl.closeDialog()} + onOpenChange={(open) => !open && deleteDialogCtrl.closeDialog()} open={deleteDialogCtrl.open} - proformaRef={deleteDialogCtrl.proforma?.reference} + proformas={deleteDialogCtrl.proformas} /> diff --git a/modules/customer-invoices/src/web/proformas/types/proforma-status.ts b/modules/customer-invoices/src/web/proformas/types/proforma-status.ts index ec499360..54ba45fa 100644 --- a/modules/customer-invoices/src/web/proformas/types/proforma-status.ts +++ b/modules/customer-invoices/src/web/proformas/types/proforma-status.ts @@ -1,3 +1,12 @@ +import { + CheckCircle2Icon, + FileCheckIcon, + FileQuestionIcon, + FileTextIcon, + SendIcon, + XCircleIcon, +} from "lucide-react"; + export enum PROFORMA_STATUS { DRAFT = "draft", SENT = "sent", @@ -52,3 +61,20 @@ export const getProformaStatusColor = (status: ProformaStatus): string => { return "bg-gray-100 text-gray-700 hover:bg-gray-100"; } }; + +export const getProformaStatusIcon = (status: ProformaStatus) => { + switch (status) { + case "draft": + return FileTextIcon; + case "sent": + return SendIcon; + case "approved": + return CheckCircle2Icon; + case "rejected": + return XCircleIcon; + case "issued": + return FileCheckIcon; + default: + return FileQuestionIcon; + } +}; diff --git a/modules/customer-invoices/src/web/proformas/ui/blocks/index.ts b/modules/customer-invoices/src/web/proformas/ui/blocks/index.ts index 403af02f..c66ccee2 100644 --- a/modules/customer-invoices/src/web/proformas/ui/blocks/index.ts +++ b/modules/customer-invoices/src/web/proformas/ui/blocks/index.ts @@ -1,5 +1,2 @@ -export * from "../../issue-proforma/ui/issue-proforma-dialog"; - -export * from "./proforma-delete-dialog"; export * from "./proforma-layout"; export * from "./proforma-tax-summary";