@cryptlex/web-components 5.2.0 → 5.3.0
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/dist/components/data-table/data-table-filter.d.ts +27 -0
- package/dist/components/data-table/data-table-filter.js +112 -0
- package/dist/components/data-table/data-table.d.ts +73 -0
- package/dist/components/data-table/data-table.js +265 -0
- package/dist/components/data-table/table-commons.d.ts +56 -0
- package/dist/components/data-table/table-commons.js +137 -0
- package/dist/components/inputs/checkbox.d.ts +8 -0
- package/dist/components/inputs/checkbox.js +25 -0
- package/dist/components/inputs/date-picker.d.ts +11 -0
- package/dist/components/inputs/date-picker.js +22 -0
- package/dist/components/inputs/datefield.d.ts +14 -0
- package/dist/components/inputs/datefield.js +25 -0
- package/dist/components/inputs/field.d.ts +21 -0
- package/dist/components/inputs/field.js +48 -0
- package/dist/components/inputs/id-search.d.ts +20 -0
- package/dist/components/inputs/id-search.js +40 -0
- package/dist/components/inputs/input-otp.d.ts +8 -0
- package/dist/components/inputs/input-otp.js +19 -0
- package/dist/components/inputs/multi-select.d.ts +17 -0
- package/dist/components/inputs/multi-select.js +18 -0
- package/dist/components/inputs/numberfield.d.ts +7 -0
- package/dist/components/inputs/numberfield.js +25 -0
- package/dist/components/inputs/searchfield.d.ts +5 -0
- package/dist/components/inputs/searchfield.js +24 -0
- package/dist/components/inputs/select-options.d.ts +8 -0
- package/dist/components/inputs/select-options.js +286 -0
- package/dist/components/inputs/select.d.ts +17 -0
- package/dist/components/inputs/select.js +34 -0
- package/dist/components/inputs/textfield.d.ts +7 -0
- package/dist/components/inputs/textfield.js +28 -0
- package/dist/components/key-value-card/key-value-card.d.ts +17 -0
- package/dist/components/key-value-card/key-value-card.js +40 -0
- package/dist/components/ui/alert.d.ts +8 -0
- package/dist/components/ui/alert.js +18 -0
- package/dist/components/ui/avatar.d.ts +8 -0
- package/dist/components/ui/avatar.js +5 -0
- package/dist/components/ui/badge.d.ts +2 -0
- package/dist/components/ui/badge.js +5 -0
- package/dist/components/ui/breadcrumbs.d.ts +10 -0
- package/dist/components/ui/breadcrumbs.js +28 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/button.js +34 -0
- package/dist/components/ui/calendar.d.ts +17 -0
- package/dist/components/ui/calendar.js +63 -0
- package/dist/components/ui/card.d.ts +7 -0
- package/dist/components/ui/card.js +20 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/dialog.js +42 -0
- package/dist/components/ui/disclosure.d.ts +19 -0
- package/dist/components/ui/disclosure.js +20 -0
- package/dist/components/ui/list-box.d.ts +5 -0
- package/dist/components/ui/list-box.js +24 -0
- package/dist/components/ui/loader.d.ts +5 -0
- package/dist/components/ui/loader.js +6 -0
- package/dist/components/ui/menu.d.ts +25 -0
- package/dist/components/ui/menu.js +38 -0
- package/dist/components/ui/popover.d.ts +4 -0
- package/dist/components/ui/popover.js +14 -0
- package/dist/components/ui/sidebar.d.ts +53 -0
- package/dist/components/ui/sidebar.js +177 -0
- package/dist/components/ui/skeleton.d.ts +1 -0
- package/dist/components/ui/skeleton.js +5 -0
- package/dist/components/ui/sonner.d.ts +4 -0
- package/dist/components/ui/sonner.js +14 -0
- package/dist/components/ui/table.d.ts +9 -0
- package/dist/components/ui/table.js +26 -0
- package/dist/components/ui/tabs.d.ts +5 -0
- package/dist/components/ui/tabs.js +25 -0
- package/dist/components/ui/timeline.d.ts +15 -0
- package/dist/components/ui/timeline.js +31 -0
- package/dist/components/ui/tooltip.d.ts +4 -0
- package/dist/components/ui/tooltip.js +12 -0
- package/dist/utils/form-context.d.ts +4 -0
- package/{lib/utils/form-context.tsx → dist/utils/form-context.js} +1 -3
- package/dist/utils/form-hook.d.ts +25 -0
- package/{lib/utils/form-hook.tsx → dist/utils/form-hook.js} +15 -18
- package/dist/utils/primitives.d.ts +30 -0
- package/{lib/utils/primitives.ts → dist/utils/primitives.js} +8 -37
- package/dist/utils/resource-names.d.ts +23 -0
- package/{lib/utils/resource-names.tsx → dist/utils/resource-names.js} +42 -75
- package/dist/utils/use-mobile.d.ts +1 -0
- package/dist/utils/use-mobile.js +15 -0
- package/package.json +10 -5
- package/lib/components/data-table/data-table-filter.tsx +0 -220
- package/lib/components/data-table/data-table.tsx +0 -593
- package/lib/components/data-table/table-commons.tsx +0 -233
- package/lib/components/inputs/checkbox.tsx +0 -72
- package/lib/components/inputs/date-picker.tsx +0 -130
- package/lib/components/inputs/datefield.tsx +0 -109
- package/lib/components/inputs/field.tsx +0 -106
- package/lib/components/inputs/id-search.tsx +0 -83
- package/lib/components/inputs/input-otp.tsx +0 -63
- package/lib/components/inputs/multi-select.tsx +0 -62
- package/lib/components/inputs/numberfield.tsx +0 -110
- package/lib/components/inputs/searchfield.tsx +0 -87
- package/lib/components/inputs/select-options.tsx +0 -303
- package/lib/components/inputs/select.tsx +0 -140
- package/lib/components/inputs/textfield.tsx +0 -96
- package/lib/components/key-value-card/key-value-card.tsx +0 -115
- package/lib/components/ui/alert.tsx +0 -32
- package/lib/components/ui/avatar.tsx +0 -22
- package/lib/components/ui/badge.tsx +0 -19
- package/lib/components/ui/breadcrumbs.tsx +0 -104
- package/lib/components/ui/button.tsx +0 -66
- package/lib/components/ui/calendar.tsx +0 -220
- package/lib/components/ui/card.tsx +0 -58
- package/lib/components/ui/dialog.tsx +0 -172
- package/lib/components/ui/disclosure.tsx +0 -113
- package/lib/components/ui/list-box.tsx +0 -86
- package/lib/components/ui/loader.tsx +0 -10
- package/lib/components/ui/menu.tsx +0 -168
- package/lib/components/ui/popover.tsx +0 -37
- package/lib/components/ui/sidebar.tsx +0 -552
- package/lib/components/ui/skeleton.tsx +0 -7
- package/lib/components/ui/sonner.tsx +0 -26
- package/lib/components/ui/table.tsx +0 -79
- package/lib/components/ui/tabs.tsx +0 -82
- package/lib/components/ui/timeline.tsx +0 -52
- package/lib/components/ui/tooltip.tsx +0 -30
- package/lib/tokens.scss +0 -89
- package/lib/utils/use-mobile.tsx +0 -21
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type OperationKeys } from "lib/components/data-table/data-table";
|
|
2
|
+
import { type ApiFilters } from "lib/components/data-table/table-commons";
|
|
3
|
+
import type { MultiSelectProps } from "lib/components/inputs/multi-select";
|
|
4
|
+
export declare const FILTER_COMPARISON_OPERATORS: readonly ["eq", "ne", "cn", "nc", "sw", "ew", "in", "nin", "gt", "gte", "lt", "lte"];
|
|
5
|
+
export type FilterComparisonOperator = (typeof FILTER_COMPARISON_OPERATORS)[number];
|
|
6
|
+
export declare const COMPARISON_OPERATOR_LABELS: Record<FilterComparisonOperator, string>;
|
|
7
|
+
export type FilterConfig = {
|
|
8
|
+
type: 'enum';
|
|
9
|
+
options: MultiSelectProps['items'];
|
|
10
|
+
} | {
|
|
11
|
+
type: 'id-search';
|
|
12
|
+
search: () => Promise<{
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
}[]>;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'id';
|
|
18
|
+
} | {
|
|
19
|
+
type: 'bool';
|
|
20
|
+
} | {
|
|
21
|
+
type: 'string';
|
|
22
|
+
} | {
|
|
23
|
+
type: 'date';
|
|
24
|
+
};
|
|
25
|
+
export type FilterType = FilterConfig['type'];
|
|
26
|
+
export type FiltersConfig<TOperation extends OperationKeys> = Required<Record<keyof ApiFilters<TOperation>, FilterConfig>>;
|
|
27
|
+
export declare function DataTableFilter({ className, ...props }: React.ComponentProps<'section'>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useDataTable } from "lib/components/data-table/data-table";
|
|
3
|
+
import { TfDatePicker } from "lib/components/inputs/date-picker";
|
|
4
|
+
import { TfIdSearchInput } from "lib/components/inputs/id-search";
|
|
5
|
+
import { Button } from "lib/components/ui/button";
|
|
6
|
+
import { EasyMenu, MenuItem } from "lib/components/ui/menu";
|
|
7
|
+
import { Popover, PopoverTrigger } from "lib/components/ui/popover";
|
|
8
|
+
import { useAppForm } from "lib/utils/form-hook";
|
|
9
|
+
import { cn } from "lib/utils/primitives";
|
|
10
|
+
import { useResourceFormatter } from "lib/utils/resource-names";
|
|
11
|
+
import { Filter, Trash } from "lucide-react";
|
|
12
|
+
import { useRef } from "react";
|
|
13
|
+
export const FILTER_COMPARISON_OPERATORS = [
|
|
14
|
+
'eq',
|
|
15
|
+
'ne',
|
|
16
|
+
'cn',
|
|
17
|
+
'nc',
|
|
18
|
+
'sw',
|
|
19
|
+
'ew',
|
|
20
|
+
'in',
|
|
21
|
+
'nin',
|
|
22
|
+
'gt',
|
|
23
|
+
'gte',
|
|
24
|
+
'lt',
|
|
25
|
+
'lte',
|
|
26
|
+
];
|
|
27
|
+
export const COMPARISON_OPERATOR_LABELS = {
|
|
28
|
+
eq: 'equal to',
|
|
29
|
+
ne: 'not equal to',
|
|
30
|
+
cn: 'contains',
|
|
31
|
+
nc: 'does not contain',
|
|
32
|
+
sw: 'starts with',
|
|
33
|
+
ew: 'ends with',
|
|
34
|
+
in: 'includes',
|
|
35
|
+
nin: 'does not include',
|
|
36
|
+
gt: 'greater than',
|
|
37
|
+
gte: 'greater than or equal to',
|
|
38
|
+
lt: 'less than',
|
|
39
|
+
lte: 'less than or equal to',
|
|
40
|
+
};
|
|
41
|
+
const FilterOperations = {
|
|
42
|
+
'bool': ['eq'],
|
|
43
|
+
'enum': ['in', 'nin'],
|
|
44
|
+
'id': ['in', 'nin'],
|
|
45
|
+
'id-search': ['in', 'nin'],
|
|
46
|
+
'string': ['eq', 'ne', 'cn', 'nc', 'sw', 'ew', 'in', 'nin'],
|
|
47
|
+
'date': ['lt', 'gt'],
|
|
48
|
+
};
|
|
49
|
+
export function DataTableFilter({ className, ...props }) {
|
|
50
|
+
const { filters, query, setFilters, filterConfig } = useDataTable();
|
|
51
|
+
// TODO focus
|
|
52
|
+
// const localFilterRefs = useRef([]);
|
|
53
|
+
const addFilterButtonRef = useRef(null);
|
|
54
|
+
const resourceFormatter = useResourceFormatter();
|
|
55
|
+
const DEFAULT_VALUES = {
|
|
56
|
+
// TODO, initialize with filters from tableState
|
|
57
|
+
filters: []
|
|
58
|
+
};
|
|
59
|
+
const form = useAppForm({
|
|
60
|
+
defaultValues: DEFAULT_VALUES,
|
|
61
|
+
onSubmit: ({ value }) => {
|
|
62
|
+
// TODO, instead of generic transformation, use type based tranform()
|
|
63
|
+
setFilters(value.filters.map(lf => { return { [lf.property]: { [lf.operator]: [lf.value] } }; }));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// More reason to hate TS https://github.com/Microsoft/TypeScript/issues/12870
|
|
67
|
+
const filterKeys = Object.keys(filterConfig).sort();
|
|
68
|
+
return (_jsx("section", { ...props, className: cn("flex gap-icon items-center", className), children: _jsxs(PopoverTrigger, { onOpenChange: (o) => {
|
|
69
|
+
// Set filters when the popover closes
|
|
70
|
+
if (!o) {
|
|
71
|
+
form.handleSubmit();
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
addFilterButtonRef.current?.focus();
|
|
75
|
+
}
|
|
76
|
+
}, children: [_jsx(Button, { active: filters.length > 0, isDisabled: query.isPending || filterKeys.length === 0, type: "button", size: 'icon', variant: 'neutral', children: _jsx(Filter, {}) }), _jsx(Popover, { className: "w-full p-icon", children: _jsx("form", { onSubmit: (e) => { e.preventDefault(); }, className: "flex flex-col gap-icon max-h-table overflow-auto ", children: _jsx(form.Field, { mode: "array", name: "filters", children: (field) => {
|
|
77
|
+
return _jsxs(_Fragment, { children: [field.state.value.map((lf, i) => {
|
|
78
|
+
return (_jsxs("div", { className: "flex gap-icon items-center justify-normal", children: [_jsx("span", { className: "text-sm", children: resourceFormatter(lf.property) }), _jsx(form.AppField, { name: `filters[${i}].operator`, children: (sf) => (_jsx(sf.TfSingleSelect, { items: FilterOperations[lf.type].map(op => ({ id: op, label: _jsx(_Fragment, { children: COMPARISON_OPERATOR_LABELS[op] }) })) })) }, lf.id), lf.type === 'bool' && _jsx(form.AppField, { name: `filters[${i}].value`, children: (sf) => (_jsx(sf.TfCheckbox, {})) }), lf.type === 'date' && _jsx(form.AppField, { name: `filters[${i}].value`, children: (_) => (_jsx(TfDatePicker, {})) }), lf.type === 'id' && _jsx(form.AppField, { name: `filters[${i}].value`, children: (sf) => (_jsx(sf.TfTextField, {})) }), lf.type === 'string' && _jsx(form.AppField, { name: `filters[${i}].value`, children: (sf) => (_jsx(sf.TfTextField, {})) }), lf.type === 'enum' && _jsx(form.AppField, { name: `filters[${i}].value`, children: (sf) => (_jsx(sf.TfMultiSelect, { items: filterConfig[lf.property].options })) }), lf.type === 'id-search' && _jsx(form.AppField, { name: `filters[${i}].value`, children: (_) => (_jsx(TfIdSearchInput, { multiple: true, accessor: "id", searchFn: filterConfig[lf.property].search })) }), _jsx(Button, { type: "button", variant: "destructive", size: "icon", onPress: () => field.removeValue(i), children: _jsx(Trash, {}) })] }, lf.id));
|
|
79
|
+
}), _jsxs("div", { className: "flex w-full items-center justify-end gap-2 not-first:mt-icon", children: [_jsx(EasyMenu, { label: "Add Filter", children: filterKeys.map(k => _jsx(MenuItem, { onAction: () => {
|
|
80
|
+
//@ts-ignore
|
|
81
|
+
const type = filterConfig[k].type;
|
|
82
|
+
const defaultValue = (t) => {
|
|
83
|
+
// TODO, ensure exhaustive???
|
|
84
|
+
switch (t) {
|
|
85
|
+
case 'bool':
|
|
86
|
+
return true;
|
|
87
|
+
case 'string':
|
|
88
|
+
return '';
|
|
89
|
+
case 'date':
|
|
90
|
+
return new Date().toISOString();
|
|
91
|
+
case 'id':
|
|
92
|
+
return '';
|
|
93
|
+
case 'id-search':
|
|
94
|
+
return [];
|
|
95
|
+
case 'enum':
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const newFilter = {
|
|
100
|
+
id: `${k}-${Date.now()}`,
|
|
101
|
+
operator: FilterOperations[type][0],
|
|
102
|
+
//@ts-ignore
|
|
103
|
+
value: defaultValue(type),
|
|
104
|
+
type: type,
|
|
105
|
+
property: k,
|
|
106
|
+
};
|
|
107
|
+
field.pushValue(newFilter);
|
|
108
|
+
// TODO Focus to ref
|
|
109
|
+
}, className: "dropdown-item", children: resourceFormatter(k) }, k)) }), field.state.value.length > 0 ? (_jsx(Button, { type: "button", variant: "neutral", onPress: () => { field.setValue([]); }, children: "Reset filters" })) : null] })] });
|
|
110
|
+
} }) }) })] }) }));
|
|
111
|
+
}
|
|
112
|
+
;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { components, operations } from "@cryptlex/web-api-types";
|
|
2
|
+
import { type ColumnDef, type TableState, type VisibilityState } from "@tanstack/react-table";
|
|
3
|
+
import React from "react";
|
|
4
|
+
/** Reserved name for actions column */
|
|
5
|
+
export declare const ACTIONS_COLUMN_ID = "tableActions";
|
|
6
|
+
export type Schemas = ApiSchema<keyof components['schemas']>;
|
|
7
|
+
export type OperationKeys = keyof operations;
|
|
8
|
+
type DataTableFactory<TData extends Schemas> = {
|
|
9
|
+
fetchFn: TableFetchFn<TData, OperationKeys>;
|
|
10
|
+
columns: ColumnDef<TData, any>[];
|
|
11
|
+
allowSelection?: boolean;
|
|
12
|
+
columnsToHideByDefault?: VisibilityState;
|
|
13
|
+
filterConfig: FiltersConfig<OperationKeys>;
|
|
14
|
+
};
|
|
15
|
+
type DataTableState = Pick<TableState, 'sorting' | 'rowSelection' | 'pagination'> & {};
|
|
16
|
+
/**
|
|
17
|
+
* Hook for handling all data-table state. Used in DataTableContext
|
|
18
|
+
*/
|
|
19
|
+
export declare function useDataTableState<TData extends Schemas>({ columns, fetchFn, columnsToHideByDefault, allowSelection, filterConfig }: DataTableFactory<TData>): {
|
|
20
|
+
tableState: Pick<TableState, "sorting" | "rowSelection" | "pagination">;
|
|
21
|
+
updateTableState: (updates: Partial<DataTableState>) => void;
|
|
22
|
+
query: import("@tanstack/react-query").UseQueryResult<{
|
|
23
|
+
total: number;
|
|
24
|
+
data: TData[] | undefined;
|
|
25
|
+
}, Error>;
|
|
26
|
+
setSearchQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
27
|
+
searchQuery: string;
|
|
28
|
+
tanTable: import("@tanstack/react-table").Table<any>;
|
|
29
|
+
mergedFilters: ApiFilters<keyof operations>;
|
|
30
|
+
filters: ApiFilters<keyof operations>[];
|
|
31
|
+
setFilters: React.Dispatch<React.SetStateAction<ApiFilters<keyof operations>[]>>;
|
|
32
|
+
filterConfig: Required<Record<never, import("lib/components/data-table/data-table-filter").FilterConfig>>;
|
|
33
|
+
};
|
|
34
|
+
export declare const DataTableContext: React.Context<{
|
|
35
|
+
tableState: Pick<TableState, "sorting" | "rowSelection" | "pagination">;
|
|
36
|
+
updateTableState: (updates: Partial<DataTableState>) => void;
|
|
37
|
+
query: import("@tanstack/react-query").UseQueryResult<{
|
|
38
|
+
total: number;
|
|
39
|
+
data: Schemas[] | undefined;
|
|
40
|
+
}, Error>;
|
|
41
|
+
setSearchQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
42
|
+
searchQuery: string;
|
|
43
|
+
tanTable: import("@tanstack/react-table").Table<any>;
|
|
44
|
+
mergedFilters: ApiFilters<keyof operations>;
|
|
45
|
+
filters: ApiFilters<keyof operations>[];
|
|
46
|
+
setFilters: React.Dispatch<React.SetStateAction<ApiFilters<keyof operations>[]>>;
|
|
47
|
+
filterConfig: Required<Record<never, import("lib/components/data-table/data-table-filter").FilterConfig>>;
|
|
48
|
+
} | null>;
|
|
49
|
+
export declare const useDataTable: () => {
|
|
50
|
+
tableState: Pick<TableState, "sorting" | "rowSelection" | "pagination">;
|
|
51
|
+
updateTableState: (updates: Partial<DataTableState>) => void;
|
|
52
|
+
query: import("@tanstack/react-query").UseQueryResult<{
|
|
53
|
+
total: number;
|
|
54
|
+
data: Schemas[] | undefined;
|
|
55
|
+
}, Error>;
|
|
56
|
+
setSearchQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
57
|
+
searchQuery: string;
|
|
58
|
+
tanTable: import("@tanstack/react-table").Table<any>;
|
|
59
|
+
mergedFilters: ApiFilters<keyof operations>;
|
|
60
|
+
filters: ApiFilters<keyof operations>[];
|
|
61
|
+
setFilters: React.Dispatch<React.SetStateAction<ApiFilters<keyof operations>[]>>;
|
|
62
|
+
filterConfig: Required<Record<never, import("lib/components/data-table/data-table-filter").FilterConfig>>;
|
|
63
|
+
};
|
|
64
|
+
export declare function DataTableProvider({ children, ...props }: {
|
|
65
|
+
children: React.ReactNode;
|
|
66
|
+
} & ReturnType<typeof useDataTableState>): import("react/jsx-runtime").JSX.Element;
|
|
67
|
+
export type DataTableProps = React.ComponentProps<'section'> & {
|
|
68
|
+
tableActions: TableActions;
|
|
69
|
+
};
|
|
70
|
+
export declare function DataTable({ tableActions, className, ...props }: DataTableProps): import("react/jsx-runtime").JSX.Element;
|
|
71
|
+
import { type FiltersConfig } from "lib/components/data-table/data-table-filter";
|
|
72
|
+
import { type ApiFilters, type ApiSchema, type TableActions, type TableFetchFn } from "lib/components/data-table/table-commons";
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
|
5
|
+
import { ArrowDownNarrowWide, ArrowDownWideNarrow, ArrowUpDown, Columns3, GripVertical, Info } from "lucide-react";
|
|
6
|
+
import { createContext, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
7
|
+
/** Reserved name for actions column */
|
|
8
|
+
export const ACTIONS_COLUMN_ID = "tableActions";
|
|
9
|
+
/**
|
|
10
|
+
* Hook for handling all data-table state. Used in DataTableContext
|
|
11
|
+
*/
|
|
12
|
+
export function useDataTableState({ columns, fetchFn, columnsToHideByDefault = {}, allowSelection = false, filterConfig }) {
|
|
13
|
+
const id = useId();
|
|
14
|
+
// TODO: Would it be better for this state to be more granular?
|
|
15
|
+
const [tableState, _setTableState] = useState({
|
|
16
|
+
/** TODO Reflect in URL */
|
|
17
|
+
pagination: { pageIndex: 0, pageSize: 20 }, // Pagination state
|
|
18
|
+
sorting: [], // Sorting state
|
|
19
|
+
/** Ephemeral */
|
|
20
|
+
rowSelection: {}, // Row selection state
|
|
21
|
+
});
|
|
22
|
+
/** TODO Reflect in URL */
|
|
23
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
24
|
+
/** TODO Store on browser as preference */
|
|
25
|
+
const [columnOrder, setColumnOrder] = useState([]);
|
|
26
|
+
// TODO Store on browser
|
|
27
|
+
const [columnVisibility, setColumnVisibility] = useState({
|
|
28
|
+
id: false,
|
|
29
|
+
updatedAt: false,
|
|
30
|
+
...columnsToHideByDefault,
|
|
31
|
+
});
|
|
32
|
+
const [filters, setFilters] = useState([]);
|
|
33
|
+
const mergedFilters = useMemo(() => {
|
|
34
|
+
return filters.reduce((acc, current) => {
|
|
35
|
+
return merge(acc, current);
|
|
36
|
+
}, {});
|
|
37
|
+
}, [filters]);
|
|
38
|
+
// Update table state with new values
|
|
39
|
+
const updateTableState = (updates) => {
|
|
40
|
+
_setTableState((prev) => ({ ...prev, ...updates }));
|
|
41
|
+
};
|
|
42
|
+
const { sorting, rowSelection, pagination, } = tableState;
|
|
43
|
+
const query = useQuery({
|
|
44
|
+
queryKey: [id, pagination, sorting, searchQuery],
|
|
45
|
+
queryFn: () => fetchFn(pagination, sorting, searchQuery, mergedFilters),
|
|
46
|
+
placeholderData: keepPreviousData, // Keep previous data while loading new data
|
|
47
|
+
retry: 0,
|
|
48
|
+
refetchOnWindowFocus: false,
|
|
49
|
+
});
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
// TODO, store in localStorage
|
|
52
|
+
}, [columnVisibility]);
|
|
53
|
+
const columnHelper = useMemo(() => createColumnHelper(), []);
|
|
54
|
+
const metadataColumns = useMemo(() => {
|
|
55
|
+
const data = query.data?.data;
|
|
56
|
+
if (!data?.length)
|
|
57
|
+
return [];
|
|
58
|
+
const rowHasMetadata = (row) => row != null &&
|
|
59
|
+
typeof row === 'object' &&
|
|
60
|
+
'metadata' in row &&
|
|
61
|
+
Array.isArray((row).metadata);
|
|
62
|
+
const rowsWithMeta = data.filter(rowHasMetadata);
|
|
63
|
+
if (rowsWithMeta.length === 0)
|
|
64
|
+
return [];
|
|
65
|
+
const keys = Array.from(new Set(rowsWithMeta.flatMap(r => r.metadata?.map(m => m.key) ?? [])));
|
|
66
|
+
return keys.map(key => columnHelper.accessor((row) => {
|
|
67
|
+
if (rowHasMetadata(row)) {
|
|
68
|
+
return row?.metadata?.find(m => m.key === key)?.value ?? '';
|
|
69
|
+
}
|
|
70
|
+
return '';
|
|
71
|
+
}, {
|
|
72
|
+
id: key,
|
|
73
|
+
header: key, // tooltip header
|
|
74
|
+
enableSorting: false,
|
|
75
|
+
cell: (info) => {
|
|
76
|
+
const value = info.getValue();
|
|
77
|
+
// Handle null/undefined values
|
|
78
|
+
if (value === null || value === undefined)
|
|
79
|
+
return "";
|
|
80
|
+
// For primitive types, return the string representation
|
|
81
|
+
return String(value);
|
|
82
|
+
},
|
|
83
|
+
}));
|
|
84
|
+
}, [query.data?.data]);
|
|
85
|
+
/**
|
|
86
|
+
* ID,createdAt and updatedAt will be added by default for all tables
|
|
87
|
+
* If selection is allowed, checkbox will be added
|
|
88
|
+
* If the dto has metadata, dynamics columns for all the metadata key-value will be added(particular for a view)
|
|
89
|
+
* If there are actions for the table, they will be placed fixed at the right side of table.
|
|
90
|
+
*/
|
|
91
|
+
const cols = [
|
|
92
|
+
...(allowSelection ? TABLE_CHECK_BOX_COLUMN : []),
|
|
93
|
+
...TABLE_ID_COLUMN,
|
|
94
|
+
...columns.filter((col) => col.id !== ACTIONS_COLUMN_ID),
|
|
95
|
+
...(metadataColumns.length ? metadataColumns : []),
|
|
96
|
+
...TABLE_DEFAULT_DATE_COLUMNS,
|
|
97
|
+
// Actions column
|
|
98
|
+
...columns.filter((col) => col.id === ACTIONS_COLUMN_ID),
|
|
99
|
+
];
|
|
100
|
+
// Type-guard for updater
|
|
101
|
+
function isUpdaterFunction(updater) {
|
|
102
|
+
return typeof updater === "function";
|
|
103
|
+
}
|
|
104
|
+
// Utility function to resolve updater
|
|
105
|
+
function resolveUpdater(updater, currentValue) {
|
|
106
|
+
if (isUpdaterFunction(updater)) {
|
|
107
|
+
return updater(currentValue);
|
|
108
|
+
}
|
|
109
|
+
return updater;
|
|
110
|
+
}
|
|
111
|
+
// Use react-table's hook to create the table instance
|
|
112
|
+
const tanTable = useReactTable({
|
|
113
|
+
data: query.data?.data ?? [],
|
|
114
|
+
columns: cols,
|
|
115
|
+
getCoreRowModel: getCoreRowModel(),
|
|
116
|
+
rowCount: query.data?.total,
|
|
117
|
+
manualPagination: true, // Handle pagination manually since pagination is done server side for data tables
|
|
118
|
+
onPaginationChange: (updater) => {
|
|
119
|
+
updateTableState({ pagination: resolveUpdater(updater, pagination) });
|
|
120
|
+
},
|
|
121
|
+
manualSorting: true, // Handle sorting manually since sorting is done server side for data tables
|
|
122
|
+
onSortingChange: (updater) => {
|
|
123
|
+
updateTableState({ sorting: [...resolveUpdater(updater, sorting)], rowSelection: {} }); // Reset selection when sorting.
|
|
124
|
+
},
|
|
125
|
+
manualFiltering: true, // Handle filtering manually since filtering is done server side for data tables
|
|
126
|
+
onColumnVisibilityChange: (updater) => {
|
|
127
|
+
setColumnVisibility(resolveUpdater(updater, columnVisibility));
|
|
128
|
+
},
|
|
129
|
+
onRowSelectionChange: (updater) => {
|
|
130
|
+
updateTableState({ rowSelection: resolveUpdater(updater, rowSelection) });
|
|
131
|
+
},
|
|
132
|
+
onColumnOrderChange: (updater) => {
|
|
133
|
+
setColumnOrder(resolveUpdater(updater, columnOrder));
|
|
134
|
+
},
|
|
135
|
+
state: {
|
|
136
|
+
sorting: sorting,
|
|
137
|
+
columnVisibility: columnVisibility,
|
|
138
|
+
pagination: pagination,
|
|
139
|
+
rowSelection: rowSelection,
|
|
140
|
+
columnOrder: columnOrder
|
|
141
|
+
},
|
|
142
|
+
meta: {
|
|
143
|
+
refetch: query.refetch,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
// By default, ColumnDef does not give guarantees of column.id existing. Once useReactTable is called, all columns are assigned IDs.
|
|
147
|
+
// This populates the columnIds in the columnOrder state
|
|
148
|
+
// TODO, add localStorage access layer for this.
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
setColumnOrder([...tanTable.getAllLeafColumns().map(c => c.id)]);
|
|
151
|
+
}, []);
|
|
152
|
+
return { tableState, updateTableState, query, setSearchQuery, searchQuery, tanTable, mergedFilters, filters, setFilters, filterConfig };
|
|
153
|
+
}
|
|
154
|
+
export const DataTableContext = createContext(null);
|
|
155
|
+
export const useDataTable = () => {
|
|
156
|
+
const ctx = useContext(DataTableContext);
|
|
157
|
+
if (!ctx) {
|
|
158
|
+
throw Error("DataTable should be used within DataTableProvider.");
|
|
159
|
+
}
|
|
160
|
+
return ctx;
|
|
161
|
+
};
|
|
162
|
+
export function DataTableProvider({ children, ...props }) {
|
|
163
|
+
return (_jsx(DataTableContext.Provider, { value: props, children: children }));
|
|
164
|
+
}
|
|
165
|
+
export function DataTable({ tableActions, className, ...props
|
|
166
|
+
// filterableFields,
|
|
167
|
+
}) {
|
|
168
|
+
// State for managing table data and filters
|
|
169
|
+
const { query, tanTable } = useDataTable();
|
|
170
|
+
return (_jsx(_Fragment, { children: _jsxs("section", { ...props, className: cn("flex flex-col bg-card", className), children: [_jsx(Actions, { tableActions: tableActions }), _jsxs("div", { className: "w-full overflow-auto border-x grow min-h-table relative", tabIndex: 0, children: [query.isLoading && (_jsx(TableOverlay, { className: "cursor-wait", children: _jsx(Loader, {}) })), !query.isLoading && tanTable.getRowModel().rows.length === 0 && (
|
|
171
|
+
// Empty table
|
|
172
|
+
_jsx(TableOverlay, { className: "cursor-not-allowed", children: !query.isFetching &&
|
|
173
|
+
(query.isError ? (_jsxs("span", { className: "flex gap-3 justify-center items-center", children: [_jsx(Info, {}), _jsx("span", { children: "You don't have the required permissions. Please contact your admin." })] })) : !query.data?.data ? (_jsx(_Fragment, { children: "No results found." })) : (_jsx(_Fragment, { children: "Unknown error. Please contact customer support." }))) })), !query.isLoading && tanTable.getRowModel().rows.length !== 0 &&
|
|
174
|
+
_jsx(TableContent, { className: "size-full" })] }), _jsxs("div", { className: "flex w-full justify-between border gap-icon p-icon overflow-x-auto", children: [_jsxs("div", { className: "flex gap-icon", children: [_jsx(ColumnPicker, {}), _jsx(PageSize, {})] }), _jsx(Paginator, {})] })] }) }));
|
|
175
|
+
}
|
|
176
|
+
/** Table overlay to be shown for loaders or other messages */
|
|
177
|
+
function TableOverlay({ children, className, }) {
|
|
178
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: cn(className, "absolute top-0 bg-card z-20 size-full text-sm flex items-center justify-center"), children: children }), _jsx("span", { className: "relative h-full w-0 block" })] }));
|
|
179
|
+
}
|
|
180
|
+
import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
181
|
+
function ColumnPicker() {
|
|
182
|
+
const { tanTable } = useDataTable();
|
|
183
|
+
const [activeId, setActiveId] = useState(null);
|
|
184
|
+
const resourceFormatter = useResourceFormatter();
|
|
185
|
+
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
|
|
186
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
187
|
+
}));
|
|
188
|
+
return (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: (event) => {
|
|
189
|
+
const { active } = event;
|
|
190
|
+
setActiveId(active.id.toString());
|
|
191
|
+
}, onDragEnd: (event) => {
|
|
192
|
+
const { active, over } = event;
|
|
193
|
+
if (over && active.id !== over.id) {
|
|
194
|
+
const columnOrder = tanTable.getState().columnOrder;
|
|
195
|
+
const oldIndex = columnOrder.indexOf(active.id.toString());
|
|
196
|
+
const newIndex = columnOrder.indexOf(over.id.toString());
|
|
197
|
+
tanTable.setColumnOrder([...arrayMove(columnOrder, oldIndex, newIndex)]);
|
|
198
|
+
}
|
|
199
|
+
setActiveId(null);
|
|
200
|
+
}, children: _jsxs(SortableContext, { items: tanTable.getState().columnOrder, strategy: verticalListSortingStrategy, children: [_jsxs(EasyMenu, { label: _jsxs(_Fragment, { children: [_jsx(Columns3, {}), "Columns"] }), selectionMode: "multiple", items: tanTable.getAllFlatColumns(), selectedKeys: tanTable.getIsAllColumnsVisible() ? 'all' : tanTable.getVisibleFlatColumns().map(c => c.id), children: [_jsx(MenuItem, { onAction: () => tanTable.toggleAllColumnsVisible(), className: 'italic', children: "(select all)" }), tanTable.getState().columnOrder.map(colId => {
|
|
201
|
+
const col = tanTable.getAllFlatColumns().find(c => c.id === colId);
|
|
202
|
+
if (!col)
|
|
203
|
+
return null;
|
|
204
|
+
return _jsx(SortableItem, { column: col }, col.id);
|
|
205
|
+
})] }), _jsx(DragOverlay, { children: activeId ? _jsx("div", { className: "dropdown-item opacity-70 border-2 border-primary", children: resourceFormatter(activeId) }) : null })] }) }));
|
|
206
|
+
function SortableItem({ column }) {
|
|
207
|
+
const { attributes, listeners, setNodeRef, transform, transition, } = useSortable({ id: column.id });
|
|
208
|
+
const style = {
|
|
209
|
+
transform: CSS.Transform.toString(transform),
|
|
210
|
+
transition,
|
|
211
|
+
zIndex: '999'
|
|
212
|
+
};
|
|
213
|
+
return _jsxs(MenuItem, { ref: setNodeRef, style: style, ...attributes, id: column.id, onAction: () => column.toggleVisibility(), isDisabled: !column.getCanHide(), className: "flex items-center", children: [_jsx(GripVertical, { ...listeners, className: "size-icon cursor-grab" }), resourceFormatter(column.id), column.getIsSorted() && _jsx(SortIcon, { className: "size-icon", direction: column.getIsSorted() })] });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight, } from "lucide-react";
|
|
217
|
+
function Paginator() {
|
|
218
|
+
const { tanTable, query } = useDataTable();
|
|
219
|
+
const rowCount = query.data?.total ?? 0;
|
|
220
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "whitespace-nowrap caption text-muted", children: `${tanTable.getState().pagination.pageIndex * tanTable.getState().pagination.pageSize + 1} - ${Math.min((tanTable.getState().pagination.pageIndex + 1) *
|
|
221
|
+
tanTable.getState().pagination.pageSize, rowCount)} of ${rowCount?.toLocaleString()}` }), _jsx(Button, { onPress: () => tanTable.firstPage(), isDisabled: !tanTable.getCanPreviousPage(), variant: "neutral", size: "icon", children: _jsx(ChevronFirst, {}) }), _jsx(Button, { onPress: () => tanTable.previousPage(), isDisabled: !tanTable.getCanPreviousPage(), variant: "neutral", size: "icon", children: _jsx(ChevronLeft, {}) }), _jsx(Button, { onPress: () => tanTable.nextPage(), isDisabled: !tanTable.getCanNextPage(), variant: "neutral", size: "icon", children: _jsx(ChevronRight, {}) }), _jsx(Button, { onClick: () => tanTable.lastPage(), isDisabled: !tanTable.getCanNextPage(), variant: "neutral", size: "icon", children: _jsx(ChevronLast, {}) })] }));
|
|
222
|
+
}
|
|
223
|
+
function PageSize() {
|
|
224
|
+
const { tanTable } = useDataTable();
|
|
225
|
+
const PAGE_SIZES = [10, 20, 30, 40, 50];
|
|
226
|
+
return (_jsx(EasyMenu, { label: tanTable.getState().pagination.pageSize.toString(), selectionMode: "single", selectedKeys: [tanTable.getState().pagination.pageSize.toString()], items: PAGE_SIZES.map(s => ({ id: s.toString(), value: s })), children: (items) => _jsx(MenuItem, { onAction: () => tanTable.setPageSize(items.value), children: items.value }) }));
|
|
227
|
+
}
|
|
228
|
+
import { RotateCw } from "lucide-react";
|
|
229
|
+
function Actions({ tableActions }) {
|
|
230
|
+
const { query, tanTable, searchQuery, setSearchQuery } = useDataTable();
|
|
231
|
+
return (_jsxs("section", { className: "flex bg-card justify-between my-0 p-icon border gap-icon overflow-auto", children: [_jsxs("div", { className: "flex gap-icon", children: [_jsx(Button, { isPending: query.isFetching, onClick: () => query.refetch(), variant: "neutral", size: "icon", children: _jsx(RotateCw, {}) }), tableActions
|
|
232
|
+
.filter(ta => ta.bulk === tanTable.getSelectedRowModel().rows.length > 0)
|
|
233
|
+
.map((ta, i) => {
|
|
234
|
+
const Icon = ta.icon;
|
|
235
|
+
return (_jsx(Button, { type: "button", isDisabled: query.isFetching, className: "animate-in fade-in slide-in-from-left-15 duration-300 transition-transform", onPress: (e) => { ta.onClick(e, tanTable); }, size: 'icon', children: _jsx(Icon, {}) }, `${i}-${ta.bulk}`));
|
|
236
|
+
})] }), _jsxs("div", { className: "flex gap-icon", children: [_jsx(DataTableFilter, {}), (_jsx(SearchField, { value: searchQuery, onChange: setSearchQuery }))] })] }));
|
|
237
|
+
}
|
|
238
|
+
import { closestCenter, DndContext, DragOverlay, KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
|
|
239
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
240
|
+
import { flexRender } from "@tanstack/react-table";
|
|
241
|
+
import { DataTableFilter } from "lib/components/data-table/data-table-filter";
|
|
242
|
+
import { TABLE_CHECK_BOX_COLUMN, TABLE_DEFAULT_DATE_COLUMNS, TABLE_ID_COLUMN } from "lib/components/data-table/table-commons";
|
|
243
|
+
import { SearchField } from "lib/components/inputs/searchfield";
|
|
244
|
+
import { Button } from "lib/components/ui/button";
|
|
245
|
+
import { Loader } from "lib/components/ui/loader";
|
|
246
|
+
import { EasyMenu, MenuItem } from "lib/components/ui/menu";
|
|
247
|
+
import { TableBody, TableCell, Table as TableComponent, TableHead, TableHeader, TableRow, } from "lib/components/ui/table";
|
|
248
|
+
import { cn } from "lib/utils/primitives";
|
|
249
|
+
import { useResourceFormatter } from "lib/utils/resource-names";
|
|
250
|
+
import { merge } from "lodash-es";
|
|
251
|
+
function SortIcon({ direction, ...props }) {
|
|
252
|
+
if (direction === 'asc')
|
|
253
|
+
return _jsx(ArrowDownNarrowWide, { ...props });
|
|
254
|
+
else if (direction === 'desc')
|
|
255
|
+
return _jsx(ArrowDownWideNarrow, { ...props });
|
|
256
|
+
else
|
|
257
|
+
return _jsx(ArrowUpDown, { ...props });
|
|
258
|
+
}
|
|
259
|
+
// TODO, automate checking valid HTML
|
|
260
|
+
function TableContent({ className }) {
|
|
261
|
+
const { tanTable } = useDataTable();
|
|
262
|
+
const tableCellStyle = (isSticky, className) => cn("animate-in fade-in slide-in-from-top-10", "px-icon py-2 text-left text-sm font-medium whitespace-nowrap", isSticky && "bg-card sticky right-0 z-50 text-center", className);
|
|
263
|
+
return (_jsxs(TableComponent, { className: cn(className), children: [_jsx(TableHeader, { className: "sticky top-0 z-10", children: tanTable.getHeaderGroups().map((headerGroup) => (_jsx(TableRow, { className: cn("h-input"), children: headerGroup.headers.map((header) => (_jsxs(TableHead, { className: tableCellStyle(false, "bg-card"), children: [!header.column.getCanSort() && !header.isPlaceholder
|
|
264
|
+
&& _jsx(_Fragment, { children: flexRender(header.column.columnDef.header, header.getContext()) }), header.column.getCanSort() && (_jsxs(Button, { variant: "ghost", className: "w-full !justify-start !px-1.5", onPress: header.column.getToggleSortingHandler(), children: [flexRender(header.column.columnDef.header, header.getContext()), _jsx(SortIcon, { direction: header.column.getIsSorted() })] }))] }, header.id))) }, headerGroup.id))) }), _jsx(TableBody, { className: "flex-1 overflow-y-auto relative", children: tanTable.getRowModel().rows.map((row) => (_jsx(TableRow, { className: cn("h-input transition-colors data-[selected=true]:bg-primary/10 hover:bg-muted-foreground/20"), "data-selected": row.getIsSelected(), children: row.getVisibleCells().map((cell) => (_jsx(TableCell, { className: tableCellStyle(cell.column.id === ACTIONS_COLUMN_ID), children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, row.id))) })] }));
|
|
265
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { components, operations, paths } from "@cryptlex/web-api-types";
|
|
2
|
+
import type { PaginationState, SortingState, Table } from "@tanstack/react-table";
|
|
3
|
+
import type { LucideIcon } from "lucide-react";
|
|
4
|
+
import type createClient from "openapi-fetch";
|
|
5
|
+
import type { PressEvent } from "react-aria-components";
|
|
6
|
+
export declare const TABLE_CHECK_BOX_COLUMN: import("@tanstack/react-table").AccessorKeyColumnDef<any, any>[];
|
|
7
|
+
export declare const TABLE_ID_COLUMN: import("@tanstack/react-table").AccessorKeyColumnDef<any, any>[];
|
|
8
|
+
export declare const TABLE_DEFAULT_DATE_COLUMNS: import("@tanstack/react-table").AccessorKeyColumnDef<any, any>[];
|
|
9
|
+
export declare function formatDate(date: string | null | undefined): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Format multiple license parameters (expired, suspended, revoked) into a single status
|
|
12
|
+
*/
|
|
13
|
+
export declare function getLicenseStatus(license: any): string;
|
|
14
|
+
export declare function getValidityDisplay(validity: number | undefined): string;
|
|
15
|
+
export declare function secondsToDuration(seconds: number): string;
|
|
16
|
+
export declare function getValueFromData(data: any, accessor: string | number | symbol): any;
|
|
17
|
+
export declare const ALL_OS: {
|
|
18
|
+
[key: string]: string;
|
|
19
|
+
};
|
|
20
|
+
export type VisibilityState<T> = {
|
|
21
|
+
[K in keyof T]?: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type TableActions = ({
|
|
24
|
+
onClick: (e: PressEvent, t: Table<any>) => void;
|
|
25
|
+
bulk: boolean;
|
|
26
|
+
icon: LucideIcon;
|
|
27
|
+
tooltip?: string;
|
|
28
|
+
})[];
|
|
29
|
+
export type TableFetchFn<TData, TOperation extends keyof operations> = (p: PaginationState, s: SortingState, q: string, f: ApiFilters<TOperation>) => Promise<{
|
|
30
|
+
total: number;
|
|
31
|
+
data: TData[] | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
/*** Type for hide some of the columns based on the dto of the particular page
|
|
34
|
+
** `id`, `updatedAt` are by default hidden
|
|
35
|
+
*/
|
|
36
|
+
export type DefaultVisibilityState<T> = {
|
|
37
|
+
[K in keyof T]?: boolean;
|
|
38
|
+
};
|
|
39
|
+
type Client = ReturnType<typeof createClient<paths>>;
|
|
40
|
+
type GetPaths = {
|
|
41
|
+
[P in keyof paths]: paths[P] extends {
|
|
42
|
+
get: any;
|
|
43
|
+
} ? P : never;
|
|
44
|
+
}[keyof paths];
|
|
45
|
+
export type ApiSchema<T extends keyof components['schemas']> = components['schemas'][T];
|
|
46
|
+
export type ApiQuery<T extends keyof operations> = NonNullable<operations[T]['parameters']['query']>;
|
|
47
|
+
export type ApiGetAllParameters = {
|
|
48
|
+
page: number;
|
|
49
|
+
limit: number;
|
|
50
|
+
search?: string;
|
|
51
|
+
sort?: string;
|
|
52
|
+
};
|
|
53
|
+
export type ApiFilter<T extends keyof operations> = Omit<ApiQuery<T>, 'page' | 'limit' | 'sort' | 'search'>;
|
|
54
|
+
export type ApiFilters<T extends keyof operations> = NonNullable<Omit<ApiQuery<T>, 'page' | 'limit' | 'sort' | 'search'>>;
|
|
55
|
+
export declare function createTableFetchFn<Return, Operation extends keyof operations>(ctxclient: Client, path: GetPaths): TableFetchFn<Return, Operation>;
|
|
56
|
+
export {};
|