diff --git a/biome.json b/biome.json index 2fe73307..7be381b0 100644 --- a/biome.json +++ b/biome.json @@ -166,7 +166,7 @@ "noUnsafeOptionalChaining": "error", "noUnusedLabels": "error", "noUnusedVariables": "warn", - "useExhaustiveDependencies": "error", + "useExhaustiveDependencies": "info", "useHookAtTopLevel": "error", "useIsNan": "error", "useJsxKeyInIterable": "error", diff --git a/modules/customer-invoices/src/web/customer-invoice-routes.tsx b/modules/customer-invoices/src/web/customer-invoice-routes.tsx index 9de27507..5d0fa96b 100644 --- a/modules/customer-invoices/src/web/customer-invoice-routes.tsx +++ b/modules/customer-invoices/src/web/customer-invoice-routes.tsx @@ -3,11 +3,11 @@ import { lazy } from "react"; import { Outlet, type RouteObject } from "react-router-dom"; const ProformaLayout = lazy(() => - import("./proformas/shared2").then((m) => ({ default: m.ProformaLayout })) + import("./proformas/shared").then((m) => ({ default: m.ProformaLayout })) ); const ProformasListPage = lazy(() => - import("./proformas/list").then((m) => ({ default: m.ProformaListPage })) + import("./proformas/list").then((m) => ({ default: m.ListProformasPage })) ); /*const ProformasCreatePage = lazy(() => diff --git a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts index 25f0c5d3..74d06518 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts @@ -2,9 +2,9 @@ import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; import * as React from "react"; import { useTranslation } from "../../../i18n"; -import type { ProformaListRow } from "../../shared2"; -import type { PROFORMA_STATUS } from "../../shared2/entities"; -import { useChangeProformaStatusMutation } from "../../shared2/hooks"; +import type { ProformaListRow } from "../../shared"; +import type { PROFORMA_STATUS } from "../../shared/entities"; +import { useChangeProformaStatusMutation } from "../../shared/hooks"; interface ChangeStatusDialogState { open: boolean; diff --git a/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts b/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts index feef7695..7b5b9f54 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts @@ -7,7 +7,7 @@ import { XCircleIcon, } from "lucide-react"; -import type { ProformaStatus } from "../../shared2"; +import type { ProformaStatus } from "../../shared"; export const getProformaStatusButtonVariant = ( status: ProformaStatus 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 57b7ff55..604284e8 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 @@ -11,7 +11,7 @@ import { import { useState } from "react"; import { useTranslation } from "../../../i18n"; -import { PROFORMA_STATUS, PROFORMA_STATUS_TRANSITIONS, type ProformaListRow } from "../../shared2"; +import { PROFORMA_STATUS, PROFORMA_STATUS_TRANSITIONS, type ProformaListRow } from "../../shared"; import { getProformaStatusIcon } from "../helpers"; import { StatusNode, TimelineConnector } from "./components"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx index 850e4921..477ddc8d 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx @@ -4,7 +4,7 @@ import { cn } from "@repo/shadcn-ui/lib/utils"; import { CheckCircle2, type LucideIcon } from "lucide-react"; import { useTranslation } from "../../../../i18n"; -import type { PROFORMA_STATUS } from "../../../shared2"; +import type { PROFORMA_STATUS } from "../../../shared"; interface StatusNodeProps { status: PROFORMA_STATUS; 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 e88230c3..9b699ac2 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 @@ -2,7 +2,7 @@ import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; import React from "react"; import { useTranslation } from "../../../i18n"; -import { type ProformaListRow, useDeleteProformaMutation } from "../../shared2"; +import { type ProformaListRow, useDeleteProformaMutation } from "../../shared"; interface DeleteProformaDialogState { open: boolean; diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx index 1a479f5c..b3005a60 100644 --- a/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx @@ -10,7 +10,7 @@ import { } from "@repo/shadcn-ui/components"; import { useTranslation } from "../../../../i18n"; -import type { ProformaListRow } from "../../../shared2"; +import type { ProformaListRow } from "../../../shared"; interface DeleteProformaDialogProps { open: boolean; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/index.ts b/modules/customer-invoices/src/web/proformas/list/controllers/index.ts index efe38e55..c3af8ef6 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/index.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/index.ts @@ -1,2 +1,3 @@ -export * from "./use-list-proformas.controller.ts"; -export * from "./use-list-proformas-page.controller.ts"; +export * from "./use-list-proformas.controller"; +export * from "./use-list-proformas-page.controller"; +export * from "./use-proforma-summary-panel.controller"; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts index 0d272424..d0448b56 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts @@ -1,62 +1,24 @@ -import React from "react"; - -import { useChangeProformaStatusDialogController } from "../../change-status"; -import { useDeleteProformaDialogController } from "../../delete"; -import { useProformaIssueDialogController } from "../../issue-proforma"; -import type { ProformaListRow } from "../../shared2"; -import { - type PROFORMA_STATUS, - PROFORMA_STATUS_TRANSITIONS, -} from "../../shared2/entities/proforma-status.entity"; +import type { RightPanelMode } from "@repo/rdx-ui/hooks"; +import { useSearchParams } from "react-router-dom"; import { useListProformasController } from "./use-list-proformas.controller"; +import { useProformaSummaryPanelController } from "./use-proforma-summary-panel.controller"; -export function useListProformasPageController() { +export const useListProformasPageController = () => { const listCtrl = useListProformasController(); + const [searchParams] = useSearchParams(); - // Controlador de diálogos - const issueDialogCtrl = useProformaIssueDialogController(); - const changeStatusDialogCtrl = useChangeProformaStatusDialogController(); - const deleteDialogCtrl = useDeleteProformaDialogController(); + const proformaId = searchParams.get("proformaId") ?? ""; + const panelMode = (searchParams.get("panel") as RightPanelMode | null) ?? "view"; - const handleOpenIssueProformaDialog = React.useCallback( - (proforma: ProformaListRow) => { - issueDialogCtrl.openDialog(proforma); - }, - [issueDialogCtrl] - ); - - const handleOpenChangeProformaStatusDialog = React.useCallback( - (proforma: ProformaListRow, nextStatus: string) => { - const proforma_status = proforma.status as PROFORMA_STATUS; - const transitions = PROFORMA_STATUS_TRANSITIONS[proforma_status] ?? []; - - if (!transitions.includes(nextStatus as PROFORMA_STATUS)) { - console.warn(`Transición inválida: ${proforma.status} → ${nextStatus}`); - return; - } - - changeStatusDialogCtrl.openDialog([proforma], nextStatus as PROFORMA_STATUS); - }, - [changeStatusDialogCtrl] - ); - - const handleOpenDeleteProformaDialog = React.useCallback( - (proforma: ProformaListRow) => { - deleteDialogCtrl.openDialog([proforma]); - }, - [deleteDialogCtrl] - ); + const panelCtrl = useProformaSummaryPanelController({ + initialProformaId: proformaId, + initialMode: panelMode, + initialOpen: proformaId !== "", + }); return { listCtrl, - - issueDialogCtrl, - changeStatusDialogCtrl, - deleteDialogCtrl, - - handleIssueProforma: handleOpenIssueProformaDialog, - handleChangeStatusProforma: handleOpenChangeProformaStatusDialog, - handleDeleteProforma: handleOpenDeleteProformaDialog, + panelCtrl, }; -} +}; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts index 408e28fe..b147a162 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts @@ -1,50 +1,85 @@ -import type { CriteriaDTO } from "@erp/core"; import { useDebounce } from "@repo/rdx-ui/components"; import { useMemo, useState } from "react"; -import { useProformasListQuery } from "../../shared2"; +import { + type ListProformasByCriteriaParams, + type ProformaList, + type ProformaStatus, + useProformasListQuery, +} from "../../shared"; + +type ProformaListStatusFilter = "all" | ProformaStatus; + +const EMPTY_PROFORMAS_LIST: ProformaList = { + items: [], + page: 0, + perPage: 5, + totalPages: 0, + totalItems: 0, +}; export const useListProformasController = () => { const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(5); const [search, setSearch] = useState(""); - const [status, setStatus] = useState("all"); + const [statusFilter, setStatusFilter] = useState("all"); - const debouncedQ = useDebounce(search, 300); + const debouncedSearch = useDebounce(search, 300); - const criteria = useMemo(() => { - const baseFilters = - status === "all" ? [] : [{ field: "status", operator: "EQUALS", value: status }]; - - return { - q: debouncedQ || "", - pageSize, + const criteria = useMemo>( + () => ({ + q: debouncedSearch || "", pageNumber: pageIndex, + pageSize, order: "desc", - orderBy: "invoice_date", - filters: baseFilters, - }; - }, [pageSize, pageIndex, debouncedQ, status]); + orderBy: "invoiceDate", + filters: + statusFilter === "all" ? [] : [{ field: "status", operator: "eq", value: statusFilter }], + }), + [debouncedSearch, pageIndex, pageSize, statusFilter] + ); const query = useProformasListQuery({ criteria }); + const setStatusFilterValue = (value: string) => { + const nextValue = (value || "all") as ProformaListStatusFilter; + + setStatusFilter((prev) => { + if (prev === nextValue) return prev; + + // Sólo si la búsqueda realmente cambia, + // reseteamos la página a 0 para evitar inconsistencias + setPageIndex(0); + return nextValue; + }); + }; + const setSearchValue = (value: string) => { - setSearch(value.trim().replace(/\s+/g, " ")); - setPageIndex(0); + const nextValue = value.trim().replace(/\s+/g, " "); + + setSearch((prev) => { + if (prev === nextValue) return prev; + + // Sólo si la búsqueda realmente cambia, + // reseteamos la página a 0 para evitar inconsistencias + setPageIndex(0); + return nextValue; + }); }; const setPageSizeValue = (value: number) => { - setPageSize(value); - setPageIndex(0); - }; + setPageSize((prev) => { + if (prev === value) return prev; - const setStatusFilter = (newStatus: string) => { - setStatus(newStatus); - setPageIndex(0); + // Sólo si el tamaño de página realmente cambia, + // reseteamos la página a 0 para evitar inconsistencias + setPageIndex(0); + return value; + }); }; return { - data: query.data, + data: query.data ?? EMPTY_PROFORMAS_LIST, isLoading: query.isLoading, isFetching: query.isFetching, @@ -61,7 +96,7 @@ export const useListProformasController = () => { search, setSearchValue, - status, - setStatusFilter, + statusFilter, + setStatusFilter: setStatusFilterValue, }; }; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-summary-panel.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-summary-panel.controller.ts new file mode 100644 index 00000000..85a6f998 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-summary-panel.controller.ts @@ -0,0 +1,49 @@ +import { type RightPanelMode, useRightPanelState } from "@repo/rdx-ui/hooks"; +import { useCallback, useState } from "react"; + +import { useProformaGetQuery } from "../../shared"; + +interface Options { + initialProformaId?: string; + initialMode?: RightPanelMode; + initialOpen?: boolean; +} + +export const useProformaSummaryPanelController = ({ + initialProformaId = "", + initialMode = "view", + initialOpen = false, +}: Options = {}) => { + const [proformaId, setProformaId] = useState(initialProformaId); + + const panelState = useRightPanelState({ + defaultMode: initialMode, + defaultVisibility: initialOpen ? "visible" : "hidden", + }); + + const query = useProformaGetQuery({ + id: proformaId, + enabled: Boolean(proformaId), + }); + + const openProformaPanel = useCallback( + (id: string, mode: RightPanelMode = "view") => { + setProformaId(id); + panelState.open(mode); + }, + [panelState.open] + ); + + const closePanel = useCallback(() => { + panelState.close(); + setProformaId(""); + }, [panelState.close]); + + return { + proforma: query.data, + proformaId, + openProformaPanel, + closePanel, + panelState, + }; +}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/index.ts b/modules/customer-invoices/src/web/proformas/list/ui/blocks/index.ts index cf0040bf..4171d2a3 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/index.ts +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/index.ts @@ -1 +1,3 @@ -export * from "./proformas-grid"; +export * from "./proforma-summary-panel/proforma-summary-panel"; +export * from "./proformas-grid/proformas-grid"; +export * from "./proformas-grid/use-proforma-grid-columns"; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/index.ts b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/index.ts new file mode 100644 index 00000000..fa6bda86 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/index.ts @@ -0,0 +1 @@ +export * from "./proforma-summary-panel"; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-footer-actions.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-footer-actions.tsx new file mode 100644 index 00000000..c1705012 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-footer-actions.tsx @@ -0,0 +1,27 @@ +import { Button } from "@repo/shadcn-ui/components"; + +import type { Proforma } from "../../../../shared"; + +export const ProformaFooterActions = ({ + proforma, + onChangeStatus, + onEdit, +}: { + proforma: Proforma; + onChangeStatus?: (proforma: Proforma) => void; + onEdit?: (proforma: Proforma) => void; +}) => { + return ( +
+
+ + + +
+
+ ); +}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-header.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-header.tsx new file mode 100644 index 00000000..bb295430 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-header.tsx @@ -0,0 +1,69 @@ +import { Avatar, AvatarFallback, Badge } from "@repo/shadcn-ui/components"; +import { Building2Icon, CopyIcon, UserIcon } from "lucide-react"; + +import type { Proforma } from "../../../../shared"; +import { Initials, ProformaStatusBadge } from "../../components"; + +export const ProformaHeader = ({ proforma }: { proforma: Proforma }) => { + const handleCopyTin = async () => { + try { + await navigator.clipboard.writeText(proforma.recipient.tin); + } catch { + // Silencio o toast fuera de este componente + } + }; + + return ( +
+
+ + + + + + +
+
+

{proforma.recipient.name}

+ +
+ + {proforma.description && ( +

{proforma.description}

+ )} + +
+ + {proforma.isProforma ? ( + <> + + Empresa + + ) : ( + <> + + Particular + + )} + + + +
+
+
+
+ ); +}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-content.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-content.tsx new file mode 100644 index 00000000..24a89d0c --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-content.tsx @@ -0,0 +1,28 @@ +import { Separator } from "@repo/shadcn-ui/components"; + +import type { Proforma } from "../../../../shared"; + +import { ProformaFooterActions } from "./proforma-footer-actions"; +import { ProformaHeader } from "./proforma-header"; + +interface ProformaSummaryContentProps { + proforma: Proforma; + onEdit?: (proforma: Proforma) => void; + onChangeStatus?: (proforma: Proforma) => void; +} + +export const ProformaSummaryContent = ({ + proforma, + onEdit, + onChangeStatus, +}: ProformaSummaryContentProps) => { + return ( +
+ + + + + +
+ ); +}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx new file mode 100644 index 00000000..6aa63024 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx @@ -0,0 +1,114 @@ +import { RightPanel } from "@repo/rdx-ui/components"; +import type { RightPanelMode, RightPanelVisibility } from "@repo/rdx-ui/hooks"; +import { Button } from "@repo/shadcn-ui/components"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import { PencilIcon } from "lucide-react"; + +import type { Proforma } from "../../../../shared"; + +import { ProformaSummaryContent } from "./proforma-summary-content"; + +const mockERPData = { + totalPurchases: 45750.8, + purchasesThisYear: 12350.25, + lastInvoices: [ + { + id: "1", + number: "FAC-2024-0156", + date: "2024-01-15", + amount: 1250.0, + status: "paid" as const, + }, + { + id: "2", + number: "FAC-2024-0142", + date: "2024-01-08", + amount: 890.5, + status: "paid" as const, + }, + { + id: "3", + number: "FAC-2024-0128", + date: "2023-12-22", + amount: 2100.0, + status: "pending" as const, + }, + { + id: "4", + number: "FAC-2023-0098", + date: "2023-11-30", + amount: 750.25, + status: "overdue" as const, + }, + ], +}; + +interface ProformaSummaryPanelProps { + proforma?: Proforma; + + open: boolean; + visibility: RightPanelVisibility; + mode: RightPanelMode; + + onOpenChange: (open: boolean) => void; + + onEdit?: (proforma: Proforma) => void; + onChangeStatus?: (proforma: Proforma) => void; + className?: string; +} + +export const ProformaSummaryPanel = ({ + proforma, + + open, + visibility, + mode, + + onOpenChange, + + onEdit, + onChangeStatus, + + className, +}: ProformaSummaryPanelProps) => { + const titleMap: Record = { + view: "Vista previa de proforma", + edit: "Editar proforma", + create: "Nuevo proforma", + }; + + return ( + + {proforma ? ( + + ) : null} + + } + onOpenChange={onOpenChange} + open={open} + title={titleMap[mode]} + > + {proforma ? ( + + ) : ( +
+

Selecciona una proforma

+
+ )} +
+ ); +}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx index fabf3e55..5cbca394 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx @@ -1,13 +1,13 @@ import { DataTable, SkeletonDataTable } from "@repo/rdx-ui/components"; import type { ColumnDef } from "@tanstack/react-table"; -import { useNavigate } from "react-router-dom"; import { useTranslation } from "../../../../../i18n"; -import type { ProformaList, ProformaListRow } from "../../../../shared2"; +import type { ProformaList, ProformaListRow } from "../../../../shared"; interface ProformasGridProps { data?: ProformaList; loading: boolean; + fetching?: boolean; columns: ColumnDef[]; @@ -29,15 +29,14 @@ export const ProformasGrid = ({ onPageSizeChange, onRowClick, }: ProformasGridProps) => { - const navigate = useNavigate(); const { t } = useTranslation(); - const { items, total_items } = data || { items: [], total_items: 0 }; + const { items, totalItems } = data || { items: [], totalItems: 0 }; if (loading) return ( @@ -55,7 +54,7 @@ export const ProformasGrid = ({ onRowClick={(row, _index) => onRowClick?.(row.id)} pageIndex={pageIndex} pageSize={pageSize} - totalItems={total_items} + totalItems={totalItems} /> ); }; 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 55d9c4bc..08106042 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 @@ -21,14 +21,20 @@ import { PROFORMA_STATUS_TRANSITIONS, type ProformaListRow, type ProformaStatus, -} from "../../../../shared2"; +} from "../../../../shared"; import { ProformaStatusBadge } from "../../components"; type GridActionHandlers = { + onPreviewClick?: (proforma: ProformaListRow) => void; onEditClick?: (proforma: ProformaListRow) => void; onIssueClick?: (proforma: ProformaListRow) => void; - onChangeStatusClick?: (proforma: ProformaListRow, nextStatus: string) => void; onDeleteClick?: (proforma: ProformaListRow) => void; + onChangeStatusClick?: (proforma: ProformaListRow, nextStatus: ProformaStatus) => void; + onLinkedInvoiceClick?: (proforma: ProformaListRow) => void; + getNextStatus?: (proforma: ProformaListRow) => ProformaStatus | null; + canIssue?: (proforma: ProformaListRow) => boolean; + canEdit?: (proforma: ProformaListRow) => boolean; + canDelete?: (proforma: ProformaListRow) => boolean; }; export function useProformasGridColumns( @@ -61,7 +67,7 @@ export function useProformasGridColumns( enableHiding: false, },*/ { - accessorKey: "invoice_number", + accessorKey: "invoiceNumber", header: ({ column }) => { return ( Ver factura {invoiceId} @@ -119,7 +124,7 @@ export function useProformasGridColumns( // Cliente { - accessorKey: "client_name", + accessorKey: "recipientName", header: ({ column }) => { return ( + +
{proforma.recipient.tin}
); @@ -156,7 +164,7 @@ export function useProformasGridColumns( header: "Reference", }, { - accessorKey: "invoice_date", + accessorKey: "invoiceDate", header: ({ column }) => { return ( + } + title={t("pages.proformas.list.title")} + /> + + + + {isPanelOpen ? ( + + + {listContent} + + + + + +
+ navigate(`/proformas/${proforma.id}/edit`)} + onOpenChange={(open) => { + if (!open) { + panelCtrl.closePanel(); + return; + } + + panelCtrl.panelState.onOpenChange(true); + }} + open={panelCtrl.panelState.isOpen} + proforma={panelCtrl.proforma} + visibility={panelCtrl.panelState.visibility} + /> +
+
+
+ ) : ( +
{listContent}
+ )} + <> + {/* Emitir factura */} + !open && issueDialogCtrl.closeDialog()} + open={issueDialogCtrl.open} + proforma={issueDialogCtrl.proforma} + /> + + {/* Cambiar estado */} + !open && changeStatusDialogCtrl.closeDialog()} + open={changeStatusDialogCtrl.open} + proformas={changeStatusDialogCtrl.proformas} // ← recibe el status seleccionado + /> + + {/* Eliminar */} + !open && deleteDialogCtrl.closeDialog()} + open={deleteDialogCtrl.open} + proformas={deleteDialogCtrl.proformas} + requireSecondConfirm={true} + /> + +
+ + ); +}; 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 deleted file mode 100644 index 01eace58..00000000 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { ErrorAlert, PageHeader, SimpleSearchInput } from "@erp/core/components"; -import { AppContent, AppHeader, BackHistoryButton } from "@repo/rdx-ui/components"; -import { - Button, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@repo/shadcn-ui/components"; -import { FilterIcon, PlusIcon } from "lucide-react"; -import { useNavigate } from "react-router-dom"; - -import { useTranslation } from "../../../../i18n"; -import { ChangeStatusDialog } from "../../../change-status"; -import { DeleteProformaDialog } from "../../../delete/ui/components"; -import { ProformaIssueDialog } from "../../../issue-proforma"; -import { useListProformasPageController } from "../../controllers"; -import { ProformasGrid, useProformasGridColumns } from "../blocks"; - -export const ProformaListPage = () => { - const { t } = useTranslation(); - const navigate = useNavigate(); - - const { - listCtrl, - - issueDialogCtrl, - changeStatusDialogCtrl, - deleteDialogCtrl, - - handleChangeStatusProforma, - handleDeleteProforma, - handleIssueProforma, - } = useListProformasPageController(); - - const columns = useProformasGridColumns({ - onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`), - onIssueClick: handleIssueProforma, - onDeleteClick: handleDeleteProforma, - onChangeStatusClick: handleChangeStatusProforma, - }); - - if (listCtrl.isError) { - return ( - - - - - ); - } - - return ( - <> - - navigate("/proformas/create")} - > - - {t("pages.proformas.create.title")} - - } - title={t("pages.proformas.list.title")} - /> - - - -
- {/* Search and filters */} -
-
- - - -
- - navigate(`/proformas/${id}`)} - /> -
- - {/* Emitir factura */} - !open && issueDialogCtrl.closeDialog()} - open={issueDialogCtrl.open} - proforma={issueDialogCtrl.proforma} - /> - - {/* Cambiar estado */} - !open && changeStatusDialogCtrl.closeDialog()} - open={changeStatusDialogCtrl.open} - proformas={changeStatusDialogCtrl.proformas} // ← recibe el status seleccionado - /> - - {/* Eliminar */} - !open && deleteDialogCtrl.closeDialog()} - open={deleteDialogCtrl.open} - proformas={deleteDialogCtrl.proformas} - requireSecondConfirm={true} - /> -
-
- - ); -}; diff --git a/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx b/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx index 9c0eb74b..5d8205e9 100644 --- a/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx +++ b/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx @@ -8,7 +8,7 @@ import { useNavigate } from "react-router-dom"; import { useTranslation } from "../../../i18n"; import { ProformaDtoAdapter } from "../../adapters"; -import { useUpdateProforma } from "../../shared2/hooks/use-proforma-update-mutation"; +import { useUpdateProforma } from "../../shared/hooks/use-proforma-update-mutation"; import { type Proforma, type ProformaFormData, diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts index 87d50f4f..3a2a3083 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts @@ -11,4 +11,13 @@ export enum PROFORMA_STATUS { ISSUED = "issued", } +// Transiciones válidas según reglas del dominio +export const PROFORMA_STATUS_TRANSITIONS: Record = { + [PROFORMA_STATUS.DRAFT]: [PROFORMA_STATUS.SENT], + [PROFORMA_STATUS.SENT]: [PROFORMA_STATUS.APPROVED, PROFORMA_STATUS.REJECTED], + [PROFORMA_STATUS.APPROVED]: [PROFORMA_STATUS.ISSUED, PROFORMA_STATUS.DRAFT], + [PROFORMA_STATUS.REJECTED]: [PROFORMA_STATUS.DRAFT], + [PROFORMA_STATUS.ISSUED]: [], +}; + export type ProformaStatus = `${PROFORMA_STATUS}`;