@pagamio/frontend-commons-lib 0.8.212 → 0.8.214
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/api/TanstackQueryProvider.d.ts +146 -0
- package/lib/api/TanstackQueryProvider.js +140 -0
- package/lib/api/index.d.ts +2 -0
- package/lib/api/index.js +2 -0
- package/lib/api/tanstackQuery.d.ts +249 -0
- package/lib/api/tanstackQuery.js +299 -0
- package/lib/components/layout/Sidebar.js +50 -16
- package/lib/components/ui/Sheet.d.ts +1 -1
- package/lib/context/SidebarContext.d.ts +20 -0
- package/lib/dashboard-visuals-v2/DashboardWrapperV2.d.ts +81 -0
- package/lib/dashboard-visuals-v2/DashboardWrapperV2.js +217 -0
- package/lib/dashboard-visuals-v2/cards/CardGrid.d.ts +22 -0
- package/lib/dashboard-visuals-v2/cards/CardGrid.js +27 -0
- package/lib/dashboard-visuals-v2/cards/InfoCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/InfoCard.js +26 -0
- package/lib/dashboard-visuals-v2/cards/MetricCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/MetricCard.js +37 -0
- package/lib/dashboard-visuals-v2/cards/StatCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/StatCard.js +92 -0
- package/lib/dashboard-visuals-v2/cards/TopItemsCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/TopItemsCard.js +34 -0
- package/lib/dashboard-visuals-v2/cards/TransactionListCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/TransactionListCard.js +24 -0
- package/lib/dashboard-visuals-v2/cards/index.d.ts +9 -0
- package/lib/dashboard-visuals-v2/cards/index.js +9 -0
- package/lib/dashboard-visuals-v2/charts/AreaChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/AreaChart.js +124 -0
- package/lib/dashboard-visuals-v2/charts/BarChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/BarChart.js +106 -0
- package/lib/dashboard-visuals-v2/charts/BaseChart.d.ts +61 -0
- package/lib/dashboard-visuals-v2/charts/BaseChart.js +173 -0
- package/lib/dashboard-visuals-v2/charts/DonutChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/DonutChart.js +108 -0
- package/lib/dashboard-visuals-v2/charts/HeatmapChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/HeatmapChart.js +101 -0
- package/lib/dashboard-visuals-v2/charts/LineChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/LineChart.js +109 -0
- package/lib/dashboard-visuals-v2/charts/MixedChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/MixedChart.js +106 -0
- package/lib/dashboard-visuals-v2/charts/PieChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/PieChart.js +10 -0
- package/lib/dashboard-visuals-v2/charts/RadialChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/RadialChart.js +72 -0
- package/lib/dashboard-visuals-v2/charts/index.d.ts +12 -0
- package/lib/dashboard-visuals-v2/charts/index.js +12 -0
- package/lib/dashboard-visuals-v2/components/DataFetchingVisual.d.ts +0 -0
- package/lib/dashboard-visuals-v2/components/DataFetchingVisual.js +1 -0
- package/lib/dashboard-visuals-v2/components/index.d.ts +0 -0
- package/lib/dashboard-visuals-v2/components/index.js +1 -0
- package/lib/dashboard-visuals-v2/hooks/index.d.ts +4 -0
- package/lib/dashboard-visuals-v2/hooks/index.js +4 -0
- package/lib/dashboard-visuals-v2/hooks/useChartData.d.ts +72 -0
- package/lib/dashboard-visuals-v2/hooks/useChartData.js +122 -0
- package/lib/dashboard-visuals-v2/index.d.ts +10 -0
- package/lib/dashboard-visuals-v2/index.js +16 -0
- package/lib/dashboard-visuals-v2/types/card.types.d.ts +237 -0
- package/lib/dashboard-visuals-v2/types/card.types.js +30 -0
- package/lib/dashboard-visuals-v2/types/chart.types.d.ts +308 -0
- package/lib/dashboard-visuals-v2/types/chart.types.js +25 -0
- package/lib/dashboard-visuals-v2/types/index.d.ts +6 -0
- package/lib/dashboard-visuals-v2/types/index.js +6 -0
- package/lib/dashboard-visuals-v2/utils/index.d.ts +0 -0
- package/lib/dashboard-visuals-v2/utils/index.js +1 -0
- package/lib/dashboard-visuals-v2/utils/propsTransformer.d.ts +0 -0
- package/lib/dashboard-visuals-v2/utils/propsTransformer.js +1 -0
- package/lib/dashboard-visuals-v2/visualRegistry.d.ts +59 -0
- package/lib/dashboard-visuals-v2/visualRegistry.js +110 -0
- package/lib/data-table-v2/DataTable.d.ts +7 -0
- package/lib/data-table-v2/DataTable.js +206 -0
- package/lib/data-table-v2/components/DataTableBody.d.ts +19 -0
- package/lib/data-table-v2/components/DataTableBody.js +20 -0
- package/lib/data-table-v2/components/DataTableEmpty.d.ts +17 -0
- package/lib/data-table-v2/components/DataTableEmpty.js +13 -0
- package/lib/data-table-v2/components/DataTableError.d.ts +16 -0
- package/lib/data-table-v2/components/DataTableError.js +14 -0
- package/lib/data-table-v2/components/DataTableHeader.d.ts +15 -0
- package/lib/data-table-v2/components/DataTableHeader.js +31 -0
- package/lib/data-table-v2/components/DataTableLoading.d.ts +14 -0
- package/lib/data-table-v2/components/DataTableLoading.js +19 -0
- package/lib/data-table-v2/components/DataTablePagination.d.ts +36 -0
- package/lib/data-table-v2/components/DataTablePagination.js +20 -0
- package/lib/data-table-v2/components/DataTableRowActions.d.ts +13 -0
- package/lib/data-table-v2/components/DataTableRowActions.js +57 -0
- package/lib/data-table-v2/components/DataTableSearch.d.ts +19 -0
- package/lib/data-table-v2/components/DataTableSearch.js +33 -0
- package/lib/data-table-v2/components/DataTableToolbar.d.ts +54 -0
- package/lib/data-table-v2/components/DataTableToolbar.js +28 -0
- package/lib/data-table-v2/components/index.d.ts +12 -0
- package/lib/data-table-v2/components/index.js +12 -0
- package/lib/data-table-v2/hooks/index.d.ts +4 -0
- package/lib/data-table-v2/hooks/index.js +4 -0
- package/lib/data-table-v2/hooks/useTableData.d.ts +118 -0
- package/lib/data-table-v2/hooks/useTableData.js +210 -0
- package/lib/data-table-v2/index.d.ts +9 -0
- package/lib/data-table-v2/index.js +12 -0
- package/lib/data-table-v2/types/index.d.ts +296 -0
- package/lib/data-table-v2/types/index.js +1 -0
- package/lib/data-table-v2/utils/export.d.ts +26 -0
- package/lib/data-table-v2/utils/export.js +92 -0
- package/lib/data-table-v2/utils/index.d.ts +4 -0
- package/lib/data-table-v2/utils/index.js +4 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +23 -0
- package/lib/styles.css +219 -0
- package/package.json +7 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RowAction } from '../types';
|
|
2
|
+
interface DataTableRowActionsProps<TData> {
|
|
3
|
+
/** Row data */
|
|
4
|
+
row: TData;
|
|
5
|
+
/** Actions configuration */
|
|
6
|
+
actions: RowAction<TData>[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* DataTable Row Actions Component
|
|
10
|
+
* Dropdown menu for row-level actions
|
|
11
|
+
*/
|
|
12
|
+
export declare function DataTableRowActions<TData>({ row, actions }: DataTableRowActionsProps<TData>): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
export default DataTableRowActions;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview DataTable Row Actions Component
|
|
4
|
+
*/
|
|
5
|
+
import { MoreHorizontal } from 'lucide-react';
|
|
6
|
+
import { useEffect, useRef, useState } from 'react';
|
|
7
|
+
import { cn } from '../../helpers';
|
|
8
|
+
/**
|
|
9
|
+
* DataTable Row Actions Component
|
|
10
|
+
* Dropdown menu for row-level actions
|
|
11
|
+
*/
|
|
12
|
+
export function DataTableRowActions({ row, actions }) {
|
|
13
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
14
|
+
const menuRef = useRef(null);
|
|
15
|
+
// Close menu on outside click
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handleClickOutside = (event) => {
|
|
18
|
+
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
19
|
+
setIsOpen(false);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
if (isOpen) {
|
|
23
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
24
|
+
}
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
27
|
+
};
|
|
28
|
+
}, [isOpen]);
|
|
29
|
+
// Filter visible actions
|
|
30
|
+
const visibleActions = actions.filter((action) => {
|
|
31
|
+
if (typeof action.hidden === 'function') {
|
|
32
|
+
return !action.hidden(row);
|
|
33
|
+
}
|
|
34
|
+
return !action.hidden;
|
|
35
|
+
});
|
|
36
|
+
if (visibleActions.length === 0) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return (_jsxs("div", { className: "relative", ref: menuRef, children: [_jsx("button", { onClick: (e) => {
|
|
40
|
+
e.stopPropagation();
|
|
41
|
+
setIsOpen(!isOpen);
|
|
42
|
+
}, className: "p-1.5 rounded-md hover:bg-gray-100 transition-colors", children: _jsx(MoreHorizontal, { className: "w-4 h-4 text-gray-500" }) }), isOpen && (_jsx("div", { className: "absolute right-0 top-full mt-1 w-40 bg-white border border-gray-200 rounded-lg shadow-lg z-30", children: _jsx("div", { className: "py-1", children: visibleActions.map((action) => {
|
|
43
|
+
const isDisabled = typeof action.disabled === 'function' ? action.disabled(row) : action.disabled;
|
|
44
|
+
return (_jsxs("button", { onClick: (e) => {
|
|
45
|
+
e.stopPropagation();
|
|
46
|
+
if (!isDisabled) {
|
|
47
|
+
action.onClick(row);
|
|
48
|
+
setIsOpen(false);
|
|
49
|
+
}
|
|
50
|
+
}, disabled: isDisabled, className: cn('w-full flex items-center gap-2 px-3 py-2 text-sm text-left transition-colors', action.variant === 'danger'
|
|
51
|
+
? 'text-red-600 hover:bg-red-50'
|
|
52
|
+
: action.variant === 'primary'
|
|
53
|
+
? 'text-blue-600 hover:bg-blue-50'
|
|
54
|
+
: 'text-gray-700 hover:bg-gray-50', isDisabled && 'opacity-50 cursor-not-allowed hover:bg-transparent'), children: [action.icon && _jsx("span", { className: "w-4 h-4", children: action.icon }), action.label] }, action.id));
|
|
55
|
+
}) }) }))] }));
|
|
56
|
+
}
|
|
57
|
+
export default DataTableRowActions;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DataTableClassNames } from '../types';
|
|
2
|
+
interface DataTableSearchProps {
|
|
3
|
+
/** Current global filter value */
|
|
4
|
+
value: string;
|
|
5
|
+
/** On change handler */
|
|
6
|
+
onChange: (value: string) => void;
|
|
7
|
+
/** Placeholder text */
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
/** Debounce delay in ms */
|
|
10
|
+
debounceMs?: number;
|
|
11
|
+
/** Custom classNames */
|
|
12
|
+
classNames?: DataTableClassNames;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* DataTable Search Component
|
|
16
|
+
* Global search/filter input with debounce
|
|
17
|
+
*/
|
|
18
|
+
export declare function DataTableSearch({ value: externalValue, onChange, placeholder, debounceMs, classNames, }: DataTableSearchProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export default DataTableSearch;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview DataTable Search/Filter Component
|
|
4
|
+
*/
|
|
5
|
+
import { Search, X } from 'lucide-react';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
import { cn } from '../../helpers';
|
|
8
|
+
/**
|
|
9
|
+
* DataTable Search Component
|
|
10
|
+
* Global search/filter input with debounce
|
|
11
|
+
*/
|
|
12
|
+
export function DataTableSearch({ value: externalValue, onChange, placeholder = 'Search...', debounceMs = 300, classNames, }) {
|
|
13
|
+
const [value, setValue] = useState(externalValue);
|
|
14
|
+
// Sync external value
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
setValue(externalValue);
|
|
17
|
+
}, [externalValue]);
|
|
18
|
+
// Debounced onChange
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const timer = setTimeout(() => {
|
|
21
|
+
if (value !== externalValue) {
|
|
22
|
+
onChange(value);
|
|
23
|
+
}
|
|
24
|
+
}, debounceMs);
|
|
25
|
+
return () => clearTimeout(timer);
|
|
26
|
+
}, [value, debounceMs, onChange, externalValue]);
|
|
27
|
+
const handleClear = () => {
|
|
28
|
+
setValue('');
|
|
29
|
+
onChange('');
|
|
30
|
+
};
|
|
31
|
+
return (_jsxs("div", { className: cn('relative', classNames?.search), children: [_jsx("div", { className: "absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none", children: _jsx(Search, { className: "w-4 h-4 text-gray-400" }) }), _jsx("input", { type: "text", value: value, onChange: (e) => setValue(e.target.value), placeholder: placeholder, className: "w-full pl-10 pr-10 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" }), value && (_jsx("button", { onClick: handleClear, className: "absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600", children: _jsx(X, { className: "w-4 h-4" }) }))] }));
|
|
32
|
+
}
|
|
33
|
+
export default DataTableSearch;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { BulkAction, ColumnVisibilityOptions, DataTableClassNames, ExportOptions } from '../types';
|
|
3
|
+
interface DataTableToolbarProps<TData> {
|
|
4
|
+
/** Table title */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** Table description */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Global filter value */
|
|
9
|
+
globalFilter: string;
|
|
10
|
+
/** Global filter setter */
|
|
11
|
+
setGlobalFilter: (value: string) => void;
|
|
12
|
+
/** Enable global filter */
|
|
13
|
+
enableGlobalFilter?: boolean;
|
|
14
|
+
/** Global filter placeholder */
|
|
15
|
+
globalFilterPlaceholder?: string;
|
|
16
|
+
/** Filter debounce time */
|
|
17
|
+
filterDebounceMs?: number;
|
|
18
|
+
/** Column visibility state */
|
|
19
|
+
columnVisibility: Record<string, boolean>;
|
|
20
|
+
/** Column visibility setter */
|
|
21
|
+
setColumnVisibility: (visibility: Record<string, boolean>) => void;
|
|
22
|
+
/** Column visibility options */
|
|
23
|
+
columnVisibilityOptions?: ColumnVisibilityOptions;
|
|
24
|
+
/** All columns for visibility toggle */
|
|
25
|
+
allColumns: Array<{
|
|
26
|
+
id: string;
|
|
27
|
+
getIsVisible: () => boolean;
|
|
28
|
+
toggleVisibility: (value: boolean) => void;
|
|
29
|
+
columnDef: {
|
|
30
|
+
header?: unknown;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
33
|
+
/** Export options */
|
|
34
|
+
exportOptions?: ExportOptions;
|
|
35
|
+
/** Export handler */
|
|
36
|
+
onExport?: (format: string) => void;
|
|
37
|
+
/** Refresh handler */
|
|
38
|
+
onRefresh?: () => void;
|
|
39
|
+
/** Selected row count */
|
|
40
|
+
selectedCount?: number;
|
|
41
|
+
/** Bulk actions */
|
|
42
|
+
bulkActions?: BulkAction<TData>[];
|
|
43
|
+
/** Selected rows for bulk actions */
|
|
44
|
+
selectedRows?: TData[];
|
|
45
|
+
/** Custom header content */
|
|
46
|
+
headerContent?: React.ReactNode;
|
|
47
|
+
/** Custom classNames */
|
|
48
|
+
classNames?: DataTableClassNames;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* DataTable Toolbar Component
|
|
52
|
+
*/
|
|
53
|
+
export declare function DataTableToolbar<TData>({ title, description, globalFilter, setGlobalFilter, enableGlobalFilter, globalFilterPlaceholder, filterDebounceMs, columnVisibility, setColumnVisibility, columnVisibilityOptions, allColumns, exportOptions, onExport, onRefresh, selectedCount, bulkActions, selectedRows, headerContent, classNames, }: DataTableToolbarProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
export default DataTableToolbar;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview DataTable Toolbar Component
|
|
4
|
+
*/
|
|
5
|
+
import { Columns, Download, RefreshCw } from 'lucide-react';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { cn } from '../../helpers';
|
|
8
|
+
import { DataTableSearch } from './DataTableSearch';
|
|
9
|
+
/**
|
|
10
|
+
* DataTable Toolbar Component
|
|
11
|
+
*/
|
|
12
|
+
export function DataTableToolbar({ title, description, globalFilter, setGlobalFilter, enableGlobalFilter = true, globalFilterPlaceholder, filterDebounceMs, columnVisibility, setColumnVisibility, columnVisibilityOptions, allColumns, exportOptions, onExport, onRefresh, selectedCount = 0, bulkActions, selectedRows = [], headerContent, classNames, }) {
|
|
13
|
+
const [showColumnMenu, setShowColumnMenu] = useState(false);
|
|
14
|
+
const [showExportMenu, setShowExportMenu] = useState(false);
|
|
15
|
+
return (_jsxs("div", { className: cn('px-4 py-3 border-b border-gray-200 bg-white', classNames?.toolbar), children: [(title || description) && (_jsxs("div", { className: "mb-3", children: [title && _jsx("h2", { className: "text-lg font-semibold text-gray-900", children: title }), description && _jsx("p", { className: "text-sm text-gray-500", children: description })] })), _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-3 flex-1", children: [enableGlobalFilter && (_jsx("div", { className: "w-64", children: _jsx(DataTableSearch, { value: globalFilter, onChange: setGlobalFilter, placeholder: globalFilterPlaceholder, debounceMs: filterDebounceMs, classNames: classNames }) })), selectedCount > 0 && bulkActions && bulkActions.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 ml-2 pl-2 border-l border-gray-200", children: [_jsxs("span", { className: "text-sm text-gray-600", children: [selectedCount, " selected"] }), bulkActions.map((action) => (_jsxs("button", { onClick: () => action.onClick(selectedRows), disabled: action.disabled, className: cn('px-3 py-1.5 text-sm font-medium rounded-md transition-colors', action.variant === 'danger'
|
|
16
|
+
? 'text-red-600 hover:bg-red-50'
|
|
17
|
+
: action.variant === 'primary'
|
|
18
|
+
? 'text-blue-600 hover:bg-blue-50'
|
|
19
|
+
: 'text-gray-600 hover:bg-gray-100', action.disabled && 'opacity-50 cursor-not-allowed'), children: [action.icon && _jsx("span", { className: "mr-1.5", children: action.icon }), action.label] }, action.id)))] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerContent, onRefresh && (_jsx("button", { onClick: onRefresh, className: "p-2 text-gray-600 hover:bg-gray-100 rounded-md transition-colors", title: "Refresh", children: _jsx(RefreshCw, { className: "w-4 h-4" }) })), columnVisibilityOptions?.enabled !== false && (_jsxs("div", { className: "relative", children: [_jsx("button", { onClick: () => setShowColumnMenu(!showColumnMenu), className: "p-2 text-gray-600 hover:bg-gray-100 rounded-md transition-colors", title: "Toggle columns", children: _jsx(Columns, { className: "w-4 h-4" }) }), showColumnMenu && (_jsx("div", { className: "absolute right-0 top-full mt-1 w-48 bg-white border border-gray-200 rounded-lg shadow-lg z-20", children: _jsx("div", { className: "p-2 max-h-64 overflow-y-auto", children: allColumns.map((column) => {
|
|
20
|
+
const isAlwaysVisible = columnVisibilityOptions?.alwaysVisible?.includes(column.id);
|
|
21
|
+
const header = typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id;
|
|
22
|
+
return (_jsxs("label", { className: cn('flex items-center gap-2 px-2 py-1.5 rounded hover:bg-gray-50 cursor-pointer', isAlwaysVisible && 'opacity-50 cursor-not-allowed'), children: [_jsx("input", { type: "checkbox", checked: column.getIsVisible(), onChange: (e) => column.toggleVisibility(e.target.checked), disabled: isAlwaysVisible, className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500" }), _jsx("span", { className: "text-sm text-gray-700", children: header })] }, column.id));
|
|
23
|
+
}) }) }))] })), exportOptions?.enabled && onExport && (_jsxs("div", { className: "relative", children: [_jsxs("button", { onClick: () => setShowExportMenu(!showExportMenu), className: "flex items-center gap-1.5 px-3 py-2 text-sm font-medium text-gray-600 hover:bg-gray-100 rounded-md transition-colors", children: [_jsx(Download, { className: "w-4 h-4" }), "Export"] }), showExportMenu && (_jsx("div", { className: "absolute right-0 top-full mt-1 w-36 bg-white border border-gray-200 rounded-lg shadow-lg z-20", children: _jsx("div", { className: "p-1", children: (exportOptions.formats ?? ['csv', 'excel', 'json']).map((format) => (_jsxs("button", { onClick: () => {
|
|
24
|
+
onExport(format);
|
|
25
|
+
setShowExportMenu(false);
|
|
26
|
+
}, className: "w-full px-3 py-2 text-sm text-left text-gray-700 hover:bg-gray-50 rounded", children: ["Export as ", format.toUpperCase()] }, format))) }) }))] }))] })] })] }));
|
|
27
|
+
}
|
|
28
|
+
export default DataTableToolbar;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DataTable V2 Components Index
|
|
3
|
+
*/
|
|
4
|
+
export { DataTableHeader } from './DataTableHeader';
|
|
5
|
+
export { DataTableBody } from './DataTableBody';
|
|
6
|
+
export { DataTablePagination } from './DataTablePagination';
|
|
7
|
+
export { DataTableSearch } from './DataTableSearch';
|
|
8
|
+
export { DataTableToolbar } from './DataTableToolbar';
|
|
9
|
+
export { DataTableEmpty } from './DataTableEmpty';
|
|
10
|
+
export { DataTableLoading } from './DataTableLoading';
|
|
11
|
+
export { DataTableError } from './DataTableError';
|
|
12
|
+
export { DataTableRowActions } from './DataTableRowActions';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DataTable V2 Components Index
|
|
3
|
+
*/
|
|
4
|
+
export { DataTableHeader } from './DataTableHeader';
|
|
5
|
+
export { DataTableBody } from './DataTableBody';
|
|
6
|
+
export { DataTablePagination } from './DataTablePagination';
|
|
7
|
+
export { DataTableSearch } from './DataTableSearch';
|
|
8
|
+
export { DataTableToolbar } from './DataTableToolbar';
|
|
9
|
+
export { DataTableEmpty } from './DataTableEmpty';
|
|
10
|
+
export { DataTableLoading } from './DataTableLoading';
|
|
11
|
+
export { DataTableError } from './DataTableError';
|
|
12
|
+
export { DataTableRowActions } from './DataTableRowActions';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for useTableData hook
|
|
3
|
+
*/
|
|
4
|
+
export interface UseTableDataOptions<TData> {
|
|
5
|
+
/** API endpoint URL */
|
|
6
|
+
url: string;
|
|
7
|
+
/** Query key for TanStack Query cache management */
|
|
8
|
+
queryKey?: unknown[];
|
|
9
|
+
/** HTTP method */
|
|
10
|
+
method?: 'GET' | 'POST';
|
|
11
|
+
/** Request body for POST requests */
|
|
12
|
+
body?: Record<string, unknown>;
|
|
13
|
+
/** Query parameters */
|
|
14
|
+
params?: Record<string, string | number>;
|
|
15
|
+
/** Transform function for response data */
|
|
16
|
+
transform?: (data: unknown) => TData[];
|
|
17
|
+
/** Whether to enable the query */
|
|
18
|
+
enabled?: boolean;
|
|
19
|
+
/** Stale time in milliseconds (how long data is considered fresh) */
|
|
20
|
+
staleTime?: number;
|
|
21
|
+
/** Garbage collection time in milliseconds */
|
|
22
|
+
gcTime?: number;
|
|
23
|
+
/** Refetch interval in milliseconds (0 = disabled) */
|
|
24
|
+
refetchInterval?: number;
|
|
25
|
+
/** Refetch on window focus */
|
|
26
|
+
refetchOnWindowFocus?: boolean;
|
|
27
|
+
/** Initial data */
|
|
28
|
+
initialData?: TData[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Options for usePaginatedTableData hook
|
|
32
|
+
*/
|
|
33
|
+
export interface UsePaginatedTableDataOptions<TData> extends UseTableDataOptions<TData> {
|
|
34
|
+
/** Current page (0-indexed) */
|
|
35
|
+
page?: number;
|
|
36
|
+
/** Page size */
|
|
37
|
+
pageSize?: number;
|
|
38
|
+
/** Sort field */
|
|
39
|
+
sortField?: string;
|
|
40
|
+
/** Sort direction */
|
|
41
|
+
sortDirection?: 'asc' | 'desc';
|
|
42
|
+
/** Search/filter query */
|
|
43
|
+
search?: string;
|
|
44
|
+
/** Additional filters */
|
|
45
|
+
filters?: Record<string, string | number | boolean | null>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Result from useTableData hook
|
|
49
|
+
*/
|
|
50
|
+
export interface UseTableDataResult<TData> {
|
|
51
|
+
/** The fetched and transformed data */
|
|
52
|
+
data: TData[];
|
|
53
|
+
/** Whether the initial fetch is in progress */
|
|
54
|
+
isLoading: boolean;
|
|
55
|
+
/** Whether a background refetch is in progress */
|
|
56
|
+
isFetching: boolean;
|
|
57
|
+
/** Whether there was an error */
|
|
58
|
+
isError: boolean;
|
|
59
|
+
/** The error object if any */
|
|
60
|
+
error: Error | null;
|
|
61
|
+
/** Function to manually refetch data */
|
|
62
|
+
refetch: () => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Result from usePaginatedTableData hook
|
|
66
|
+
*/
|
|
67
|
+
export interface UsePaginatedTableDataResult<TData> extends UseTableDataResult<TData> {
|
|
68
|
+
/** Total number of items */
|
|
69
|
+
totalItems: number;
|
|
70
|
+
/** Total number of pages */
|
|
71
|
+
totalPages: number;
|
|
72
|
+
/** Current page (0-indexed) */
|
|
73
|
+
currentPage: number;
|
|
74
|
+
/** Whether there's a next page */
|
|
75
|
+
hasNextPage: boolean;
|
|
76
|
+
/** Whether there's a previous page */
|
|
77
|
+
hasPreviousPage: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Hook for fetching table data using TanStack Query
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* const { data, isLoading, error, refetch } = useTableData<User>({
|
|
85
|
+
* url: '/api/users',
|
|
86
|
+
* queryKey: ['users'],
|
|
87
|
+
* params: { status: 'active' },
|
|
88
|
+
* transform: (raw) => raw.users,
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function useTableData<TData = unknown>(options: UseTableDataOptions<TData>): UseTableDataResult<TData>;
|
|
93
|
+
/**
|
|
94
|
+
* Hook for fetching paginated table data using TanStack Query
|
|
95
|
+
* Designed for Spring Boot style paginated responses
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```tsx
|
|
99
|
+
* const {
|
|
100
|
+
* data,
|
|
101
|
+
* isLoading,
|
|
102
|
+
* totalItems,
|
|
103
|
+
* totalPages,
|
|
104
|
+
* hasNextPage,
|
|
105
|
+
* } = usePaginatedTableData<User>({
|
|
106
|
+
* url: '/api/users',
|
|
107
|
+
* queryKey: ['users'],
|
|
108
|
+
* page: 0,
|
|
109
|
+
* pageSize: 20,
|
|
110
|
+
* sortField: 'createdAt',
|
|
111
|
+
* sortDirection: 'desc',
|
|
112
|
+
* search: searchQuery,
|
|
113
|
+
* filters: { status: 'active' },
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function usePaginatedTableData<TData = unknown>(options: UsePaginatedTableDataOptions<TData>): UsePaginatedTableDataResult<TData>;
|
|
118
|
+
export default useTableData;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview useTableData hook - Data fetching for DataTable V2
|
|
3
|
+
* Uses TanStack Query for declarative, cache-aware data fetching with pagination support
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback, useMemo } from 'react';
|
|
6
|
+
import { useApiQuery, usePaginatedApiQuery } from '../../api/tanstackQuery';
|
|
7
|
+
/**
|
|
8
|
+
* Hook for fetching table data using TanStack Query
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { data, isLoading, error, refetch } = useTableData<User>({
|
|
13
|
+
* url: '/api/users',
|
|
14
|
+
* queryKey: ['users'],
|
|
15
|
+
* params: { status: 'active' },
|
|
16
|
+
* transform: (raw) => raw.users,
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function useTableData(options) {
|
|
21
|
+
const { url, queryKey: customQueryKey, method = 'GET', body, params, transform, enabled = true, staleTime, gcTime, refetchInterval, refetchOnWindowFocus, initialData = [], } = options;
|
|
22
|
+
// Build query string
|
|
23
|
+
const queryString = useMemo(() => {
|
|
24
|
+
if (!params || Object.keys(params).length === 0)
|
|
25
|
+
return '';
|
|
26
|
+
const searchParams = new URLSearchParams();
|
|
27
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
28
|
+
if (value !== undefined && value !== null) {
|
|
29
|
+
searchParams.append(key, String(value));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return '?' + searchParams.toString();
|
|
33
|
+
}, [params]);
|
|
34
|
+
const fullUrl = `${url}${queryString}`;
|
|
35
|
+
// Build stable query key
|
|
36
|
+
const queryKey = useMemo(() => customQueryKey || ['table', url, method, params, body].filter(Boolean), [customQueryKey, url, method, params, body]);
|
|
37
|
+
// Use TanStack Query for data fetching
|
|
38
|
+
const { data: rawData, isLoading, isFetching, isError, error, refetch: queryRefetch, } = useApiQuery({
|
|
39
|
+
queryKey,
|
|
40
|
+
endpoint: fullUrl,
|
|
41
|
+
requestConfig: {
|
|
42
|
+
method,
|
|
43
|
+
...(method === 'POST' && body ? { body: JSON.stringify(body) } : {}),
|
|
44
|
+
},
|
|
45
|
+
enabled: enabled && !!url,
|
|
46
|
+
staleTime,
|
|
47
|
+
gcTime,
|
|
48
|
+
refetchInterval: refetchInterval || false,
|
|
49
|
+
refetchOnWindowFocus: refetchOnWindowFocus ?? false,
|
|
50
|
+
});
|
|
51
|
+
// Transform data
|
|
52
|
+
const transformedData = useMemo(() => {
|
|
53
|
+
if (!rawData)
|
|
54
|
+
return initialData;
|
|
55
|
+
if (transform) {
|
|
56
|
+
try {
|
|
57
|
+
return transform(rawData);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.error('Error transforming table data:', e);
|
|
61
|
+
return initialData;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Default: assume data is array or has data/content property
|
|
65
|
+
if (Array.isArray(rawData))
|
|
66
|
+
return rawData;
|
|
67
|
+
if (rawData && typeof rawData === 'object') {
|
|
68
|
+
if ('content' in rawData) {
|
|
69
|
+
return rawData.content;
|
|
70
|
+
}
|
|
71
|
+
if ('data' in rawData) {
|
|
72
|
+
return rawData.data;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return initialData;
|
|
76
|
+
}, [rawData, transform, initialData]);
|
|
77
|
+
const refetch = useCallback(() => {
|
|
78
|
+
queryRefetch();
|
|
79
|
+
}, [queryRefetch]);
|
|
80
|
+
return {
|
|
81
|
+
data: transformedData,
|
|
82
|
+
isLoading,
|
|
83
|
+
isFetching,
|
|
84
|
+
isError,
|
|
85
|
+
error: error,
|
|
86
|
+
refetch,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Hook for fetching paginated table data using TanStack Query
|
|
91
|
+
* Designed for Spring Boot style paginated responses
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* const {
|
|
96
|
+
* data,
|
|
97
|
+
* isLoading,
|
|
98
|
+
* totalItems,
|
|
99
|
+
* totalPages,
|
|
100
|
+
* hasNextPage,
|
|
101
|
+
* } = usePaginatedTableData<User>({
|
|
102
|
+
* url: '/api/users',
|
|
103
|
+
* queryKey: ['users'],
|
|
104
|
+
* page: 0,
|
|
105
|
+
* pageSize: 20,
|
|
106
|
+
* sortField: 'createdAt',
|
|
107
|
+
* sortDirection: 'desc',
|
|
108
|
+
* search: searchQuery,
|
|
109
|
+
* filters: { status: 'active' },
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function usePaginatedTableData(options) {
|
|
114
|
+
const { url, queryKey: customQueryKey, method = 'GET', body, params: additionalParams, transform, enabled = true, staleTime, gcTime, refetchInterval, refetchOnWindowFocus, initialData = [], page = 0, pageSize = 20, sortField, sortDirection, search, filters, } = options;
|
|
115
|
+
// Build params with pagination
|
|
116
|
+
const params = useMemo(() => {
|
|
117
|
+
const p = {
|
|
118
|
+
page,
|
|
119
|
+
size: pageSize,
|
|
120
|
+
...additionalParams,
|
|
121
|
+
};
|
|
122
|
+
if (sortField) {
|
|
123
|
+
p.sort = sortDirection ? `${sortField},${sortDirection}` : sortField;
|
|
124
|
+
}
|
|
125
|
+
if (search) {
|
|
126
|
+
p.search = search;
|
|
127
|
+
}
|
|
128
|
+
if (filters) {
|
|
129
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
130
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
131
|
+
p[key] = String(value);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return p;
|
|
136
|
+
}, [page, pageSize, sortField, sortDirection, search, filters, additionalParams]);
|
|
137
|
+
// Build query string
|
|
138
|
+
const queryString = useMemo(() => {
|
|
139
|
+
const searchParams = new URLSearchParams();
|
|
140
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
141
|
+
searchParams.append(key, String(value));
|
|
142
|
+
});
|
|
143
|
+
return '?' + searchParams.toString();
|
|
144
|
+
}, [params]);
|
|
145
|
+
const fullUrl = `${url}${queryString}`;
|
|
146
|
+
// Build stable query key
|
|
147
|
+
const queryKey = useMemo(() => customQueryKey || ['table-paginated', url, params, body].filter(Boolean), [customQueryKey, url, params, body]);
|
|
148
|
+
// Use paginated API query
|
|
149
|
+
const { data: rawData, isLoading, isFetching, isError, error, refetch: queryRefetch, } = usePaginatedApiQuery({
|
|
150
|
+
queryKey,
|
|
151
|
+
endpoint: fullUrl,
|
|
152
|
+
requestConfig: {
|
|
153
|
+
method,
|
|
154
|
+
...(method === 'POST' && body ? { body: JSON.stringify(body) } : {}),
|
|
155
|
+
},
|
|
156
|
+
enabled: enabled && !!url,
|
|
157
|
+
staleTime,
|
|
158
|
+
gcTime,
|
|
159
|
+
refetchInterval: refetchInterval || false,
|
|
160
|
+
refetchOnWindowFocus: refetchOnWindowFocus ?? false,
|
|
161
|
+
});
|
|
162
|
+
// Extract data and pagination info
|
|
163
|
+
const { data, totalItems, totalPages, hasNextPage, hasPreviousPage } = useMemo(() => {
|
|
164
|
+
if (!rawData) {
|
|
165
|
+
return {
|
|
166
|
+
data: initialData,
|
|
167
|
+
totalItems: 0,
|
|
168
|
+
totalPages: 0,
|
|
169
|
+
hasNextPage: false,
|
|
170
|
+
hasPreviousPage: false,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// Handle Spring Boot response
|
|
174
|
+
const springResponse = rawData;
|
|
175
|
+
let content = springResponse.content || [];
|
|
176
|
+
if (transform) {
|
|
177
|
+
try {
|
|
178
|
+
content = transform(content);
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
console.error('Error transforming table data:', e);
|
|
182
|
+
content = initialData;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
data: content,
|
|
187
|
+
totalItems: springResponse.totalElements || 0,
|
|
188
|
+
totalPages: springResponse.totalPages || 0,
|
|
189
|
+
hasNextPage: !springResponse.last,
|
|
190
|
+
hasPreviousPage: !springResponse.first,
|
|
191
|
+
};
|
|
192
|
+
}, [rawData, transform, initialData]);
|
|
193
|
+
const refetch = useCallback(() => {
|
|
194
|
+
queryRefetch();
|
|
195
|
+
}, [queryRefetch]);
|
|
196
|
+
return {
|
|
197
|
+
data,
|
|
198
|
+
isLoading,
|
|
199
|
+
isFetching,
|
|
200
|
+
isError,
|
|
201
|
+
error: error,
|
|
202
|
+
refetch,
|
|
203
|
+
totalItems,
|
|
204
|
+
totalPages,
|
|
205
|
+
currentPage: page,
|
|
206
|
+
hasNextPage,
|
|
207
|
+
hasPreviousPage,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export default useTableData;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DataTable V2 - Main exports
|
|
3
|
+
* TanStack Table-based data table with headless architecture
|
|
4
|
+
*/
|
|
5
|
+
export { DataTable } from './DataTable';
|
|
6
|
+
export type { DataTableProps, DataTableColumn, DataTableClassNames, RowSelectionOptions, PaginationOptions, SortingOptions, FilteringOptions, ColumnVisibilityOptions, ExportOptions, RowAction, BulkAction, DataTableServerResponse, DataTableServerRequest, } from './types';
|
|
7
|
+
export { DataTableHeader, DataTableBody, DataTablePagination, DataTableSearch, DataTableToolbar, DataTableEmpty, DataTableLoading, DataTableError, DataTableRowActions, } from './components';
|
|
8
|
+
export { useTableData, usePaginatedTableData, type UseTableDataOptions, type UsePaginatedTableDataOptions, type UseTableDataResult, type UsePaginatedTableDataResult, } from './hooks';
|
|
9
|
+
export { exportToCSV, exportToJSON, downloadFile, createExportHandler } from './utils';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DataTable V2 - Main exports
|
|
3
|
+
* TanStack Table-based data table with headless architecture
|
|
4
|
+
*/
|
|
5
|
+
// Main component
|
|
6
|
+
export { DataTable } from './DataTable';
|
|
7
|
+
// Sub-components (for customization)
|
|
8
|
+
export { DataTableHeader, DataTableBody, DataTablePagination, DataTableSearch, DataTableToolbar, DataTableEmpty, DataTableLoading, DataTableError, DataTableRowActions, } from './components';
|
|
9
|
+
// Hooks (TanStack Query based)
|
|
10
|
+
export { useTableData, usePaginatedTableData, } from './hooks';
|
|
11
|
+
// Utilities
|
|
12
|
+
export { exportToCSV, exportToJSON, downloadFile, createExportHandler } from './utils';
|