@pagamio/frontend-commons-lib 0.8.350 → 0.8.351
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/ui/TableDownload.js +1 -1
- package/lib/pagamio-table/data-table/TableDownload.js +1 -1
- package/lib/pagamio-table/data-table/exportUtils.d.ts +3 -4
- package/lib/pagamio-table/data-table/exportUtils.js +68 -8
- package/lib/pagamio-table/data-table/pdfExportUtils.js +17 -5
- package/lib/shared/hooks/index.d.ts +1 -0
- package/lib/shared/hooks/index.js +1 -0
- package/lib/shared/hooks/useInfiniteCursor.d.ts +104 -0
- package/lib/shared/hooks/useInfiniteCursor.js +73 -0
- package/package.json +3 -3
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
import { Dropdown, DropdownItem } from 'flowbite-react';
|
|
4
4
|
import { jsPDF } from 'jspdf';
|
|
5
|
-
import autoTable from 'jspdf-autotable';
|
|
5
|
+
import { autoTable } from 'jspdf-autotable';
|
|
6
6
|
import { FiDownload } from 'react-icons/fi';
|
|
7
7
|
import * as XLSX from 'xlsx';
|
|
8
8
|
const TableDownload = ({ columns, data }) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Dropdown, DropdownItem } from 'flowbite-react';
|
|
3
3
|
import { jsPDF } from 'jspdf';
|
|
4
|
-
import autoTable from 'jspdf-autotable';
|
|
4
|
+
import { autoTable } from 'jspdf-autotable';
|
|
5
5
|
import { FiDownload } from 'react-icons/fi';
|
|
6
6
|
import * as XLSX from 'xlsx';
|
|
7
7
|
const TableDownload = ({ columns, data }) => {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'jspdf-autotable';
|
|
2
1
|
import { type MRT_ColumnDef } from 'mantine-react-table';
|
|
3
2
|
interface ThemeColors {
|
|
4
3
|
primary: {
|
|
@@ -36,9 +35,9 @@ interface CsvExportOptions {
|
|
|
36
35
|
delimiter?: string;
|
|
37
36
|
filename?: string;
|
|
38
37
|
}
|
|
39
|
-
export declare const exportToCsv: <T extends Record<string, any>>(data: T[],
|
|
40
|
-
export declare const exportToXlsx: <T extends Record<string, any>>(data: T[],
|
|
41
|
-
export declare const exportToPdf: <T extends Record<string, any>>(data: T[],
|
|
38
|
+
export declare const exportToCsv: <T extends Record<string, any>>(data: T[], allColumns: MRT_ColumnDef<T>[], options?: CsvExportOptions) => void;
|
|
39
|
+
export declare const exportToXlsx: <T extends Record<string, any>>(data: T[], allColumns: MRT_ColumnDef<T>[], options?: XlsxExportOptions) => void;
|
|
40
|
+
export declare const exportToPdf: <T extends Record<string, any>>(data: T[], allColumns: MRT_ColumnDef<T>[], options?: PdfExportOptions) => Promise<void>;
|
|
42
41
|
/**
|
|
43
42
|
* Sends an export file to the user via email using multipart/form-data
|
|
44
43
|
* Automatically includes Authorization header from cookies if available
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import jsPDF from 'jspdf';
|
|
2
|
-
import 'jspdf-autotable';
|
|
1
|
+
import { jsPDF } from 'jspdf';
|
|
2
|
+
import { applyPlugin } from 'jspdf-autotable';
|
|
3
3
|
import Papa from 'papaparse';
|
|
4
4
|
import * as XLSX from 'xlsx';
|
|
5
|
+
// jspdf-autotable v5 no longer auto-attaches `autoTable` to jsPDF instances.
|
|
6
|
+
// applyPlugin restores the `doc.autoTable(...)` call style used below.
|
|
7
|
+
applyPlugin(jsPDF);
|
|
5
8
|
// Helper function to convert RGB string to RGB values
|
|
6
9
|
const parseRgbString = (rgbString) => {
|
|
7
10
|
// Handle rgb(r, g, b) format
|
|
@@ -42,6 +45,50 @@ const loadImageAsBase64 = async (url) => {
|
|
|
42
45
|
return null;
|
|
43
46
|
}
|
|
44
47
|
};
|
|
48
|
+
/**
|
|
49
|
+
* jsPDF's addImage only embeds raster formats (PNG/JPEG/WEBP), not SVG. Many
|
|
50
|
+
* logos are SVGs, so rasterise any SVG data URL onto a canvas and return a PNG
|
|
51
|
+
* data URL. Raster inputs pass through unchanged.
|
|
52
|
+
*/
|
|
53
|
+
const ensureRasterImage = async (dataUrl,
|
|
54
|
+
// High render width so SVG logos stay crisp once scaled down into the PDF
|
|
55
|
+
// (the logo prints at ~30mm; rendering at 600px gives ample DPI headroom).
|
|
56
|
+
targetWidthPx = 600) => {
|
|
57
|
+
const isSvg = dataUrl.startsWith('data:image/svg');
|
|
58
|
+
if (!isSvg) {
|
|
59
|
+
const match = /^data:image\/(png|jpeg|jpg|webp)/i.exec(dataUrl);
|
|
60
|
+
if (!match)
|
|
61
|
+
return null;
|
|
62
|
+
const fmt = match[1].toUpperCase();
|
|
63
|
+
return {
|
|
64
|
+
dataUrl,
|
|
65
|
+
format: fmt === 'JPG' ? 'JPEG' : fmt,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
const img = new Image();
|
|
70
|
+
img.onload = () => {
|
|
71
|
+
// SVGs may report 0 intrinsic size; fall back to a 2:1 logo ratio so the
|
|
72
|
+
// canvas is never degenerate (which would yield a blank/blurry raster).
|
|
73
|
+
const ratio = img.naturalWidth > 0 ? img.naturalHeight / img.naturalWidth : 0.5;
|
|
74
|
+
const canvas = document.createElement('canvas');
|
|
75
|
+
canvas.width = targetWidthPx;
|
|
76
|
+
canvas.height = Math.max(1, Math.round(targetWidthPx * ratio));
|
|
77
|
+
const ctx = canvas.getContext('2d');
|
|
78
|
+
if (!ctx)
|
|
79
|
+
return resolve(null);
|
|
80
|
+
ctx.imageSmoothingEnabled = true;
|
|
81
|
+
ctx.imageSmoothingQuality = 'high';
|
|
82
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
83
|
+
resolve({ dataUrl: canvas.toDataURL('image/png'), format: 'PNG' });
|
|
84
|
+
};
|
|
85
|
+
img.onerror = () => resolve(null);
|
|
86
|
+
// Force the SVG to rasterise at the target size rather than its (often
|
|
87
|
+
// missing) intrinsic size.
|
|
88
|
+
img.width = targetWidthPx;
|
|
89
|
+
img.src = dataUrl;
|
|
90
|
+
});
|
|
91
|
+
};
|
|
45
92
|
// Helper functions for formatting different data types
|
|
46
93
|
const formatStatusObject = (value) => {
|
|
47
94
|
if ('label' in value && typeof value.label === 'string') {
|
|
@@ -224,8 +271,10 @@ const formatCellContentForExcel = (value, columnKey) => {
|
|
|
224
271
|
return String(value);
|
|
225
272
|
};
|
|
226
273
|
// CSV Export
|
|
227
|
-
export const exportToCsv = (data,
|
|
274
|
+
export const exportToCsv = (data, allColumns, options = {}) => {
|
|
228
275
|
const { title = 'Data Export', includeTimestamp = true, includeHeaders = true, delimiter = ',', filename } = options;
|
|
276
|
+
// Drop display-only columns (no accessorKey) so keys/headers stay aligned.
|
|
277
|
+
const columns = allColumns.filter((col) => Boolean(col.accessorKey));
|
|
229
278
|
const columnKeys = columns.map((col) => col.accessorKey);
|
|
230
279
|
const headers = columns.map((col) => formatHeaderText(col.header || ''));
|
|
231
280
|
// Prepare data with proper formatting
|
|
@@ -471,7 +520,9 @@ const addMergedCells = (worksheet, title, hasSubtitle, headers) => {
|
|
|
471
520
|
}
|
|
472
521
|
};
|
|
473
522
|
// XLSX Export
|
|
474
|
-
export const exportToXlsx = (data,
|
|
523
|
+
export const exportToXlsx = (data, allColumns, options = {}) => {
|
|
524
|
+
// Drop display-only columns (no accessorKey) so keys/headers stay aligned.
|
|
525
|
+
const columns = allColumns.filter((col) => Boolean(col.accessorKey));
|
|
475
526
|
const { title = 'Data Export', subtitle, colors, sheetName = 'Data', includeTimestamp = true, autoFitColumns = true, } = options;
|
|
476
527
|
const columnKeys = columns.map((col) => col.accessorKey);
|
|
477
528
|
const headers = columns.map((col) => formatHeaderText(col.header || ''));
|
|
@@ -509,6 +560,11 @@ export const exportToXlsx = (data, columns, options = {}) => {
|
|
|
509
560
|
};
|
|
510
561
|
// Helper function to check if a column likely contains images
|
|
511
562
|
const isImageColumn = (columnKey, data) => {
|
|
563
|
+
// Display-only columns (e.g. an "actions" column) have no accessorKey, so
|
|
564
|
+
// columnKey can be undefined here. They hold no exportable data.
|
|
565
|
+
if (!columnKey) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
512
568
|
const imageKeywords = ['image', 'photo', 'picture', 'avatar', 'logo'];
|
|
513
569
|
const keyLower = columnKey.toLowerCase();
|
|
514
570
|
// Check if column key suggests images
|
|
@@ -579,7 +635,11 @@ const prepareTableDataForPdf = async (data, columns, doc) => {
|
|
|
579
635
|
return { tableData, imageInfo };
|
|
580
636
|
};
|
|
581
637
|
// PDF Export
|
|
582
|
-
export const exportToPdf = async (data,
|
|
638
|
+
export const exportToPdf = async (data, allColumns, options = {}) => {
|
|
639
|
+
// Only data-bound columns are exportable; display-only columns (e.g. an
|
|
640
|
+
// "actions" column) have no accessorKey and would otherwise produce
|
|
641
|
+
// undefined keys/headers that crash downstream formatting.
|
|
642
|
+
const columns = allColumns.filter((col) => Boolean(col.accessorKey));
|
|
583
643
|
const { title = 'Data Export', subtitle = `Generated on ${new Date().toLocaleDateString('en-US', {
|
|
584
644
|
year: 'numeric',
|
|
585
645
|
month: 'long',
|
|
@@ -606,10 +666,11 @@ export const exportToPdf = async (data, columns, options = {}) => {
|
|
|
606
666
|
if (logo?.url) {
|
|
607
667
|
try {
|
|
608
668
|
const logoBase64 = await loadImageAsBase64(logo.url);
|
|
609
|
-
|
|
669
|
+
const raster = logoBase64 ? await ensureRasterImage(logoBase64) : null;
|
|
670
|
+
if (raster) {
|
|
610
671
|
const logoWidth = logo.width || 30;
|
|
611
672
|
const logoHeight = logo.height || 15;
|
|
612
|
-
doc.addImage(
|
|
673
|
+
doc.addImage(raster.dataUrl, raster.format, margin, currentY, logoWidth, logoHeight);
|
|
613
674
|
currentY += logoHeight + 5;
|
|
614
675
|
}
|
|
615
676
|
}
|
|
@@ -638,7 +699,6 @@ export const exportToPdf = async (data, columns, options = {}) => {
|
|
|
638
699
|
const columnHeaders = columns.map((col) => formatHeaderText(col.header || String(col.accessorKey)));
|
|
639
700
|
// Format table data with enhanced handling for images, status, etc.
|
|
640
701
|
const { tableData, imageInfo } = await prepareTableDataForPdf(data, columns, doc);
|
|
641
|
-
console.log(`PDF export: Processing ${tableData.length} rows with ${imageInfo.size} images`);
|
|
642
702
|
// Calculate optimal column widths
|
|
643
703
|
const availableWidth = pageWidth - 2 * margin;
|
|
644
704
|
const numColumns = columnHeaders.length;
|
|
@@ -20,11 +20,23 @@ export const createPdfExportOptions = (config, title) => {
|
|
|
20
20
|
primary: config.theme.colors.primary,
|
|
21
21
|
core: config.theme.colors.core,
|
|
22
22
|
},
|
|
23
|
-
logo:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
logo: scalePdfLogo(config.branding.navbarLogo?.path || config.branding.logo.path, config.branding.navbarLogo?.width || config.branding.logo.width, config.branding.navbarLogo?.height || config.branding.logo.height),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Branding logo dimensions in `config` are CSS pixels (sized for the navbar).
|
|
28
|
+
* `exportToPdf` interprets logo width/height as millimetres, so passing the raw
|
|
29
|
+
* pixel values renders a giant logo that fills the page. Scale to a fixed print
|
|
30
|
+
* width (mm) while preserving the source aspect ratio.
|
|
31
|
+
*/
|
|
32
|
+
const PDF_LOGO_WIDTH_MM = 32;
|
|
33
|
+
const PDF_LOGO_FALLBACK_HEIGHT_MM = 12;
|
|
34
|
+
const scalePdfLogo = (url, pxWidth, pxHeight) => {
|
|
35
|
+
const aspect = pxWidth && pxHeight && pxWidth > 0 ? pxHeight / pxWidth : undefined;
|
|
36
|
+
return {
|
|
37
|
+
url,
|
|
38
|
+
width: PDF_LOGO_WIDTH_MM,
|
|
39
|
+
height: aspect ? Math.round(PDF_LOGO_WIDTH_MM * aspect) : PDF_LOGO_FALLBACK_HEIGHT_MM,
|
|
28
40
|
};
|
|
29
41
|
};
|
|
30
42
|
/**
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useInfiniteCursor — TanStack `useInfiniteQuery` wrapper for cursor-paginated
|
|
3
|
+
* Pagamio endpoints.
|
|
4
|
+
*
|
|
5
|
+
* Matches the backend cursor contract from `pagamio-nestjs-api-commons`:
|
|
6
|
+
* `GET /<resource>/feed` → CursorPaginatedResponseDto<T> =
|
|
7
|
+
* { data: T[], meta: { nextCursor, previousCursor, hasNextPage,
|
|
8
|
+
* hasPreviousPage, count, total? } }
|
|
9
|
+
*
|
|
10
|
+
* The hook treats `nextCursor` as an opaque base64 string — never parse or
|
|
11
|
+
* construct cursors on the client. If the backend changes its cursor encoding,
|
|
12
|
+
* the frontend doesn't need to know.
|
|
13
|
+
*
|
|
14
|
+
* For tables and any page-numbered UI, use `usePagamioTable` (offset paging)
|
|
15
|
+
* instead. Cursor pagination is for infinite-scroll surfaces (storefront,
|
|
16
|
+
* mobile feeds) where "Page X of Y" UX isn't shown.
|
|
17
|
+
*
|
|
18
|
+
* SRP: Wires `useInfiniteQuery` to the cursor contract. Callers own:
|
|
19
|
+
* - flattening `data.pages` into a single list
|
|
20
|
+
* - intersection-observer / "load more" wiring
|
|
21
|
+
* - reading `data.pages[0]?.meta.total` for header counts (only present
|
|
22
|
+
* when the caller passed `withTotal=true` on the first page).
|
|
23
|
+
*/
|
|
24
|
+
import { type QueryKey, type UseInfiniteQueryResult } from '@tanstack/react-query';
|
|
25
|
+
/**
|
|
26
|
+
* Wire shape for one page of a cursor-paginated Pagamio response.
|
|
27
|
+
*
|
|
28
|
+
* Mirrors `CursorPaginatedResponseDto<T>` from `@pagamio/nestjs-api-commons`.
|
|
29
|
+
* `total` is optional and only set when the caller passes `withTotal=true`
|
|
30
|
+
* (conventionally first page only — see backend `.agent/conventions/pagination.md`).
|
|
31
|
+
*/
|
|
32
|
+
export type CursorResponse<T> = {
|
|
33
|
+
data: T[];
|
|
34
|
+
meta: {
|
|
35
|
+
nextCursor: string | null;
|
|
36
|
+
previousCursor: string | null;
|
|
37
|
+
hasNextPage: boolean;
|
|
38
|
+
hasPreviousPage: boolean;
|
|
39
|
+
count: number;
|
|
40
|
+
total?: number;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Documentation-as-code marker for the cursor meta shape. Mirrors the role
|
|
45
|
+
* of `pagamioMapping` for offset pagination — gives consumers a single named
|
|
46
|
+
* export to reference. Not consumed by `useInfiniteCursor` itself (TanStack
|
|
47
|
+
* handles the wiring); useful for grep / IDE jump-to-definition.
|
|
48
|
+
*/
|
|
49
|
+
export declare const cursorMapping: {
|
|
50
|
+
responseType: "cursor";
|
|
51
|
+
data: string;
|
|
52
|
+
nextCursor: string;
|
|
53
|
+
hasNextPage: string;
|
|
54
|
+
total: string;
|
|
55
|
+
};
|
|
56
|
+
export interface UseInfiniteCursorConfig<T> {
|
|
57
|
+
/** TanStack query key. Include scope params (orgId, BU id, filters) so
|
|
58
|
+
* changes invalidate correctly. */
|
|
59
|
+
queryKey: QueryKey;
|
|
60
|
+
/** Fetches one page. Receives the cursor for the next page (undefined on
|
|
61
|
+
* first page) and an AbortSignal forwarded from TanStack. */
|
|
62
|
+
queryFn: (args: {
|
|
63
|
+
cursor: string | undefined;
|
|
64
|
+
signal: AbortSignal;
|
|
65
|
+
}) => Promise<CursorResponse<T>>;
|
|
66
|
+
enabled?: boolean;
|
|
67
|
+
staleTime?: number;
|
|
68
|
+
gcTime?: number;
|
|
69
|
+
/**
|
|
70
|
+
* When true, the previous page set stays visible while the query refetches
|
|
71
|
+
* after a key change (filter swap, sort change). Prevents the consumer's
|
|
72
|
+
* grid from flashing to a skeleton between filter changes. Maps to
|
|
73
|
+
* TanStack's `placeholderData: keepPreviousData`.
|
|
74
|
+
*/
|
|
75
|
+
keepPreviousResults?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Cursor-paginated infinite query for Pagamio `/feed` endpoints.
|
|
79
|
+
*
|
|
80
|
+
* Returns the raw TanStack `useInfiniteQueryResult` — no custom shape — so
|
|
81
|
+
* callers keep full access to `fetchNextPage`, `hasNextPage`, `isFetchingNextPage`,
|
|
82
|
+
* `data.pages`, etc.
|
|
83
|
+
*
|
|
84
|
+
* Example:
|
|
85
|
+
* ```ts
|
|
86
|
+
* const query = useInfiniteCursor<Product>({
|
|
87
|
+
* queryKey: ['catalog', 'products', { businessUnitId, search }],
|
|
88
|
+
* queryFn: ({ cursor, signal }) =>
|
|
89
|
+
* apiClient
|
|
90
|
+
* .get('/ecommerce/catalog/products/feed', {
|
|
91
|
+
* params: { businessUnitId, cursor, limit: 30, withTotal: cursor === undefined },
|
|
92
|
+
* signal,
|
|
93
|
+
* })
|
|
94
|
+
* .then((r) => r.data),
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* const items = query.data?.pages.flatMap((p) => p.data) ?? [];
|
|
98
|
+
* const total = query.data?.pages[0]?.meta.total;
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function useInfiniteCursor<T>({ queryKey, queryFn, enabled, staleTime, gcTime, keepPreviousResults, }: UseInfiniteCursorConfig<T>): UseInfiniteQueryResult<{
|
|
102
|
+
pages: Array<CursorResponse<T>>;
|
|
103
|
+
pageParams: Array<string | undefined>;
|
|
104
|
+
}, Error>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useInfiniteCursor — TanStack `useInfiniteQuery` wrapper for cursor-paginated
|
|
3
|
+
* Pagamio endpoints.
|
|
4
|
+
*
|
|
5
|
+
* Matches the backend cursor contract from `pagamio-nestjs-api-commons`:
|
|
6
|
+
* `GET /<resource>/feed` → CursorPaginatedResponseDto<T> =
|
|
7
|
+
* { data: T[], meta: { nextCursor, previousCursor, hasNextPage,
|
|
8
|
+
* hasPreviousPage, count, total? } }
|
|
9
|
+
*
|
|
10
|
+
* The hook treats `nextCursor` as an opaque base64 string — never parse or
|
|
11
|
+
* construct cursors on the client. If the backend changes its cursor encoding,
|
|
12
|
+
* the frontend doesn't need to know.
|
|
13
|
+
*
|
|
14
|
+
* For tables and any page-numbered UI, use `usePagamioTable` (offset paging)
|
|
15
|
+
* instead. Cursor pagination is for infinite-scroll surfaces (storefront,
|
|
16
|
+
* mobile feeds) where "Page X of Y" UX isn't shown.
|
|
17
|
+
*
|
|
18
|
+
* SRP: Wires `useInfiniteQuery` to the cursor contract. Callers own:
|
|
19
|
+
* - flattening `data.pages` into a single list
|
|
20
|
+
* - intersection-observer / "load more" wiring
|
|
21
|
+
* - reading `data.pages[0]?.meta.total` for header counts (only present
|
|
22
|
+
* when the caller passed `withTotal=true` on the first page).
|
|
23
|
+
*/
|
|
24
|
+
import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query';
|
|
25
|
+
/**
|
|
26
|
+
* Documentation-as-code marker for the cursor meta shape. Mirrors the role
|
|
27
|
+
* of `pagamioMapping` for offset pagination — gives consumers a single named
|
|
28
|
+
* export to reference. Not consumed by `useInfiniteCursor` itself (TanStack
|
|
29
|
+
* handles the wiring); useful for grep / IDE jump-to-definition.
|
|
30
|
+
*/
|
|
31
|
+
export const cursorMapping = {
|
|
32
|
+
responseType: 'cursor',
|
|
33
|
+
data: 'data',
|
|
34
|
+
nextCursor: 'meta.nextCursor',
|
|
35
|
+
hasNextPage: 'meta.hasNextPage',
|
|
36
|
+
total: 'meta.total',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Cursor-paginated infinite query for Pagamio `/feed` endpoints.
|
|
40
|
+
*
|
|
41
|
+
* Returns the raw TanStack `useInfiniteQueryResult` — no custom shape — so
|
|
42
|
+
* callers keep full access to `fetchNextPage`, `hasNextPage`, `isFetchingNextPage`,
|
|
43
|
+
* `data.pages`, etc.
|
|
44
|
+
*
|
|
45
|
+
* Example:
|
|
46
|
+
* ```ts
|
|
47
|
+
* const query = useInfiniteCursor<Product>({
|
|
48
|
+
* queryKey: ['catalog', 'products', { businessUnitId, search }],
|
|
49
|
+
* queryFn: ({ cursor, signal }) =>
|
|
50
|
+
* apiClient
|
|
51
|
+
* .get('/ecommerce/catalog/products/feed', {
|
|
52
|
+
* params: { businessUnitId, cursor, limit: 30, withTotal: cursor === undefined },
|
|
53
|
+
* signal,
|
|
54
|
+
* })
|
|
55
|
+
* .then((r) => r.data),
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* const items = query.data?.pages.flatMap((p) => p.data) ?? [];
|
|
59
|
+
* const total = query.data?.pages[0]?.meta.total;
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function useInfiniteCursor({ queryKey, queryFn, enabled, staleTime, gcTime, keepPreviousResults, }) {
|
|
63
|
+
return useInfiniteQuery({
|
|
64
|
+
queryKey,
|
|
65
|
+
queryFn: ({ pageParam, signal }) => queryFn({ cursor: pageParam, signal }),
|
|
66
|
+
initialPageParam: undefined,
|
|
67
|
+
getNextPageParam: (lastPage) => lastPage.meta.hasNextPage && lastPage.meta.nextCursor ? lastPage.meta.nextCursor : undefined,
|
|
68
|
+
enabled,
|
|
69
|
+
staleTime,
|
|
70
|
+
gcTime,
|
|
71
|
+
...(keepPreviousResults ? { placeholderData: keepPreviousData } : {}),
|
|
72
|
+
});
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagamio/frontend-commons-lib",
|
|
3
3
|
"description": "Pagamio library for Frontend reusable components like the form engine and table container",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.351",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"provenance": false
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"dayjs": "^1.11.13",
|
|
101
101
|
"file-saver": "^2.0.5",
|
|
102
102
|
"js-cookie": "^3.0.5",
|
|
103
|
-
"jspdf": "^2.
|
|
104
|
-
"jspdf-autotable": "^
|
|
103
|
+
"jspdf": "^4.2.1",
|
|
104
|
+
"jspdf-autotable": "^5.0.8",
|
|
105
105
|
"jwt-decode": "^4.0.0",
|
|
106
106
|
"lodash": "^4.17.21",
|
|
107
107
|
"papaparse": "^5.4.1",
|