.
This commit is contained in:
parent
0ff5c39023
commit
d298754ee5
@ -44,6 +44,7 @@
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@tanstack/react-query": "^5.51.23",
|
||||
"@tanstack/react-table": "^8.20.1",
|
||||
"@wojtekmaj/react-hooks": "^1.21.0",
|
||||
"axios": "^1.7.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"cmdk": "^1.0.0",
|
||||
@ -52,6 +53,7 @@
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"joi": "^17.13.1",
|
||||
"lucide-react": "^0.427.0",
|
||||
"print-js": "^1.6.0",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-currency-input-field": "^3.8.0",
|
||||
@ -60,6 +62,7 @@
|
||||
"react-hook-form": "^7.52.2",
|
||||
"react-hook-form-persist": "^3.0.0",
|
||||
"react-i18next": "^15.0.1",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-resizable-panels": "^2.0.23",
|
||||
"react-router-dom": "^6.26.0",
|
||||
"react-secure-storage": "^1.3.2",
|
||||
@ -94,6 +97,7 @@
|
||||
"ts-jest": "^29.2.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.0"
|
||||
"vite": "^5.4.0",
|
||||
"vite-plugin-static-copy": "^1.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,34 @@
|
||||
import { Badge, Button, Card, CardContent } from "@/ui";
|
||||
import { Badge, Button, Card, CardContent, CardHeader } from "@/ui";
|
||||
|
||||
import { DataTable, DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components";
|
||||
import {
|
||||
DataTable,
|
||||
DataTableSkeleton,
|
||||
ErrorOverlay,
|
||||
PDFViewer,
|
||||
SimpleEmptyState,
|
||||
} from "@/components";
|
||||
|
||||
import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar";
|
||||
import { useDataTable, useDataTableContext } from "@/lib/hooks";
|
||||
import { IListQuotes_Response_DTO, MoneyValue, UTCDateValue } from "@shared/contexts";
|
||||
import { ColumnDef, Row, Table } from "@tanstack/react-table";
|
||||
import { t } from "i18next";
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useQuotes } from "../hooks";
|
||||
|
||||
export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
export const QuotesDataTable = ({
|
||||
status = "all",
|
||||
className,
|
||||
}: {
|
||||
status?: string;
|
||||
className?: string;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
||||
const { useList } = useQuotes();
|
||||
const [focusedRow, setFocusedRow] = useState<Row<IListQuotes_Response_DTO>>();
|
||||
const { useList, useReport } = useQuotes();
|
||||
|
||||
const { data, isPending, isError, error } = useList({
|
||||
pagination: {
|
||||
@ -26,6 +39,13 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
quickSearchTerm: globalFilter,
|
||||
});
|
||||
|
||||
const {
|
||||
data: reportData,
|
||||
isPending: reportIsPending,
|
||||
isError: reportIsError,
|
||||
error: errorReport,
|
||||
} = useReport(focusedRow ? focusedRow.original.id : undefined);
|
||||
|
||||
const columns = useMemo<ColumnDef<IListQuotes_Response_DTO, any>[]>(
|
||||
() => [
|
||||
{
|
||||
@ -119,23 +139,31 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
pageCount: data?.total_pages ?? -1,
|
||||
});
|
||||
|
||||
const handleOnRowClick = (row: Row<IListQuotes_Response_DTO>) => {
|
||||
console.log("setFocusedRow", row.id);
|
||||
setFocusedRow(row);
|
||||
};
|
||||
|
||||
if (isError) {
|
||||
return <ErrorOverlay subtitle={(error as Error).message} />;
|
||||
}
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<DataTableSkeleton
|
||||
columnCount={6}
|
||||
searchableColumnCount={1}
|
||||
filterableColumnCount={2}
|
||||
//cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem"]}
|
||||
shrinkZero
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className='grid items-start flex-1 gap-4 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3'>
|
||||
<Card className='grid items-start gap-4 auto-rows-max md:gap-8 lg:col-span-2'>
|
||||
<CardContent>
|
||||
<DataTableSkeleton
|
||||
columnCount={6}
|
||||
searchableColumnCount={1}
|
||||
filterableColumnCount={2}
|
||||
//cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem"]}
|
||||
shrinkZero
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -149,11 +177,29 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
);
|
||||
}
|
||||
|
||||
console.log(reportData);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable table={table} paginationOptions={{ visible: true }}>
|
||||
<div className='grid items-start flex-1 gap-4 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3'>
|
||||
<DataTable
|
||||
table={table}
|
||||
paginationOptions={{ visible: true }}
|
||||
className='grid items-start gap-4 auto-rows-max md:gap-8 lg:col-span-2'
|
||||
onRowClick={handleOnRowClick}
|
||||
>
|
||||
<DataTableToolbar table={table} />
|
||||
</DataTable>
|
||||
</>
|
||||
<div>
|
||||
<Card className='overflow-hidden' x-chunk='dashboard-05-chunk-4'>
|
||||
<CardHeader className='flex flex-row items-start bg-muted/50'></CardHeader>
|
||||
<CardContent>
|
||||
<PDFViewer
|
||||
file={reportData}
|
||||
className='aspect-[3/4] object-contain overflow-hidden border-2 border-dashed rounded-lg'
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { UseListQueryResult, useList, useOne, useSave } from "@/lib/hooks/useDataSource";
|
||||
import { UseListQueryResult, useCustom, useList, useOne, useSave } from "@/lib/hooks/useDataSource";
|
||||
import {
|
||||
IFilterItemDataProviderParam,
|
||||
IGetListDataProviderParams,
|
||||
@ -12,6 +12,7 @@ import {
|
||||
IGetQuote_Response_DTO,
|
||||
IListQuotes_Response_DTO,
|
||||
IListResponse_DTO,
|
||||
IReportQuote_Response_DTO,
|
||||
IUpdateQuote_Request_DTO,
|
||||
IUpdateQuote_Response_DTO,
|
||||
UniqueID,
|
||||
@ -33,6 +34,11 @@ export type UseQuotesGetParamsType = {
|
||||
queryOptions?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type UseQuotesReportParamsType = {
|
||||
enabled?: boolean;
|
||||
queryOptions?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const quoteStatusFilter: Record<string, IFilterItemDataProviderParam> = {
|
||||
draft: {
|
||||
field: "status",
|
||||
@ -85,7 +91,7 @@ export const useQuotes = () => {
|
||||
|
||||
useOne: (id?: string, params?: UseQuotesGetParamsType) =>
|
||||
useOne<IGetQuote_Response_DTO>({
|
||||
queryKey: keys().data().resource("quotes").action("one").id("").params().get(),
|
||||
queryKey: keys().data().resource("quotes").action("one").id(id).params().get(),
|
||||
queryFn: () =>
|
||||
dataSource.getOne({
|
||||
resource: "quotes",
|
||||
@ -94,6 +100,7 @@ export const useQuotes = () => {
|
||||
enabled: !!id,
|
||||
...params,
|
||||
}),
|
||||
|
||||
useCreate: () =>
|
||||
useSave<ICreateQuote_Response_DTO, TDataSourceError, ICreateQuote_Request_DTO>({
|
||||
//mutationKey: keys().data().resource("quotes").action("one").id("").params().get(),
|
||||
@ -131,5 +138,21 @@ export const useQuotes = () => {
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
useReport: (id?: string, params?: UseQuotesReportParamsType) =>
|
||||
useCustom<IReportQuote_Response_DTO>({
|
||||
queryKey: keys().data().resource("quotes").action("report").id(id).params().get(),
|
||||
queryFn: () =>
|
||||
dataSource.custom({
|
||||
url: `${dataSource.getApiUrl()}/quotes/${id}/report`,
|
||||
method: "get",
|
||||
headers: {
|
||||
responseType: "arraybuffer",
|
||||
},
|
||||
}),
|
||||
enabled: !!id,
|
||||
select: (data) => new Uint8Array(data),
|
||||
...params,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ColumnDef, Table as ReactTable, flexRender } from "@tanstack/react-table";
|
||||
import { ColumnDef, Table as ReactTable, Row, flexRender } from "@tanstack/react-table";
|
||||
import { PropsWithChildren, ReactNode } from "react";
|
||||
|
||||
import {
|
||||
@ -45,6 +45,7 @@ export type DataTableProps<TData> = PropsWithChildren<{
|
||||
footerClassName?: string;
|
||||
rowClassName?: string;
|
||||
cellClassName?: string;
|
||||
onRowClick?: (row: Row<TData>) => void;
|
||||
}>;
|
||||
|
||||
export function DataTable<TData>({
|
||||
@ -60,6 +61,7 @@ export function DataTable<TData>({
|
||||
footerClassName,
|
||||
rowClassName,
|
||||
cellClassName,
|
||||
onRowClick,
|
||||
}: DataTableProps<TData>) {
|
||||
const headerVisible = headerOptions?.visible;
|
||||
|
||||
@ -104,6 +106,8 @@ export function DataTable<TData>({
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
onClick={() => (onRowClick ? onRowClick(row) : null)}
|
||||
tabIndex={0}
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className={cn(row.getIsSelected() ? "bg-accent" : "", rowClassName)}
|
||||
|
||||
156
client/src/components/PDFViewer/PDFViewer.tsx
Normal file
156
client/src/components/PDFViewer/PDFViewer.tsx
Normal file
@ -0,0 +1,156 @@
|
||||
import { Button } from "@/ui";
|
||||
import { useResizeObserver } from "@wojtekmaj/react-hooks";
|
||||
import type { PDFDocumentProxy } from "pdfjs-dist";
|
||||
import printJS from "print-js";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Document, Page, pdfjs } from "react-pdf";
|
||||
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
|
||||
import "react-pdf/dist/esm/Page/TextLayer.css";
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
"pdfjs-dist/build/pdf.worker.min.mjs",
|
||||
import.meta.url
|
||||
).toString();
|
||||
|
||||
const options = {
|
||||
cMapUrl: "/cmaps/",
|
||||
standardFontDataUrl: "/standard_fonts/",
|
||||
};
|
||||
|
||||
const maxWidth = 800;
|
||||
const resizeObserverOptions = {};
|
||||
|
||||
export interface PDFViewerProps {
|
||||
file?: Uint8Array;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const PDFViewer = ({ file, className }: PDFViewerProps): JSX.Element => {
|
||||
const [eventResize, setEventResize] = useState(false);
|
||||
|
||||
const [numPages, setNumPages] = useState(0);
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
|
||||
const [width, setWidth] = useState(undefined);
|
||||
//const parentRef = useRef(null);
|
||||
//const canvasRef = useRef(null);
|
||||
|
||||
const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);
|
||||
const [containerWidth, setContainerWidth] = useState<number>();
|
||||
|
||||
const onResize = useCallback<ResizeObserverCallback>((entries) => {
|
||||
const [entry] = entries;
|
||||
|
||||
if (entry) {
|
||||
setContainerWidth(entry.contentRect.width);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useResizeObserver(containerRef, resizeObserverOptions, onResize);
|
||||
|
||||
/*const hidePageCanvas = useCallback(() => {
|
||||
const canvas = containerRef?.current?.querySelector("canvas");
|
||||
if (canvas) canvas.style.visibility = "hidden";
|
||||
}, [containerRef]);
|
||||
|
||||
const showPageCanvas = useCallback(() => {
|
||||
const canvas = containerRef?.current?.querySelector("canvas");
|
||||
if (canvas) canvas.style.visibility = "visible";
|
||||
}, [containerRef]);
|
||||
|
||||
const onPageLoadSuccess = useCallback(() => {
|
||||
hidePageCanvas();
|
||||
}, [hidePageCanvas]);
|
||||
|
||||
const onPageRenderSuccess = useCallback(() => {
|
||||
showPageCanvas();
|
||||
}, [showPageCanvas]);
|
||||
|
||||
const onPageRenderError = useCallback(() => {
|
||||
showPageCanvas();
|
||||
}, [showPageCanvas]);
|
||||
*/
|
||||
|
||||
function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {
|
||||
setNumPages(nextNumPages);
|
||||
}
|
||||
|
||||
/*function onDocumentLoadSuccess({ numPages: nextNumPages }: PDFDocumentProxy): void {
|
||||
setPageNumber(1);
|
||||
setNumPages(nextNumPages);
|
||||
}*/
|
||||
|
||||
const changePage = (offset: number) =>
|
||||
setPageNumber((prevPage) =>
|
||||
offset > 0 ? Math.min(prevPage + offset, numPages) : Math.max(prevPage + offset, 1)
|
||||
);
|
||||
|
||||
const goToNextPage = () => changePage(1);
|
||||
const goToPrevPage = () => changePage(-1);
|
||||
const goToFirstPage = () => setPageNumber(1);
|
||||
const goToLastPage = () => setPageNumber(numPages);
|
||||
|
||||
/*useEffect(() => {
|
||||
if (numPages > 0 || eventResize) {
|
||||
const parentWidth = parentRef.current ? parentRef.current.offsetWidth : 0;
|
||||
const canvasWidth = canvasRef.current ? canvasRef.current.width : 0;
|
||||
|
||||
console.log("Document => ", parentWidth);
|
||||
console.log("Canvas => ", canvasWidth);
|
||||
|
||||
setWidth(parentWidth);
|
||||
setEventResize(false);
|
||||
}
|
||||
}, [eventResize, numPages]);**/
|
||||
|
||||
//file={`data:application/pdf;base64,$(pdfBase64String)`}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col' ref={setContainerRef}>
|
||||
<Document
|
||||
options={options}
|
||||
file={file}
|
||||
onLoadSuccess={onDocumentLoadSuccess}
|
||||
className={className}
|
||||
>
|
||||
{Array.from(new Array(numPages), (_el, index) => (
|
||||
<Page
|
||||
canvasBackground={"white"}
|
||||
key={`page_${index + 1}`}
|
||||
pageNumber={index + 1}
|
||||
width={containerWidth ? Math.min(containerWidth, maxWidth) : maxWidth}
|
||||
//onLoadSuccess={onPageLoadSuccess}
|
||||
//onRenderSuccess={onPageRenderSuccess}
|
||||
//onRenderError={onPageRenderError}
|
||||
/>
|
||||
))}
|
||||
</Document>
|
||||
<p className='text-center'>
|
||||
Página {pageNumber} de {numPages}
|
||||
</p>
|
||||
<div>
|
||||
<Button size={"icon"} variant='link' onClick={goToPrevPage}>
|
||||
Prev
|
||||
</Button>
|
||||
<Button size={"icon"} variant='link' onClick={goToNextPage}>
|
||||
Next
|
||||
</Button>
|
||||
<Button
|
||||
size={"icon"}
|
||||
variant='link'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
printJS({
|
||||
printable: file,
|
||||
type: "pdf",
|
||||
showModal: false,
|
||||
modalMessage: "Cargando...",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Imprimir react
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
1
client/src/components/PDFViewer/index.ts
Normal file
1
client/src/components/PDFViewer/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './PDFViewer';
|
||||
@ -10,6 +10,7 @@ export * from "./Forms";
|
||||
export * from "./Layout";
|
||||
export * from "./LoadingIndicator";
|
||||
export * from "./LoadingOverlay";
|
||||
export * from "./PDFViewer";
|
||||
export * from "./ProtectedRoute";
|
||||
//export * from "./SorteableDataTable";
|
||||
export * from "./TailwindIndicator";
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { IListResponse_DTO, INITIAL_PAGE_INDEX, INITIAL_PAGE_SIZE } from "@shared/contexts";
|
||||
import {
|
||||
ICreateOneDataProviderParams,
|
||||
ICustomDataProviderParam,
|
||||
IDataSource,
|
||||
IFilterItemDataProviderParam,
|
||||
IGetListDataProviderParams,
|
||||
@ -18,6 +19,10 @@ export const createAxiosDataProvider = (
|
||||
): IDataSource => ({
|
||||
name: () => "AxiosDataProvider",
|
||||
|
||||
getApiUrl: () => {
|
||||
return apiUrl;
|
||||
},
|
||||
|
||||
getList: async <R>(params: IGetListDataProviderParams): Promise<IListResponse_DTO<R>> => {
|
||||
const { resource, quickSearchTerm, pagination, filters, sort } = params;
|
||||
|
||||
@ -111,6 +116,56 @@ export const createAxiosDataProvider = (
|
||||
return;
|
||||
},
|
||||
|
||||
custom: async <R>(params: ICustomDataProviderParam): Promise<R> => {
|
||||
const { url, method, headers, payload } = params;
|
||||
const requestUrl = `${url}?`;
|
||||
|
||||
/*if (sort) {
|
||||
const generatedSort = extractSortParams(sort);
|
||||
if (generatedSort) {
|
||||
const { _sort, _order } = generatedSort;
|
||||
const sortQuery = {
|
||||
_sort: _sort.join(","),
|
||||
_order: _order.join(","),
|
||||
};
|
||||
requestUrl = `${requestUrl}&${queryString.stringify(sortQuery)}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
const filterQuery = extractFilterParams(filters);
|
||||
requestUrl = `${requestUrl}&${queryString.stringify(filterQuery)}`;
|
||||
}*/
|
||||
|
||||
/*if (query) {
|
||||
requestUrl = `${requestUrl}&${queryString.stringify(query)}`;
|
||||
}*/
|
||||
|
||||
if (headers) {
|
||||
httpClient.defaults.headers = {
|
||||
...httpClient.defaults.headers,
|
||||
...headers,
|
||||
};
|
||||
}
|
||||
|
||||
let customResponse;
|
||||
switch (method) {
|
||||
case "put":
|
||||
case "post":
|
||||
case "patch":
|
||||
customResponse = await httpClient[method]<R>(url, payload);
|
||||
break;
|
||||
case "remove":
|
||||
customResponse = await httpClient.delete<R>(url);
|
||||
break;
|
||||
default:
|
||||
customResponse = await httpClient.get<R>(requestUrl);
|
||||
break;
|
||||
}
|
||||
|
||||
return customResponse.data;
|
||||
},
|
||||
|
||||
/*getMany: async ({ resource }) => {
|
||||
const { body } = await httpClient.request({
|
||||
url: `${apiUrl}/${resource}`,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { IListResponse_DTO } from "@shared/contexts";
|
||||
import { AxiosHeaderValue } from "axios";
|
||||
|
||||
export interface IPaginationDataProviderParam {
|
||||
pageIndex: number;
|
||||
@ -51,11 +52,14 @@ export interface IRemoveOneDataProviderParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
/*export interface ICustomDataProviderParam {
|
||||
resource: string;
|
||||
method: string;
|
||||
params: any;
|
||||
}*/
|
||||
export interface ICustomDataProviderParam {
|
||||
url: string;
|
||||
method: "get" | "delete" | "head" | "options" | "post" | "put" | "patch";
|
||||
headers?: {
|
||||
[key: string]: AxiosHeaderValue;
|
||||
};
|
||||
payload?: unknown;
|
||||
}
|
||||
|
||||
export interface IDataSource {
|
||||
name: () => string;
|
||||
@ -66,9 +70,9 @@ export interface IDataSource {
|
||||
updateOne: <P, R>(params: IUpdateOneDataProviderParams<P>) => Promise<R>;
|
||||
removeOne: (params: IRemoveOneDataProviderParams) => Promise<void>;
|
||||
|
||||
//custom: <R>(params: ICustomDataProviderParam) => Promise<R>;
|
||||
custom: <R>(params: ICustomDataProviderParam) => Promise<R>;
|
||||
|
||||
//getApiUrl: () => string;
|
||||
getApiUrl: () => string;
|
||||
|
||||
//create: () => any;
|
||||
//createMany: () => any;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// export * from './useApiUrl';
|
||||
// export * from './useCreateMany';
|
||||
export * from "./useCustom";
|
||||
export * from "./useList";
|
||||
export * from "./useMany";
|
||||
export * from "./useOne";
|
||||
|
||||
14
client/src/lib/hooks/useDataSource/useCustom.tsx
Normal file
14
client/src/lib/hooks/useDataSource/useCustom.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { UseQueryOptions, UseQueryResult, keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { TDataSourceError, TDataSourceRecord } from "./types";
|
||||
|
||||
export function useCustom<
|
||||
TQueryFnData extends TDataSourceRecord = TDataSourceRecord,
|
||||
TError = TDataSourceError,
|
||||
TData extends TDataSourceRecord = TQueryFnData
|
||||
>(options: UseQueryOptions<TQueryFnData, TError, TData>): UseQueryResult<TData, TError> {
|
||||
return useQuery<TQueryFnData, TError, TData>({
|
||||
placeholderData: keepPreviousData,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
type BaseKey = string | number;
|
||||
|
||||
type ParametrizedDataActions = "list" | "infinite";
|
||||
type IdRequiredDataActions = "one";
|
||||
type IdRequiredDataActions = "one" | "report";
|
||||
type IdsRequiredDataActions = "many";
|
||||
type DataMutationActions =
|
||||
| "custom"
|
||||
@ -100,7 +100,7 @@ class DataResourceKeyBuilder extends BaseKeyBuilder {
|
||||
action(
|
||||
actionType: ParametrizedDataActions | IdRequiredDataActions | IdsRequiredDataActions
|
||||
): ParamsKeyBuilder | DataIdRequiringKeyBuilder | DataIdsRequiringKeyBuilder {
|
||||
if (actionType === "one") {
|
||||
if (["one", "report"].includes(actionType)) {
|
||||
return new DataIdRequiringKeyBuilder([...this.segments, actionType]);
|
||||
}
|
||||
if (actionType === "many") {
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { defineConfig, normalizePath } from "vite";
|
||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||
|
||||
import { resolve } from "node:path";
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const pdfjsDistPath = path.dirname(require.resolve("pdfjs-dist/package.json"));
|
||||
const cMapsDir = normalizePath(path.join(pdfjsDistPath, "cmaps"));
|
||||
|
||||
//const require = createRequire(import.meta.url);
|
||||
|
||||
@ -17,17 +23,28 @@ const standardFontsDir = normalizePath(
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [
|
||||
react(),
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: cMapsDir,
|
||||
dest: "",
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
css: { postcss: "./postcss.config.js" },
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: "@",
|
||||
replacement: resolve(__dirname, "./src"),
|
||||
replacement: path.resolve(__dirname, "./src"),
|
||||
},
|
||||
{
|
||||
find: "@shared",
|
||||
replacement: resolve(__dirname, "../shared/lib/"),
|
||||
replacement: path.resolve(__dirname, "../shared/lib/"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -62,7 +62,7 @@ export abstract class ExpressController implements IController {
|
||||
}
|
||||
|
||||
public downloadPDF(pdfBuffer: Buffer, filename: string) {
|
||||
return this._download(pdfBuffer, "application/pdf", `${filename}.pdf`);
|
||||
return this._download(pdfBuffer, "application/pdf", `${filename}`);
|
||||
}
|
||||
|
||||
public clientError(message?: string) {
|
||||
@ -125,7 +125,7 @@ export abstract class ExpressController implements IController {
|
||||
this.res.set({
|
||||
"Content-Type": contentType,
|
||||
"Content-Disposition": `attachment; filename=${filename}`,
|
||||
"Content-Length": buffer.length,
|
||||
//"Content-Length": buffer.length,
|
||||
});
|
||||
|
||||
return this.res.send(buffer);
|
||||
|
||||
@ -55,7 +55,7 @@ export class ReportQuoteController extends ExpressController {
|
||||
|
||||
const quote = <Quote>result.object;
|
||||
|
||||
return this.downloadPDF(await this.reporter.toPDF(quote, this.context), "prueba.pdf");
|
||||
return this.downloadPDF(await this.reporter.toPDF(quote, this.context), "quote.pdf");
|
||||
} catch (e: unknown) {
|
||||
return this.fail(e as IServerError);
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export type IReportQuote_Response_DTO = Uint8Array;
|
||||
@ -0,0 +1 @@
|
||||
export * from "./IReportQuote_Response.dto";
|
||||
@ -1,4 +1,5 @@
|
||||
export * from "./CreateQuote.dto";
|
||||
export * from "./GetQuote.dto";
|
||||
export * from "./ListQuotes.dto";
|
||||
export * from "./ReportQuote.dto";
|
||||
export * from "./UpdateQuote.dto";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user