@ram_28/kf-ai-sdk 1.0.19 → 1.0.21

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.
Files changed (97) hide show
  1. package/README.md +45 -12
  2. package/dist/api/client.d.ts.map +1 -1
  3. package/dist/api/datetime.d.ts +59 -10
  4. package/dist/api/datetime.d.ts.map +1 -1
  5. package/dist/api/index.d.ts +3 -2
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api.cjs +1 -1
  8. package/dist/api.d.ts +1 -1
  9. package/dist/api.d.ts.map +1 -1
  10. package/dist/api.mjs +43 -21
  11. package/dist/api.types.d.ts +2 -1
  12. package/dist/api.types.d.ts.map +1 -1
  13. package/dist/auth/AuthProvider.d.ts.map +1 -1
  14. package/dist/auth.cjs +1 -1
  15. package/dist/auth.mjs +34 -34
  16. package/dist/base-types.d.ts +1 -1
  17. package/dist/base-types.d.ts.map +1 -1
  18. package/dist/client-BIkaIr2y.js +217 -0
  19. package/dist/client-DxjRcEtN.cjs +1 -0
  20. package/dist/components/hooks/useFilter/types.d.ts +14 -11
  21. package/dist/components/hooks/useFilter/types.d.ts.map +1 -1
  22. package/dist/components/hooks/useFilter/useFilter.d.ts +1 -1
  23. package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -1
  24. package/dist/components/hooks/useForm/apiClient.d.ts +45 -4
  25. package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -1
  26. package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
  27. package/dist/components/hooks/useKanban/types.d.ts +5 -22
  28. package/dist/components/hooks/useKanban/types.d.ts.map +1 -1
  29. package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -1
  30. package/dist/components/hooks/useTable/types.d.ts +19 -31
  31. package/dist/components/hooks/useTable/types.d.ts.map +1 -1
  32. package/dist/components/hooks/useTable/useTable.d.ts.map +1 -1
  33. package/dist/error-handling-CAoD0Kwb.cjs +1 -0
  34. package/dist/error-handling-CrhTtD88.js +14 -0
  35. package/dist/filter.cjs +1 -1
  36. package/dist/filter.mjs +1 -1
  37. package/dist/form.cjs +1 -1
  38. package/dist/form.mjs +736 -750
  39. package/dist/index.d.ts +18 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/kanban.cjs +2 -2
  42. package/dist/kanban.mjs +333 -323
  43. package/dist/{metadata-0lZAfuTP.cjs → metadata-Bz8zJqC1.cjs} +1 -1
  44. package/dist/{metadata-B88D_pVS.js → metadata-VbQzyD2C.js} +1 -1
  45. package/dist/table.cjs +1 -1
  46. package/dist/table.mjs +113 -96
  47. package/dist/table.types.d.ts +1 -1
  48. package/dist/table.types.d.ts.map +1 -1
  49. package/dist/types/base-fields.d.ts +71 -17
  50. package/dist/types/base-fields.d.ts.map +1 -1
  51. package/dist/types/common.d.ts +26 -18
  52. package/dist/types/common.d.ts.map +1 -1
  53. package/dist/useFilter-DzpP_ag0.cjs +1 -0
  54. package/dist/useFilter-H5bgAZQF.js +120 -0
  55. package/dist/utils/api/buildListOptions.d.ts +43 -0
  56. package/dist/utils/api/buildListOptions.d.ts.map +1 -0
  57. package/dist/utils/api/index.d.ts +2 -0
  58. package/dist/utils/api/index.d.ts.map +1 -0
  59. package/dist/utils/error-handling.d.ts +41 -0
  60. package/dist/utils/error-handling.d.ts.map +1 -0
  61. package/dist/utils/index.d.ts +2 -0
  62. package/dist/utils/index.d.ts.map +1 -1
  63. package/docs/QUICK_REFERENCE.md +142 -420
  64. package/docs/useAuth.md +52 -340
  65. package/docs/useFilter.md +858 -162
  66. package/docs/useForm.md +712 -501
  67. package/docs/useKanban.md +534 -279
  68. package/docs/useTable.md +725 -214
  69. package/package.json +1 -1
  70. package/sdk/api/client.ts +3 -41
  71. package/sdk/api/datetime.ts +98 -14
  72. package/sdk/api/index.ts +12 -6
  73. package/sdk/api.ts +6 -3
  74. package/sdk/api.types.ts +3 -4
  75. package/sdk/auth/AuthProvider.tsx +22 -24
  76. package/sdk/base-types.ts +2 -0
  77. package/sdk/components/hooks/useFilter/types.ts +14 -11
  78. package/sdk/components/hooks/useFilter/useFilter.ts +20 -18
  79. package/sdk/components/hooks/useForm/apiClient.ts +120 -5
  80. package/sdk/components/hooks/useForm/useForm.ts +97 -61
  81. package/sdk/components/hooks/useKanban/types.ts +7 -23
  82. package/sdk/components/hooks/useKanban/useKanban.ts +54 -18
  83. package/sdk/components/hooks/useTable/types.ts +26 -32
  84. package/sdk/components/hooks/useTable/useTable.llm.txt +8 -22
  85. package/sdk/components/hooks/useTable/useTable.ts +70 -25
  86. package/sdk/index.ts +157 -10
  87. package/sdk/table.types.ts +3 -0
  88. package/sdk/types/base-fields.ts +71 -17
  89. package/sdk/types/common.ts +33 -19
  90. package/sdk/utils/api/buildListOptions.ts +120 -0
  91. package/sdk/utils/api/index.ts +2 -0
  92. package/sdk/utils/error-handling.ts +150 -0
  93. package/sdk/utils/index.ts +6 -0
  94. package/dist/client-DgtkT50N.cjs +0 -1
  95. package/dist/client-V-WzUb8H.js +0 -237
  96. package/dist/useFilter-Dofowpr_.cjs +0 -1
  97. package/dist/useFilter-Dv-mr9QW.js +0 -117
