Descarga de proformas en PDF
This commit is contained in:
parent
00c647fe20
commit
9d7e18d73d
@ -5,9 +5,9 @@ export async function downloadInvoicePDFApi(
|
|||||||
invoiceId: string,
|
invoiceId: string,
|
||||||
params?: Record<string, unknown>
|
params?: Record<string, unknown>
|
||||||
): Promise<Blob> {
|
): Promise<Blob> {
|
||||||
return dataSource.custom<Blob>({
|
return dataSource.custom<unknown, Blob>({
|
||||||
...params,
|
...params,
|
||||||
path: `issued-invoices/${invoiceId}/report`,
|
path: `issued-invoices/${invoiceId}/report/pdf`,
|
||||||
method: "get",
|
method: "get",
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { useDownloadInvoicePDFQuery } from "../../shared";
|
import { useDownloadInvoicePDFQuery } from "../hooks";
|
||||||
|
|
||||||
interface PendingDownload {
|
interface PendingDownload {
|
||||||
id: string;
|
id: string;
|
||||||
@ -37,7 +37,7 @@ export function useDownloadInvoicePDFController() {
|
|||||||
setPending({ id, invoice_number });
|
setPending({ id, invoice_number });
|
||||||
|
|
||||||
// refetch se dispara en un efecto, o aquí si prefieres:
|
// refetch se dispara en un efecto, o aquí si prefieres:
|
||||||
refetch().catch((err) => {
|
refetch().catch((err: unknown) => {
|
||||||
showErrorToast(
|
showErrorToast(
|
||||||
"Error al descargar",
|
"Error al descargar",
|
||||||
err instanceof Error ? err.message : "Error desconocido"
|
err instanceof Error ? err.message : "Error desconocido"
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./use-download-invoice-pdf-query";
|
||||||
@ -3,7 +3,7 @@
|
|||||||
import { useDataSource } from "@erp/core/hooks";
|
import { useDataSource } from "@erp/core/hooks";
|
||||||
import { type QueryKey, useQuery } from "@tanstack/react-query";
|
import { type QueryKey, useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { downloadInvoicePDFApi } from "../../download-pdf/api";
|
import { downloadInvoicePDFApi } from "../api";
|
||||||
|
|
||||||
export const ISSUED_INVOICE_QUERY_KEY = (id: string): QueryKey => ["issued_invoice", id] as const;
|
export const ISSUED_INVOICE_QUERY_KEY = (id: string): QueryKey => ["issued_invoice", id] as const;
|
||||||
|
|
||||||
@ -1,2 +1 @@
|
|||||||
export * from "./use-download-invoice-pdf-query";
|
|
||||||
export * from "./use-issued-invoice-list-query";
|
export * from "./use-issued-invoice-list-query";
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import type { IDataSource } from "@erp/core/client";
|
||||||
|
|
||||||
|
export async function downloadProformaPDFApi(
|
||||||
|
dataSource: IDataSource,
|
||||||
|
proformaId: string,
|
||||||
|
params?: Record<string, unknown>
|
||||||
|
): Promise<Blob> {
|
||||||
|
return dataSource.custom<Blob>({
|
||||||
|
...params,
|
||||||
|
path: `proformas/${proformaId}/report/pdf`,
|
||||||
|
method: "get",
|
||||||
|
responseType: "blob",
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./download-proforma-pdf.api";
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./use-download-proforma-pdf.controller";
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { useDownloadProformaPDFQuery } from "../hooks";
|
||||||
|
|
||||||
|
interface PendingDownload {
|
||||||
|
id: string;
|
||||||
|
proforma_number: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDownloadProformaPDFController() {
|
||||||
|
const [pending, setPending] = React.useState<PendingDownload | null>(null);
|
||||||
|
|
||||||
|
const { data, isFetching, refetch } = useDownloadProformaPDFQuery(pending?.id);
|
||||||
|
|
||||||
|
// Efecto: cuando hay blob + pending, disparamos la descarga
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!(pending && data)) return;
|
||||||
|
|
||||||
|
const blob = data;
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = `proforma-${pending.proforma_number}.pdf`;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
showSuccessToast(
|
||||||
|
"Descarga iniciada",
|
||||||
|
`La proforma ${pending.proforma_number} se está descargando.`
|
||||||
|
);
|
||||||
|
|
||||||
|
setPending(null);
|
||||||
|
}, [data, pending]);
|
||||||
|
|
||||||
|
const download = (id: string, proforma_number: string) => {
|
||||||
|
setPending({ id, proforma_number });
|
||||||
|
|
||||||
|
// refetch se dispara en un efecto, o aquí si prefieres:
|
||||||
|
refetch().catch((err: unknown) => {
|
||||||
|
showErrorToast(
|
||||||
|
"Error al descargar",
|
||||||
|
err instanceof Error ? err.message : "Error desconocido"
|
||||||
|
);
|
||||||
|
setPending(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
download, // (id, reference) => void
|
||||||
|
isLoading: isFetching,
|
||||||
|
loadingId: pending?.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./use-download-proforma-pdf-query";
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
import { useDataSource } from "@erp/core/hooks";
|
||||||
|
import { type QueryKey, useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { downloadProformaPDFApi } from "../api";
|
||||||
|
|
||||||
|
export const PROFORMA_QUERY_KEY = (id: string): QueryKey => ["proforma", id] as const;
|
||||||
|
|
||||||
|
type DownloadProformaPDFOptions = {
|
||||||
|
enabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useDownloadProformaPDFQuery(
|
||||||
|
proformaId?: string,
|
||||||
|
options?: DownloadProformaPDFOptions
|
||||||
|
) {
|
||||||
|
const dataSource = useDataSource();
|
||||||
|
const enabled = (options?.enabled ?? true) && !!proformaId;
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: PROFORMA_QUERY_KEY(proformaId ?? "unknown"),
|
||||||
|
queryFn: async (context) => {
|
||||||
|
if (!proformaId) throw new Error("proformaId is required");
|
||||||
|
|
||||||
|
const { signal } = context;
|
||||||
|
return await downloadProformaPDFApi(dataSource, proformaId, {
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enabled,
|
||||||
|
staleTime: 0,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
export function useQuery<
|
||||||
|
TQueryFnData = unknown,
|
||||||
|
TError = unknown,
|
||||||
|
TData = TQueryFnData,
|
||||||
|
TQueryKey extends QueryKey = QueryKey
|
||||||
|
>
|
||||||
|
|
||||||
|
TQueryFnData: the type returned from the queryFn.
|
||||||
|
TError: the type of Errors to expect from the queryFn.
|
||||||
|
TData: the type our data property will eventually have.
|
||||||
|
Only relevant if you use the select option,
|
||||||
|
because then the data property can be different
|
||||||
|
from what the queryFn returns.
|
||||||
|
Otherwise, it will default to whatever the queryFn returns.
|
||||||
|
TQueryKey: the type of our queryKey, only relevant
|
||||||
|
if you use the queryKey that is passed to your queryFn.
|
||||||
|
|
||||||
|
*/
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./api";
|
||||||
|
export * from "./controller";
|
||||||
|
export * from "./hooks";
|
||||||
@ -1,9 +1,12 @@
|
|||||||
import type { RightPanelMode } from "@repo/rdx-ui/hooks";
|
import type { RightPanelMode } from "@repo/rdx-ui/hooks";
|
||||||
|
import { useCallback } from "react";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
import { useChangeProformaStatusDialogController } from "../../change-status";
|
import { useChangeProformaStatusDialogController } from "../../change-status";
|
||||||
import { useDeleteProformaDialogController } from "../../delete";
|
import { useDeleteProformaDialogController } from "../../delete";
|
||||||
|
import { useDownloadProformaPDFController } from "../../download-pdf/controller";
|
||||||
import { useIssueProformaDialogController } from "../../issue-proforma";
|
import { useIssueProformaDialogController } from "../../issue-proforma";
|
||||||
|
import type { ProformaListRow } from "../../shared";
|
||||||
|
|
||||||
import { useListProformasController } from "./use-list-proformas.controller";
|
import { useListProformasController } from "./use-list-proformas.controller";
|
||||||
import { useProformaSummaryPanelController } from "./use-proforma-summary-panel.controller";
|
import { useProformaSummaryPanelController } from "./use-proforma-summary-panel.controller";
|
||||||
@ -14,6 +17,15 @@ export const useListProformasPageController = () => {
|
|||||||
const issueDialogCtrl = useIssueProformaDialogController();
|
const issueDialogCtrl = useIssueProformaDialogController();
|
||||||
const changeStatusDialogCtrl = useChangeProformaStatusDialogController();
|
const changeStatusDialogCtrl = useChangeProformaStatusDialogController();
|
||||||
|
|
||||||
|
const downloadPDFCtrl = useDownloadProformaPDFController();
|
||||||
|
|
||||||
|
const handleDownloadPDF = useCallback(
|
||||||
|
(proforma: ProformaListRow) => {
|
||||||
|
downloadPDFCtrl.download(proforma.id, proforma.invoiceNumber);
|
||||||
|
},
|
||||||
|
[downloadPDFCtrl]
|
||||||
|
);
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const proformaId = searchParams.get("proformaId") ?? "";
|
const proformaId = searchParams.get("proformaId") ?? "";
|
||||||
@ -32,5 +44,10 @@ export const useListProformasPageController = () => {
|
|||||||
deleteDialogCtrl,
|
deleteDialogCtrl,
|
||||||
issueDialogCtrl,
|
issueDialogCtrl,
|
||||||
changeStatusDialogCtrl,
|
changeStatusDialogCtrl,
|
||||||
|
|
||||||
|
downloadPDFCtrl,
|
||||||
|
handleDownloadPDF,
|
||||||
|
pdfDownloadingId: downloadPDFCtrl.loadingId,
|
||||||
|
isPDFDownloading: downloadPDFCtrl.isLoading,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -44,8 +44,16 @@ export const ListProformasPage = () => {
|
|||||||
fallbackPath: "/proformas",
|
fallbackPath: "/proformas",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { listCtrl, panelCtrl, deleteDialogCtrl, issueDialogCtrl, changeStatusDialogCtrl } =
|
const {
|
||||||
useListProformasPageController();
|
listCtrl,
|
||||||
|
panelCtrl,
|
||||||
|
deleteDialogCtrl,
|
||||||
|
issueDialogCtrl,
|
||||||
|
changeStatusDialogCtrl,
|
||||||
|
handleDownloadPDF,
|
||||||
|
isPDFDownloading: isPdfDownloading,
|
||||||
|
pdfDownloadingId,
|
||||||
|
} = useListProformasPageController();
|
||||||
|
|
||||||
const handleEditClick = (proformaId: string) => {
|
const handleEditClick = (proformaId: string) => {
|
||||||
navigate({
|
navigate({
|
||||||
@ -65,7 +73,9 @@ export const ListProformasPage = () => {
|
|||||||
onChangeStatusClick: (proforma) =>
|
onChangeStatusClick: (proforma) =>
|
||||||
changeStatusDialogCtrl.openDialog(prepareChangeProformaStatusTargets(proforma)),
|
changeStatusDialogCtrl.openDialog(prepareChangeProformaStatusTargets(proforma)),
|
||||||
|
|
||||||
onDownloadPdf: (proforma) => null,
|
onDownloadPdf: handleDownloadPDF,
|
||||||
|
pdfDownloadingId,
|
||||||
|
isPdfDownloading,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isPanelOpen = panelCtrl.panelState.isOpen;
|
const isPanelOpen = panelCtrl.panelState.isOpen;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user