package/docs/useTable.md CHANGED
@@ -1,44 +1,22 @@
1
1
  # useTable
2
2
 
3
- ## Brief Description
3
+ Complete table state management with data fetching, sorting, filtering, search, and pagination.
4
4
 
5
- - Provides complete table state management including data fetching, sorting, filtering, search, and pagination
6
- - Integrates with `useFilter` for advanced filtering capabilities with nested condition groups
7
- - Handles API interactions automatically using React Query for caching and background refetching
8
- - Returns flattened state accessors (`sort.field`, `pagination.currentPage`) for easy component integration
9
-
10
- ## Type Reference
5
+ ## Imports
11
6
 
12
7
  ```typescript
13
8
  import { useTable } from "@ram_28/kf-ai-sdk/table";
14
- import { isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
15
9
  import type {
16
10
  UseTableOptionsType,
17
11
  UseTableReturnType,
18
12
  ColumnDefinitionType,
13
+ PaginationStateType,
19
14
  } from "@ram_28/kf-ai-sdk/table/types";
20
- import type {
21
- ConditionType,
22
- ConditionGroupType,
23
- ConditionOperatorType,
24
- ConditionGroupOperatorType,
25
- FilterType,
26
- UseFilterReturnType,
27
- } from "@ram_28/kf-ai-sdk/filter/types";
28
- import type { ListResponseType } from "@ram_28/kf-ai-sdk/api/types";
29
-
30
- // Condition operators for comparing field values
31
- type ConditionOperatorType =
32
- | "EQ" | "NE" | "GT" | "GTE" | "LT" | "LTE"
33
- | "Between" | "NotBetween"
34
- | "IN" | "NIN"
35
- | "Empty" | "NotEmpty"
36
- | "Contains" | "NotContains"
37
- | "MinLength" | "MaxLength";
38
-
39
- // Group operators for combining conditions
40
- type ConditionGroupOperatorType = "And" | "Or" | "Not";
15
+ ```
41
16
 
17
+ ## Type Definitions
18
+
19
+ ```typescript
42
20
  // Column configuration
43
21
  interface ColumnDefinitionType<T> {
44
22
  fieldId: keyof T;
@@ -48,18 +26,20 @@ interface ColumnDefinitionType<T> {
48
26
  transform?: (value: any, row: T) => React.ReactNode;
49
27
  }
50
28
 
29
+ // Pagination state
30
+ interface PaginationStateType {
31
+ pageNo: number;
32
+ pageSize: number;
33
+ }
34
+
51
35
  // Hook options
52
36
  interface UseTableOptionsType<T> {
53
37
  source: string;
54
38
  columns: ColumnDefinitionType<T>[];
55
- enableSorting?: boolean;
56
- enableFiltering?: boolean;
57
- enablePagination?: boolean;
58
39
  initialState?: {
59
- pagination?: { pageNo: number; pageSize: number };
60
- sorting?: { field: keyof T; direction: "asc" | "desc" };
61
- filters?: Array<ConditionType | ConditionGroupType>;
62
- filterOperator?: ConditionGroupOperatorType;
40
+ sort?: Array<Record<string, "ASC" | "DESC">>;
41
+ pagination?: PaginationStateType; // Defaults: { pageNo: 1, pageSize: 10 }
42
+ filter?: UseFilterOptionsType<T>; // { conditions?, operator? }
63
43
  };
64
44
  onError?: (error: Error) => void;
65
45
  onSuccess?: (data: T[]) => void;
@@ -67,23 +47,16 @@ interface UseTableOptionsType<T> {
67
47
 
68
48
  // Hook return type
69
49
  interface UseTableReturnType<T> {
70
- // Data
71
50
  rows: T[];
72
51
  totalItems: number;
73
-
74
- // Loading states
75
52
  isLoading: boolean;
76
53
  isFetching: boolean;
77
54
  error: Error | null;
78
-
79
- // Search
80
55
  search: {
81
56
  query: string;
82
57
  setQuery: (value: string) => void;
83
58
  clear: () => void;
84
59
  };
85
-
86
- // Sort
87
60
  sort: {
88
61
  field: keyof T | null;
89
62
  direction: "asc" | "desc" | null;
@@ -91,13 +64,9 @@ interface UseTableReturnType<T> {
91
64
  clear: () => void;
92
65
  set: (field: keyof T, direction: "asc" | "desc") => void;
93
66
  };
94
-
95
- // Filter (uses useFilter internally)
96
- filter: UseFilterReturnType;
97
-
98
- // Pagination
67
+ filter: UseFilterReturnType<T>;
99
68
  pagination: {
100
- currentPage: number;
69
+ pageNo: number;
101
70
  pageSize: number;
102
71
  totalPages: number;
103
72
  totalItems: number;
@@ -108,192 +77,133 @@ interface UseTableReturnType<T> {
108
77
  goToPage: (page: number) => void;
109
78
  setPageSize: (size: number) => void;
110
79
  };
111
-
112
- // Operations
113
80
  refetch: () => Promise<ListResponseType<T>>;
114
81
  }
82
+ ```
83
+
84
+ ## Basic Example
85
+
86
+ A minimal table displaying data with loading and error states.
87
+
88
+ ```tsx
89
+ import { useTable } from "@ram_28/kf-ai-sdk/table";
90
+ import type { ColumnDefinitionType } from "@ram_28/kf-ai-sdk/table/types";
91
+ import { Product, ProductType } from "../sources";
92
+ import { Roles } from "../sources/roles";
115
93
 
116
- // API response structure
117
- interface ListResponseType<T> {
118
- Data: T[];
94
+ type BuyerProduct = ProductType<typeof Roles.Buyer>;
95
+
96
+ function ProductsTable() {
97
+ const product = new Product(Roles.Buyer);
98
+
99
+ const columns: ColumnDefinitionType<BuyerProduct>[] = [
100
+ { fieldId: "Title", label: "Name" },
101
+ { fieldId: "Price", label: "Price" },
102
+ { fieldId: "Category", label: "Category" },
103
+ ];
104
+
105
+ const table = useTable<BuyerProduct>({
106
+ source: product._id,
107
+ columns,
108
+ });
109
+
110
+ if (table.isLoading) return <div>Loading...</div>;
111
+ if (table.error) return <div>Error: {table.error.message}</div>;
112
+
113
+ return (
114
+ <table>
115
+ <thead>
116
+ <tr>
117
+ {columns.map((col) => (
118
+ <th key={String(col.fieldId)}>{col.label}</th>
119
+ ))}
120
+ </tr>
121
+ </thead>
122
+ <tbody>
123
+ {table.rows.map((row) => (
124
+ <tr key={row._id}>
125
+ <td>{row.Title}</td>
126
+ <td>${row.Price}</td>
127
+ <td>{row.Category}</td>
128
+ </tr>
129
+ ))}
130
+ </tbody>
131
+ </table>
132
+ );
119
133
  }
120
134
  ```
121
135
 
122
- ## Usage Example
136
+ ---
137
+
138
+ ## Initial State
139
+
140
+ ### Table with Initial Configuration
141
+
142
+ Set default pagination, sorting, and filters when the table loads.
123
143
 
124
144
  ```tsx
125
145
  import { useTable } from "@ram_28/kf-ai-sdk/table";
126
- import { isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
146
+ import { useAuth } from "@ram_28/kf-ai-sdk/auth";
127
147
  import type {
128
148
  UseTableOptionsType,
129
149
  UseTableReturnType,
130
150
  ColumnDefinitionType,
131
151
  } from "@ram_28/kf-ai-sdk/table/types";
132
- import type {
133
- ConditionType,
134
- ConditionGroupType,
135
- ConditionOperatorType,
136
- ConditionGroupOperatorType,
137
- FilterType,
138
- UseFilterReturnType,
139
- } from "@ram_28/kf-ai-sdk/filter/types";
140
- import type { ListResponseType } from "@ram_28/kf-ai-sdk/api/types";
141
152
  import { Product, ProductType } from "../sources";
142
153
  import { Roles } from "../sources/roles";
143
154
 
144
- // Get the typed product for the Buyer role
145
155
  type BuyerProduct = ProductType<typeof Roles.Buyer>;
146
156
 
147
- function ProductsPage() {
148
- // Instantiate the Product source with role
157
+ function MyItemsTable() {
149
158
  const product = new Product(Roles.Buyer);
159
+ const { user } = useAuth();
150
160
 
151
- // Column definitions with type safety
152
161
  const columns: ColumnDefinitionType<BuyerProduct>[] = [
153
162
  { fieldId: "Title", label: "Name", enableSorting: true },
154
- {
155
- fieldId: "Price",
156
- label: "Price",
157
- enableSorting: true,
158
- transform: (value) => `$${value.toFixed(2)}`,
159
- },
160
- { fieldId: "Category", label: "Category", enableSorting: true, enableFiltering: true },
163
+ { fieldId: "Price", label: "Price", enableSorting: true },
164
+ { fieldId: "Category", label: "Category", enableSorting: true },
161
165
  { fieldId: "Stock", label: "Stock", enableSorting: true },
162
166
  ];
163
167
 
164
- // Hook configuration with full options
165
- const options: UseTableOptionsType<BuyerProduct> = {
166
- source: product._id, // Use the Business Object ID from the Product class
168
+ const tableOptions: UseTableOptionsType<BuyerProduct> = {
169
+ source: product._id,
167
170
  columns,
168
- enableSorting: true,
169
- enableFiltering: true,
170
- enablePagination: true,
171
171
  initialState: {
172
+ sort: [{ Title: "ASC" }],
172
173
  pagination: { pageNo: 1, pageSize: 10 },
173
- sorting: { field: "_created_at", direction: "desc" },
174
- filterOperator: "And",
174
+ filter: {
175
+ conditions: [
176
+ {
177
+ Operator: "EQ",
178
+ LHSField: "_created_by",
179
+ RHSValue: { _id: user?._id, _name: user?._name },
180
+ RHSType: "Constant",
181
+ },
182
+ ],
183
+ operator: "And",
184
+ },
175
185
  },
176
- onError: (error: Error) => console.error("Table error:", error.message),
177
- onSuccess: (data: BuyerProduct[]) => console.log("Loaded", data.length, "products"),
178
- };
179
-
180
- const table: UseTableReturnType<BuyerProduct> = useTable<BuyerProduct>(options);
181
-
182
- // Access filter functionality (UseFilterReturnType)
183
- const filterState: UseFilterReturnType = table.filter;
184
-
185
- // Add a filter with dynamic operator selection
186
- const addFilter = (field: keyof BuyerProduct, operator: ConditionOperatorType, value: any) => {
187
- table.filter.addCondition({
188
- Operator: operator,
189
- LHSField: field as string,
190
- RHSValue: value,
191
- });
192
186
  };
193
187
 
194
- // Add a simple filter condition
195
- const filterByCategory = (category: string) => {
196
- table.filter.clearAllConditions();
197
- addFilter("Category", "EQ", category);
198
- };
199
-
200
- // Add a complex nested filter (Price > 100 OR Stock < 10)
201
- const addComplexFilter = () => {
202
- const groupId = table.filter.addConditionGroup("Or");
203
- table.filter.addCondition({ Operator: "GT", LHSField: "Price", RHSValue: 100 }, groupId);
204
- table.filter.addCondition({ Operator: "LT", LHSField: "Stock", RHSValue: 10 }, groupId);
205
- };
206
-
207
- // Display active filters using type guards
208
- const renderActiveFilters = () => (
209
- <div className="active-filters">
210
- {table.filter.items.map((item) => {
211
- if (isCondition(item)) {
212
- return (
213
- <span key={item.id} className="filter-tag">
214
- {item.LHSField} {item.Operator} {String(item.RHSValue)}
215
- <button onClick={() => table.filter.removeCondition(item.id!)}>×</button>
216
- </span>
217
- );
218
- }
219
- if (isConditionGroup(item)) {
220
- return (
221
- <span key={item.id} className="filter-group-tag">
222
- {item.Operator} Group ({item.Condition.length} conditions)
223
- <button onClick={() => table.filter.removeCondition(item.id!)}>×</button>
224
- </span>
225
- );
226
- }
227
- return null;
228
- })}
229
- </div>
230
- );
188
+ const table: UseTableReturnType<BuyerProduct> =
189
+ useTable<BuyerProduct>(tableOptions);
231
190
 
232
- // Toggle filter logic operator
233
- const toggleFilterLogic = () => {
234
- const next: ConditionGroupOperatorType = table.filter.operator === "And" ? "Or" : "And";
235
- table.filter.setRootOperator(next);
236
- };
237
-
238
- // Refetch data manually
239
- const handleRefresh = async () => {
240
- const response: ListResponseType<BuyerProduct> = await table.refetch();
241
- console.log(`Refreshed: ${response.Data.length} items`);
242
- };
243
-
244
- // Access filter payload for debugging
245
- const getFilterPayload = (): FilterType | undefined => {
246
- return table.filter.payload;
247
- };
248
-
249
- if (table.isLoading) {
250
- return <div>Loading products...</div>;
251
- }
252
-
253
- if (table.error) {
254
- return <div>Error: {table.error.message}</div>;
255
- }
191
+ if (table.isLoading) return <div>Loading...</div>;
256
192
 
257
193
  return (
258
- <div className="products-page">
259
- {/* Search */}
260
- <div className="search-bar">
261
- <input
262
- type="text"
263
- placeholder="Search products..."
264
- value={table.search.query}
265
- onChange={(e) => table.search.setQuery(e.target.value)}
266
- />
267
- <button onClick={table.search.clear}>Clear</button>
268
- </div>
269
-
270
- {/* Filter Controls */}
271
- <div className="filter-controls">
272
- <button onClick={() => filterByCategory("Electronics")}>Electronics</button>
273
- <button onClick={() => filterByCategory("Books")}>Books</button>
274
- <button onClick={addComplexFilter}>Add Complex Filter</button>
275
- <button onClick={toggleFilterLogic}>
276
- Logic: {table.filter.operator}
277
- </button>
278
- {table.filter.hasConditions && (
279
- <button onClick={() => table.filter.clearAllConditions()}>Clear All Filters</button>
280
- )}
281
- </div>
282
-
283
- {/* Active Filters */}
284
- {renderActiveFilters()}
285
-
286
- {/* Table */}
194
+ <div>
287
195
  <table>
288
196
  <thead>
289
197
  <tr>
290
198
  {columns.map((col) => (
291
199
  <th
292
200
  key={String(col.fieldId)}
293
- onClick={() => col.enableSorting && table.sort.toggle(col.fieldId)}
201
+ onClick={() =>
202
+ col.enableSorting && table.sort.toggle(col.fieldId)
203
+ }
294
204
  style={{ cursor: col.enableSorting ? "pointer" : "default" }}
295
205
  >
296
- {col.label || String(col.fieldId)}
206
+ {col.label}
297
207
  {table.sort.field === col.fieldId && (
298
208
  <span>{table.sort.direction === "asc" ? " ↑" : " ↓"}</span>
299
209
  )}
@@ -302,10 +212,10 @@ function ProductsPage() {
302
212
  </tr>
303
213
  </thead>
304
214
  <tbody>
305
- {table.rows.map((row: BuyerProduct) => (
215
+ {table.rows.map((row) => (
306
216
  <tr key={row._id}>
307
217
  <td>{row.Title}</td>
308
- <td>${row.Price.toFixed(2)}</td>
218
+ <td>${row.Price}</td>
309
219
  <td>{row.Category}</td>
310
220
  <td>{row.Stock}</td>
311
221
  </tr>
@@ -313,7 +223,6 @@ function ProductsPage() {
313
223
  </tbody>
314
224
  </table>
315
225
 
316
- {/* Pagination */}
317
226
  <div className="pagination">
318
227
  <button
319
228
  onClick={table.pagination.goToPrevious}
@@ -322,15 +231,371 @@ function ProductsPage() {
322
231
  Previous
323
232
  </button>
324
233
  <span>
325
- Page {table.pagination.currentPage} of {table.pagination.totalPages}
326
- ({table.pagination.totalItems} total)
234
+ Page {table.pagination.pageNo} of {table.pagination.totalPages}
235
+ </span>
236
+ <button
237
+ onClick={table.pagination.goToNext}
238
+ disabled={!table.pagination.canGoNext}
239
+ >
240
+ Next
241
+ </button>
242
+ </div>
243
+ </div>
244
+ );
245
+ }
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Filter
251
+
252
+ ### Status Filter
253
+
254
+ Dropdown-based status filtering.
255
+
256
+ ```tsx
257
+ function ProductsWithStatusFilter() {
258
+ const product = new Product(Roles.Buyer);
259
+
260
+ const table = useTable<BuyerProduct>({
261
+ source: product._id,
262
+ columns,
263
+ });
264
+
265
+ const handleStatusChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
266
+ const status = e.target.value;
267
+ table.filter.clearAllConditions();
268
+
269
+ if (status !== "all") {
270
+ table.filter.addCondition({
271
+ Operator: "EQ",
272
+ LHSField: "IsActive",
273
+ RHSValue: status === "active",
274
+ RHSType: "Constant",
275
+ });
276
+ }
277
+ };
278
+
279
+ return (
280
+ <div>
281
+ <select onChange={handleStatusChange} defaultValue="all">
282
+ <option value="all">All Products</option>
283
+ <option value="active">Active</option>
284
+ <option value="inactive">Inactive</option>
285
+ </select>
286
+ {/* table rendering */}
287
+ </div>
288
+ );
289
+ }
290
+ ```
291
+
292
+ ### Multiple Filters Combined
293
+
294
+ Apply category and price range filters together.
295
+
296
+ ```tsx
297
+ const PRICE_RANGES = [
298
+ { label: "Under $25", min: 0, max: 25 },
299
+ { label: "$25 to $50", min: 25, max: 50 },
300
+ { label: "$50 to $100", min: 50, max: 100 },
301
+ { label: "$100 to $200", min: 100, max: 200 },
302
+ { label: "$200 & Above", min: 200, max: null },
303
+ ] as const;
304
+
305
+ function ProductsWithMultipleFilters() {
306
+ const product = new Product(Roles.Buyer);
307
+ const [selectedCategory, setSelectedCategory] = useState("all");
308
+ const [selectedPriceRange, setSelectedPriceRange] = useState<string | null>(
309
+ null,
310
+ );
311
+
312
+ const table = useTable<BuyerProduct>({
313
+ source: product._id,
314
+ columns,
315
+ });
316
+
317
+ // Helper to apply all active filters
318
+ const applyFilters = (category: string, priceRange: string | null) => {
319
+ table.filter.clearAllConditions();
320
+
321
+ // Apply category filter
322
+ if (category !== "all") {
323
+ table.filter.addCondition({
324
+ LHSField: "Category",
325
+ Operator: "EQ",
326
+ RHSValue: category,
327
+ RHSType: "Constant",
328
+ });
329
+ }
330
+
331
+ // Apply price filter
332
+ if (priceRange) {
333
+ const range = PRICE_RANGES.find((r) => r.label === priceRange);
334
+ if (range) {
335
+ if (range.max === null) {
336
+ // "$200 & Above" - use GTE
337
+ table.filter.addCondition({
338
+ Operator: "GTE",
339
+ LHSField: "Price",
340
+ RHSValue: range.min,
341
+ RHSType: "Constant",
342
+ });
343
+ } else if (range.min === 0) {
344
+ // "Under $25" - use LT
345
+ table.filter.addCondition({
346
+ Operator: "LT",
347
+ LHSField: "Price",
348
+ RHSValue: range.max,
349
+ RHSType: "Constant",
350
+ });
351
+ } else {
352
+ // Range like "$25 to $50" - use Between
353
+ table.filter.addCondition({
354
+ LHSField: "Price",
355
+ Operator: "Between",
356
+ RHSValue: [range.min, range.max],
357
+ RHSType: "Constant",
358
+ });
359
+ }
360
+ }
361
+ }
362
+ };
363
+
364
+ const handleCategoryChange = (category: string) => {
365
+ setSelectedCategory(category);
366
+ applyFilters(category, selectedPriceRange);
367
+ };
368
+
369
+ const handlePriceChange = (priceRange: string | null) => {
370
+ setSelectedPriceRange(priceRange);
371
+ applyFilters(selectedCategory, priceRange);
372
+ };
373
+
374
+ return (
375
+ <div>
376
+ <select
377
+ value={selectedCategory}
378
+ onChange={(e) => handleCategoryChange(e.target.value)}
379
+ >
380
+ <option value="all">All Categories</option>
381
+ <option value="Electronics">Electronics</option>
382
+ <option value="Books">Books</option>
383
+ </select>
384
+
385
+ <select
386
+ value={selectedPriceRange || ""}
387
+ onChange={(e) => handlePriceChange(e.target.value || null)}
388
+ >
389
+ <option value="">Any Price</option>
390
+ {PRICE_RANGES.map((range) => (
391
+ <option key={range.label} value={range.label}>
392
+ {range.label}
393
+ </option>
394
+ ))}
395
+ </select>
396
+
397
+ {/* table rendering */}
398
+ </div>
399
+ );
400
+ }
401
+ ```
402
+
403
+ ---
404
+
405
+ ## Sort
406
+
407
+ ### Column Sort Toggle
408
+
409
+ Click column headers to toggle sort direction.
410
+
411
+ ```tsx
412
+ function SortableTable() {
413
+ const product = new Product(Roles.Buyer);
414
+
415
+ const columns: ColumnDefinitionType<BuyerProduct>[] = [
416
+ { fieldId: "Title", label: "Name", enableSorting: true },
417
+ { fieldId: "Price", label: "Price", enableSorting: true },
418
+ { fieldId: "Category", label: "Category", enableSorting: true },
419
+ ];
420
+
421
+ const table = useTable<BuyerProduct>({
422
+ source: product._id,
423
+ columns,
424
+ });
425
+
426
+ return (
427
+ <table>
428
+ <thead>
429
+ <tr>
430
+ {columns.map((col) => (
431
+ <th
432
+ key={String(col.fieldId)}
433
+ onClick={() =>
434
+ col.enableSorting && table.sort.toggle(col.fieldId)
435
+ }
436
+ style={{ cursor: col.enableSorting ? "pointer" : "default" }}
437
+ >
438
+ {col.label}
439
+ {table.sort.field === col.fieldId && (
440
+ <span>{table.sort.direction === "asc" ? " ↑" : " ↓"}</span>
441
+ )}
442
+ </th>
443
+ ))}
444
+ </tr>
445
+ </thead>
446
+ <tbody>
447
+ {table.rows.map((row) => (
448
+ <tr key={row._id}>
449
+ <td>{row.Title}</td>
450
+ <td>${row.Price}</td>
451
+ <td>{row.Category}</td>
452
+ </tr>
453
+ ))}
454
+ </tbody>
455
+ </table>
456
+ );
457
+ }
458
+ ```
459
+
460
+ ### Sort Dropdown
461
+
462
+ Allow users to select sort order from a dropdown.
463
+
464
+ ```tsx
465
+ function TableWithSortDropdown() {
466
+ const product = new Product(Roles.Buyer);
467
+ const [selectedSort, setSelectedSort] = useState("featured");
468
+
469
+ const table = useTable<BuyerProduct>({
470
+ source: product._id,
471
+ columns,
472
+ initialState: {
473
+ sort: [{ Title: "ASC" }],
474
+ },
475
+ });
476
+
477
+ const handleSortChange = (value: string) => {
478
+ setSelectedSort(value);
479
+ switch (value) {
480
+ case "price-asc":
481
+ table.sort.set("Price", "asc");
482
+ break;
483
+ case "price-desc":
484
+ table.sort.set("Price", "desc");
485
+ break;
486
+ case "newest":
487
+ table.sort.set("_created_at", "desc");
488
+ break;
489
+ case "featured":
490
+ default:
491
+ table.sort.set("Title", "asc");
492
+ break;
493
+ }
494
+ };
495
+
496
+ return (
497
+ <div>
498
+ <select
499
+ value={selectedSort}
500
+ onChange={(e) => handleSortChange(e.target.value)}
501
+ >
502
+ <option value="featured">Featured</option>
503
+ <option value="price-asc">Price: Low to High</option>
504
+ <option value="price-desc">Price: High to Low</option>
505
+ <option value="newest">Newest Arrivals</option>
506
+ </select>
507
+ {/* table rendering */}
508
+ </div>
509
+ );
510
+ }
511
+ ```
512
+
513
+ ---
514
+
515
+ ## Pagination
516
+
517
+ ### Basic Pagination Controls
518
+
519
+ Navigate between pages with previous/next buttons.
520
+
521
+ ```tsx
522
+ function PaginatedTable() {
523
+ const product = new Product(Roles.Buyer);
524
+
525
+ const table = useTable<BuyerProduct>({
526
+ source: product._id,
527
+ columns,
528
+ initialState: {
529
+ pagination: { pageNo: 1, pageSize: 10 },
530
+ },
531
+ });
532
+
533
+ return (
534
+ <div>
535
+ {/* table rendering */}
536
+
537
+ <div className="pagination">
538
+ <button
539
+ onClick={table.pagination.goToPrevious}
540
+ disabled={!table.pagination.canGoPrevious}
541
+ >
542
+ Previous
543
+ </button>
544
+
545
+ <span>
546
+ Page {table.pagination.pageNo} of {table.pagination.totalPages}
547
+ </span>
548
+
549
+ <button
550
+ onClick={table.pagination.goToNext}
551
+ disabled={!table.pagination.canGoNext}
552
+ >
553
+ Next
554
+ </button>
555
+ </div>
556
+
557
+ <p>{table.pagination.totalItems} total items</p>
558
+ </div>
559
+ );
560
+ }
561
+ ```
562
+
563
+ ### Page Size Selector
564
+
565
+ Allow users to change the number of items per page.
566
+
567
+ ```tsx
568
+ function TableWithPageSize() {
569
+ const product = new Product(Roles.Buyer);
570
+
571
+ const table = useTable<BuyerProduct>({
572
+ source: product._id,
573
+ columns,
574
+ });
575
+
576
+ return (
577
+ <div>
578
+ {/* table rendering */}
579
+
580
+ <div className="pagination">
581
+ <button
582
+ onClick={table.pagination.goToPrevious}
583
+ disabled={!table.pagination.canGoPrevious}
584
+ >
585
+ Previous
586
+ </button>
587
+
588
+ <span>
589
+ Page {table.pagination.pageNo} of {table.pagination.totalPages}
327
590
  </span>
591
+
328
592
  <button
329
593
  onClick={table.pagination.goToNext}
330
594
  disabled={!table.pagination.canGoNext}
331
595
  >
332
596
  Next
333
597
  </button>
598
+
334
599
  <select
335
600
  value={table.pagination.pageSize}
336
601
  onChange={(e) => table.pagination.setPageSize(Number(e.target.value))}
@@ -338,34 +603,280 @@ function ProductsPage() {
338
603
  <option value={10}>10 per page</option>
339
604
  <option value={25}>25 per page</option>
340
605
  <option value={50}>50 per page</option>
606
+ <option value={100}>100 per page</option>
341
607
  </select>
608
+ </div>
609
+ </div>
610
+ );
611
+ }
612
+ ```
613
+
614
+ ### Jump to Page
615
+
616
+ Allow users to navigate directly to a specific page.
617
+
618
+ ```tsx
619
+ function TableWithPageJump() {
620
+ const product = new Product(Roles.Buyer);
621
+ const [pageInput, setPageInput] = useState("");
622
+
623
+ const table = useTable<BuyerProduct>({
624
+ source: product._id,
625
+ columns,
626
+ });
627
+
628
+ const handlePageJump = (e: React.KeyboardEvent<HTMLInputElement>) => {
629
+ if (e.key === "Enter") {
630
+ const page = parseInt(pageInput, 10);
631
+ if (page >= 1 && page <= table.pagination.totalPages) {
632
+ table.pagination.goToPage(page);
633
+ setPageInput("");
634
+ }
635
+ }
636
+ };
637
+
638
+ return (
639
+ <div>
640
+ {/* table rendering */}
641
+
642
+ <div className="pagination">
643
+ <button
644
+ onClick={table.pagination.goToPrevious}
645
+ disabled={!table.pagination.canGoPrevious}
646
+ >
647
+ Previous
648
+ </button>
649
+
650
+ <span>
651
+ Page {table.pagination.pageNo} of {table.pagination.totalPages}
652
+ </span>
653
+
654
+ <button
655
+ onClick={table.pagination.goToNext}
656
+ disabled={!table.pagination.canGoNext}
657
+ >
658
+ Next
659
+ </button>
660
+
342
661
  <input
343
662
  type="number"
344
663
  min={1}
345
664
  max={table.pagination.totalPages}
346
665
  placeholder="Go to page"
347
- onKeyDown={(e) => {
348
- if (e.key === "Enter") {
349
- table.pagination.goToPage(Number((e.target as HTMLInputElement).value));
350
- }
351
- }}
666
+ value={pageInput}
667
+ onChange={(e) => setPageInput(e.target.value)}
668
+ onKeyDown={handlePageJump}
352
669
  />
353
670
  </div>
671
+ </div>
672
+ );
673
+ }
674
+ ```
354
675
 
355
- {/* Actions */}
356
- <div className="table-actions">
357
- <button onClick={handleRefresh} disabled={table.isFetching}>
358
- {table.isFetching ? "Refreshing..." : "Refresh"}
359
- </button>
360
- <button onClick={() => table.sort.clear()}>Clear Sort</button>
361
- <button onClick={() => table.sort.set("Price", "desc")}>Sort by Price (desc)</button>
676
+ ---
677
+
678
+ ## Search
679
+
680
+ ### Basic Search
681
+
682
+ Add search functionality to filter results by text. The search has built-in debouncing (300ms).
683
+
684
+ ```tsx
685
+ function SearchableTable() {
686
+ const product = new Product(Roles.Buyer);
687
+
688
+ const table = useTable<BuyerProduct>({
689
+ source: product._id,
690
+ columns,
691
+ });
692
+
693
+ return (
694
+ <div>
695
+ <div className="search-bar">
696
+ <input
697
+ type="text"
698
+ placeholder="Search products..."
699
+ value={table.search.query}
700
+ onChange={(e) => table.search.setQuery(e.target.value)}
701
+ />
702
+ {table.search.query && (
703
+ <button onClick={table.search.clear}>Clear</button>
704
+ )}
705
+ {table.isFetching && <span>Searching...</span>}
706
+ </div>
707
+
708
+ {/* table rendering */}
709
+ </div>
710
+ );
711
+ }
712
+ ```
713
+
714
+ ---
715
+
716
+ ## Complete Example
717
+
718
+ A full-featured product listing page with filters, search, sort, and pagination.
719
+
720
+ ```tsx
721
+ import { useState } from "react";
722
+ import { useTable } from "@ram_28/kf-ai-sdk/table";
723
+ import type {
724
+ UseTableOptionsType,
725
+ UseTableReturnType,
726
+ ColumnDefinitionType,
727
+ } from "@ram_28/kf-ai-sdk/table/types";
728
+ import { Product, ProductType } from "../sources";
729
+ import { Roles } from "../sources/roles";
730
+
731
+ type BuyerProduct = ProductType<typeof Roles.Buyer>;
732
+
733
+ function ProductListPage() {
734
+ const product = new Product(Roles.Buyer);
735
+ const [selectedCategory, setSelectedCategory] = useState("all");
736
+ const [selectedSort, setSelectedSort] = useState("featured");
737
+
738
+ const columns: ColumnDefinitionType<BuyerProduct>[] = [
739
+ { fieldId: "Title", label: "Name", enableSorting: true },
740
+ { fieldId: "Price", label: "Price", enableSorting: true },
741
+ { fieldId: "Category", label: "Category", enableSorting: true },
742
+ { fieldId: "Stock", label: "Stock", enableSorting: true },
743
+ ];
744
+
745
+ const tableOptions: UseTableOptionsType<BuyerProduct> = {
746
+ source: product._id,
747
+ columns,
748
+ initialState: {
749
+ sort: [{ Title: "ASC" }],
750
+ pagination: { pageNo: 1, pageSize: 10 },
751
+ },
752
+ };
753
+
754
+ const table: UseTableReturnType<BuyerProduct> =
755
+ useTable<BuyerProduct>(tableOptions);
756
+
757
+ const handleCategoryChange = (category: string) => {
758
+ setSelectedCategory(category);
759
+ table.filter.clearAllConditions();
760
+ if (category !== "all") {
761
+ table.filter.addCondition({
762
+ LHSField: "Category",
763
+ Operator: "EQ",
764
+ RHSValue: category,
765
+ RHSType: "Constant",
766
+ });
767
+ }
768
+ };
769
+
770
+ const handleSortChange = (value: string) => {
771
+ setSelectedSort(value);
772
+ switch (value) {
773
+ case "price-asc":
774
+ table.sort.set("Price", "asc");
775
+ break;
776
+ case "price-desc":
777
+ table.sort.set("Price", "desc");
778
+ break;
779
+ case "newest":
780
+ table.sort.set("_created_at", "desc");
781
+ break;
782
+ default:
783
+ table.sort.set("Title", "asc");
784
+ break;
785
+ }
786
+ };
787
+
788
+ if (table.error) {
789
+ return (
790
+ <div>
791
+ <p>Error: {table.error.message}</p>
792
+ <button onClick={() => table.refetch()}>Try Again</button>
793
+ </div>
794
+ );
795
+ }
796
+
797
+ if (table.isLoading) {
798
+ return <div>Loading...</div>;
799
+ }
800
+
801
+ return (
802
+ <div>
803
+ {/* Controls */}
804
+ <div className="controls">
805
+ <input
806
+ type="text"
807
+ placeholder="Search..."
808
+ value={table.search.query}
809
+ onChange={(e) => table.search.setQuery(e.target.value)}
810
+ />
811
+
812
+ <select
813
+ value={selectedCategory}
814
+ onChange={(e) => handleCategoryChange(e.target.value)}
815
+ >
816
+ <option value="all">All Categories</option>
817
+ <option value="Electronics">Electronics</option>
818
+ <option value="Books">Books</option>
819
+ </select>
820
+
821
+ <select
822
+ value={selectedSort}
823
+ onChange={(e) => handleSortChange(e.target.value)}
824
+ >
825
+ <option value="featured">Featured</option>
826
+ <option value="price-asc">Price: Low to High</option>
827
+ <option value="price-desc">Price: High to Low</option>
828
+ <option value="newest">Newest</option>
829
+ </select>
362
830
  </div>
363
831
 
364
- {/* Debug: Filter payload */}
365
- <details>
366
- <summary>Filter Payload (Debug)</summary>
367
- <pre>{JSON.stringify(getFilterPayload(), null, 2)}</pre>
368
- </details>
832
+ {/* Results count */}
833
+ <p>{table.totalItems} results found</p>
834
+
835
+ {/* Product grid */}
836
+ {table.rows.length === 0 ? (
837
+ <div>
838
+ <p>No products found</p>
839
+ <button
840
+ onClick={() => {
841
+ table.search.clear();
842
+ table.filter.clearAllConditions();
843
+ setSelectedCategory("all");
844
+ }}
845
+ >
846
+ Clear Filters
847
+ </button>
848
+ </div>
849
+ ) : (
850
+ <div className="product-grid">
851
+ {table.rows.map((row) => (
852
+ <div key={row._id} className="product-card">
853
+ <h3>{row.Title}</h3>
854
+ <p>${row.Price}</p>
855
+ <p>{row.Category}</p>
856
+ <p>{row.Stock > 0 ? "In Stock" : "Out of Stock"}</p>
857
+ </div>
858
+ ))}
859
+ </div>
860
+ )}
861
+
862
+ {/* Pagination */}
863
+ <div className="pagination">
864
+ <button
865
+ onClick={table.pagination.goToPrevious}
866
+ disabled={!table.pagination.canGoPrevious}
867
+ >
868
+ Previous
869
+ </button>
870
+ <span>
871
+ Page {table.pagination.pageNo} of {table.pagination.totalPages}
872
+ </span>
873
+ <button
874
+ onClick={table.pagination.goToNext}
875
+ disabled={!table.pagination.canGoNext}
876
+ >
877
+ Next
878
+ </button>
879
+ </div>
369
880
  </div>
370
881
  );
371
882
  }