@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ram_28/kf-ai-sdk",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Type-safe, AI-driven SDK for building modern web applications with role-based access control",
5
5
  "author": "Ramprasad",
6
6
  "license": "MIT",
package/sdk/api/client.ts CHANGED
@@ -9,8 +9,6 @@ import type {
9
9
  CreateUpdateResponseType,
10
10
  DeleteResponseType,
11
11
  CountResponseType,
12
- DateTimeEncodedType,
13
- DateEncodedType,
14
12
  MetricOptionsType,
15
13
  MetricResponseType,
16
14
  PivotOptionsType,
@@ -157,40 +155,6 @@ export function getApiBaseUrl(): string {
157
155
  return apiConfig.baseUrl || "";
158
156
  }
159
157
 
160
- /**
161
- * Recursively process an object to decode datetime fields
162
- */
163
- function decodeResponseData<T>(data: any): T {
164
- if (data === null || data === undefined) {
165
- return data;
166
- }
167
-
168
- if (Array.isArray(data)) {
169
- return data.map((item) => decodeResponseData(item)) as T;
170
- }
171
-
172
- if (typeof data === "object") {
173
- // Check for datetime encoding
174
- if ("$__dt__" in data) {
175
- return new Date((data as DateTimeEncodedType).$__dt__ * 1000) as T;
176
- }
177
-
178
- // Check for date encoding
179
- if ("$__d__" in data) {
180
- return new Date((data as DateEncodedType).$__d__) as T;
181
- }
182
-
183
- // Recursively process object properties
184
- const result: any = {};
185
- for (const [key, value] of Object.entries(data)) {
186
- result[key] = decodeResponseData(value);
187
- }
188
- return result as T;
189
- }
190
-
191
- return data as T;
192
- }
193
-
194
158
  /**
195
159
  * Create a resource client for the specified Business Object
196
160
  * @param bo_id - Business Object identifier (e.g., "user", "leave", "vendor")
@@ -212,7 +176,7 @@ export function api<T = any>(bo_id: string): ResourceClient<T> {
212
176
  }
213
177
 
214
178
  const responseData: ReadResponseType<T> = await response.json();
215
- return decodeResponseData<T>(responseData.Data);
179
+ return responseData.Data;
216
180
  },
217
181
 
218
182
  async create(
@@ -278,10 +242,8 @@ export function api<T = any>(bo_id: string): ResourceClient<T> {
278
242
  throw new Error(`Failed to list ${bo_id}: ${response.statusText}`);
279
243
  }
280
244
 
281
- const responseData: ListResponseType<any> = await response.json();
282
- return {
283
- Data: responseData.Data.map((item) => decodeResponseData<T>(item)),
284
- };
245
+ const responseData: ListResponseType<T> = await response.json();
246
+ return responseData;
285
247
  },
286
248
 
287
249
  async count(options?: ListOptionsType): Promise<CountResponseType> {
@@ -1,33 +1,117 @@
1
- import type { DateTimeEncodedType, DateEncodedType } from '../types/common';
1
+ import type { DateEncodedType, DateTimeEncodedType } from "../types/base-fields";
2
2
 
3
3
  /**
4
- * Utility functions for datetime encoding/decoding
4
+ * Date format constants matching backend DatetimeFormat class
5
+ * Source: kf-ai-base/core/util/datetime_utils.py
5
6
  */
7
+ export const DatetimeFormat = {
8
+ /** Date format: "YYYY-MM-DD" */
9
+ DATE: "%Y-%m-%d",
10
+ /** Time format: "HH:MM:SS" */
11
+ TIME: "%H:%M:%S",
12
+ /** DateTime format: "YYYY-MM-DD HH:MM:SS" */
13
+ DATE_TIME: "%Y-%m-%d %H:%M:%S",
14
+ } as const;
15
+
16
+ // ============================================================
17
+ // DECODING FUNCTIONS (API Response → Date object)
18
+ // Use these in components to convert API response to Date
19
+ // ============================================================
6
20
 
7
21
  /**
8
- * Encode a Date object to API datetime format
22
+ * Decode API date response to Date object
23
+ * @param encoded - API response format { "$__d__": "YYYY-MM-DD" }
24
+ * @returns JavaScript Date object
25
+ *
26
+ * @example
27
+ * const apiResponse = { "$__d__": "2025-03-15" };
28
+ * const date = decodeDate(apiResponse); // => Date object
9
29
  */
10
- export function encodeDatetime(date: Date): DateTimeEncodedType {
11
- return { $__dt__: date.getTime() / 1000 };
30
+ export function decodeDate(encoded: DateEncodedType): Date {
31
+ const [year, month, day] = encoded.$__d__.split("-").map(Number);
32
+ return new Date(year, month - 1, day);
12
33
  }
13
34
 
14
35
  /**
15
- * Decode API datetime format to Date object
36
+ * Decode API datetime response to Date object
37
+ * @param encoded - API response format { "$__dt__": unix_timestamp_seconds }
38
+ * @returns JavaScript Date object
39
+ *
40
+ * @example
41
+ * const apiResponse = { "$__dt__": 1769110463 };
42
+ * const date = decodeDateTime(apiResponse); // => Date object
16
43
  */
17
- export function decodeDatetime(encoded: DateTimeEncodedType): Date {
44
+ export function decodeDateTime(encoded: DateTimeEncodedType): Date {
18
45
  return new Date(encoded.$__dt__ * 1000);
19
46
  }
20
47
 
48
+ // ============================================================
49
+ // FORMATTING FUNCTIONS (Date object → API Request string)
50
+ // Use these when creating/updating records
51
+ // ============================================================
52
+
21
53
  /**
22
- * Encode a Date object to API date format (YYYY-MM-DD)
54
+ * Format a Date object to API request date string
55
+ * @param date - JavaScript Date object
56
+ * @returns "YYYY-MM-DD" formatted string for API requests
57
+ *
58
+ * @example
59
+ * formatDate(new Date(2025, 2, 15)) // => "2025-03-15"
23
60
  */
24
- export function encodeDate(date: Date): DateEncodedType {
25
- return { $__d__: date.toISOString().split('T')[0] };
61
+ export function formatDate(date: Date): string {
62
+ const year = date.getFullYear();
63
+ const month = String(date.getMonth() + 1).padStart(2, "0");
64
+ const day = String(date.getDate()).padStart(2, "0");
65
+ return `${year}-${month}-${day}`;
26
66
  }
27
67
 
28
68
  /**
29
- * Decode API date format to Date object
69
+ * Format a Date object to API request datetime string
70
+ * @param date - JavaScript Date object
71
+ * @returns "YYYY-MM-DD HH:MM:SS" formatted string for API requests
72
+ *
73
+ * @example
74
+ * formatDateTime(new Date(2025, 2, 15, 10, 30, 45)) // => "2025-03-15 10:30:45"
30
75
  */
31
- export function decodeDate(encoded: DateEncodedType): Date {
32
- return new Date(encoded.$__d__);
33
- }
76
+ export function formatDateTime(date: Date): string {
77
+ const year = date.getFullYear();
78
+ const month = String(date.getMonth() + 1).padStart(2, "0");
79
+ const day = String(date.getDate()).padStart(2, "0");
80
+ const hours = String(date.getHours()).padStart(2, "0");
81
+ const minutes = String(date.getMinutes()).padStart(2, "0");
82
+ const seconds = String(date.getSeconds()).padStart(2, "0");
83
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
84
+ }
85
+
86
+ // ============================================================
87
+ // PARSING FUNCTIONS (String → Date object)
88
+ // Use these when you have a date string and need a Date object
89
+ // ============================================================
90
+
91
+ /**
92
+ * Parse date string to Date object
93
+ * @param dateStr - "YYYY-MM-DD" formatted string
94
+ * @returns JavaScript Date object
95
+ *
96
+ * @example
97
+ * parseDate("2025-03-15") // => Date object for March 15, 2025
98
+ */
99
+ export function parseDate(dateStr: string): Date {
100
+ const [year, month, day] = dateStr.split("-").map(Number);
101
+ return new Date(year, month - 1, day);
102
+ }
103
+
104
+ /**
105
+ * Parse datetime string to Date object
106
+ * @param dateTimeStr - "YYYY-MM-DD HH:MM:SS" formatted string
107
+ * @returns JavaScript Date object
108
+ *
109
+ * @example
110
+ * parseDateTime("2025-03-15 10:30:45") // => Date object
111
+ */
112
+ export function parseDateTime(dateTimeStr: string): Date {
113
+ const [datePart, timePart] = dateTimeStr.split(" ");
114
+ const [year, month, day] = datePart.split("-").map(Number);
115
+ const [hours, minutes, seconds] = timePart.split(":").map(Number);
116
+ return new Date(year, month - 1, day, hours, minutes, seconds);
117
+ }
package/sdk/api/index.ts CHANGED
@@ -10,10 +10,13 @@ export type { ResourceClient } from "./client";
10
10
 
11
11
  // DateTime utilities
12
12
  export {
13
- encodeDatetime,
14
- decodeDatetime,
15
- encodeDate,
13
+ DatetimeFormat,
16
14
  decodeDate,
15
+ decodeDateTime,
16
+ formatDate,
17
+ formatDateTime,
18
+ parseDate,
19
+ parseDateTime,
17
20
  } from "./datetime";
18
21
 
19
22
  // Metadata API client
@@ -40,9 +43,6 @@ export type {
40
43
  CreateUpdateResponseType,
41
44
  DeleteResponseType,
42
45
  CountResponseType,
43
- // DateTime types
44
- DateTimeEncodedType,
45
- DateEncodedType,
46
46
  // Metric types
47
47
  MetricTypeType,
48
48
  MetricFieldType,
@@ -58,3 +58,9 @@ export type {
58
58
  // Fields types
59
59
  FieldsResponseType,
60
60
  } from "../types/common";
61
+
62
+ // Re-export date types from base-fields
63
+ export type {
64
+ DateEncodedType,
65
+ DateTimeEncodedType,
66
+ } from "../types/base-fields";
package/sdk/api.ts CHANGED
@@ -14,10 +14,13 @@ export {
14
14
 
15
15
  // DateTime utilities
16
16
  export {
17
- encodeDatetime,
18
- decodeDatetime,
19
- encodeDate,
17
+ formatDateTime,
18
+ decodeDateTime,
19
+ formatDate,
20
20
  decodeDate,
21
+ parseDate,
22
+ parseDateTime,
23
+ DatetimeFormat,
21
24
  } from './api/datetime';
22
25
 
23
26
  // Metadata API client
package/sdk/api.types.ts CHANGED
@@ -9,6 +9,9 @@ export type { ResourceClient } from './api/client';
9
9
  // Metadata types
10
10
  export type { BackendSchema, MetadataItem, FieldMetadata } from './api/metadata';
11
11
 
12
+ // DateTime types from base-fields
13
+ export type { DateTimeEncodedType, DateEncodedType } from './types/base-fields';
14
+
12
15
  // Common API types
13
16
  export type {
14
17
  // Sort types
@@ -32,10 +35,6 @@ export type {
32
35
  DeleteResponseType,
33
36
  CountResponseType,
34
37
 
35
- // DateTime types
36
- DateTimeEncodedType,
37
- DateEncodedType,
38
-
39
38
  // Metric types
40
39
  MetricTypeType,
41
40
  MetricFieldType,
@@ -102,7 +102,8 @@ export function AuthProvider({
102
102
 
103
103
  const status: AuthStatusType = useMemo(() => {
104
104
  if (isLoading || isFetching) return "loading";
105
- if (sessionData?.userDetails) return "authenticated";
105
+ if (Object.keys(sessionData?.userDetails || {}).length > 0)
106
+ return "authenticated";
106
107
  return "unauthenticated";
107
108
  }, [isLoading, isFetching, sessionData]);
108
109
 
@@ -139,11 +140,7 @@ export function AuthProvider({
139
140
  }, [queryError]);
140
141
 
141
142
  useEffect(() => {
142
- if (
143
- status === "unauthenticated" &&
144
- authConfig.autoRedirect &&
145
- !isLoading
146
- ) {
143
+ if (status === "unauthenticated" && authConfig.autoRedirect && !isLoading) {
147
144
  initiateLogin();
148
145
  }
149
146
  }, [status, isLoading, authConfig.autoRedirect]);
@@ -156,7 +153,7 @@ export function AuthProvider({
156
153
  (provider?: AuthProviderNameType, options?: LoginOptionsType) => {
157
154
  initiateLogin(provider, options);
158
155
  },
159
- []
156
+ [],
160
157
  );
161
158
 
162
159
  const logout = useCallback(
@@ -164,36 +161,37 @@ export function AuthProvider({
164
161
  queryClient.removeQueries({ queryKey: SESSION_QUERY_KEY });
165
162
  await performLogout(options);
166
163
  },
167
- [queryClient]
164
+ [queryClient],
168
165
  );
169
166
 
170
- const refreshSession = useCallback(async (): Promise<SessionResponseType | null> => {
171
- // Prevent concurrent refreshes - return existing data if already fetching
172
- if (isFetching) {
173
- return sessionData || null;
174
- }
167
+ const refreshSession =
168
+ useCallback(async (): Promise<SessionResponseType | null> => {
169
+ // Prevent concurrent refreshes - return existing data if already fetching
170
+ if (isFetching) {
171
+ return sessionData || null;
172
+ }
175
173
 
176
- try {
177
- const result = await refetch();
178
- return result.data || null;
179
- } catch (err) {
180
- setError(err as Error);
181
- return null;
182
- }
183
- }, [refetch, isFetching, sessionData]);
174
+ try {
175
+ const result = await refetch();
176
+ return result.data || null;
177
+ } catch (err) {
178
+ setError(err as Error);
179
+ return null;
180
+ }
181
+ }, [refetch, isFetching, sessionData]);
184
182
 
185
183
  const hasRole = useCallback(
186
184
  (role: string): boolean => {
187
185
  return user?.Role === role;
188
186
  },
189
- [user]
187
+ [user],
190
188
  );
191
189
 
192
190
  const hasAnyRole = useCallback(
193
191
  (roles: string[]): boolean => {
194
192
  return roles.includes(user?.Role || "");
195
193
  },
196
- [user]
194
+ [user],
197
195
  );
198
196
 
199
197
  const clearError = useCallback(() => {
@@ -240,7 +238,7 @@ export function AuthProvider({
240
238
  error,
241
239
  clearError,
242
240
  _forceCheck,
243
- ]
241
+ ],
244
242
  );
245
243
 
246
244
  // ============================================================
package/sdk/base-types.ts CHANGED
@@ -25,6 +25,8 @@ export type {
25
25
  CurrencyValueType,
26
26
  JSONFieldType,
27
27
  ReferenceFieldType,
28
+ ExtractReferenceType,
29
+ ExtractFetchFieldType,
28
30
 
29
31
  // JSON types
30
32
  JSONValueType,
@@ -57,18 +57,21 @@ export interface ConditionGroupBuilder {
57
57
 
58
58
  /**
59
59
  * Hook options (minimal configuration)
60
+ * Used for initializing useFilter, and also for initialState in useTable/useKanban
61
+ * @template T - Data type for type-safe field names (defaults to any)
60
62
  */
61
- export interface UseFilterOptionsType {
62
- /** Initial filter conditions */
63
- initialConditions?: Array<ConditionType | ConditionGroupType>;
64
- /** Initial operator for combining conditions (defaults to "And") */
65
- initialOperator?: ConditionGroupOperatorType;
63
+ export interface UseFilterOptionsType<T = any> {
64
+ /** Filter conditions */
65
+ conditions?: Array<ConditionType<T> | ConditionGroupType<T>>;
66
+ /** Operator for combining conditions (defaults to "And") */
67
+ operator?: ConditionGroupOperatorType;
66
68
  }
67
69
 
68
70
  /**
69
71
  * Hook return interface with nested filter support
72
+ * @template T - Data type for type-safe field names (defaults to any)
70
73
  */
71
- export interface UseFilterReturnType {
74
+ export interface UseFilterReturnType<T = any> {
72
75
  // ============================================================
73
76
  // STATE (read-only)
74
77
  // ============================================================
@@ -77,10 +80,10 @@ export interface UseFilterReturnType {
77
80
  operator: ConditionGroupOperatorType;
78
81
 
79
82
  /** Current filter items (with id populated) */
80
- items: Array<ConditionType | ConditionGroupType>;
83
+ items: Array<ConditionType<T> | ConditionGroupType<T>>;
81
84
 
82
85
  /** Ready-to-use API payload (id stripped, undefined if no conditions) */
83
- payload: FilterType | undefined;
86
+ payload: FilterType<T> | undefined;
84
87
 
85
88
  /** Whether any conditions exist */
86
89
  hasConditions: boolean;
@@ -95,7 +98,7 @@ export interface UseFilterReturnType {
95
98
  * @param parentId - Optional id of the parent ConditionGroup. If omitted, adds at root level
96
99
  * @returns The id of the created condition
97
100
  */
98
- addCondition: (condition: Omit<ConditionType, "id">, parentId?: string) => string;
101
+ addCondition: (condition: Omit<ConditionType<T>, "id">, parentId?: string) => string;
99
102
 
100
103
  /**
101
104
  * Add a condition group at root level or to a specific parent group
@@ -114,7 +117,7 @@ export interface UseFilterReturnType {
114
117
  * @param id - The id of the condition to update
115
118
  * @param updates - Partial updates to apply
116
119
  */
117
- updateCondition: (id: string, updates: Partial<Omit<ConditionType, "id">>) => void;
120
+ updateCondition: (id: string, updates: Partial<Omit<ConditionType<T>, "id">>) => void;
118
121
 
119
122
  /**
120
123
  * Update a condition group's operator by id
@@ -138,7 +141,7 @@ export interface UseFilterReturnType {
138
141
  * @param id - The id to look up
139
142
  * @returns The item or undefined if not found
140
143
  */
141
- getCondition: (id: string) => ConditionType | ConditionGroupType | undefined;
144
+ getCondition: (id: string) => ConditionType<T> | ConditionGroupType<T> | undefined;
142
145
 
143
146
  // ============================================================
144
147
  // UTILITY
@@ -16,9 +16,11 @@ let idCounter = 0;
16
16
 
17
17
  /**
18
18
  * Generate a unique ID for conditions and groups
19
+ * Uses timestamp + random component + counter to prevent collisions
19
20
  */
20
21
  const generateId = (): string => {
21
- return `filter_${Date.now()}_${++idCounter}`;
22
+ const random = Math.random().toString(36).substring(2, 9);
23
+ return `filter_${Date.now()}_${random}_${++idCounter}`;
22
24
  };
23
25
 
24
26
  /**
@@ -178,22 +180,22 @@ const addToParent = (
178
180
  // USE FILTER HOOK - Nested Filter Support
179
181
  // ============================================================
180
182
 
181
- export function useFilter(options: UseFilterOptionsType = {}): UseFilterReturnType {
183
+ export function useFilter<T = any>(options: UseFilterOptionsType<T> = {}): UseFilterReturnType<T> {
182
184
  // Initialize items with ids
183
- const [items, setItems] = useState<Array<ConditionType | ConditionGroupType>>(() =>
184
- cloneWithIds(options.initialConditions || [])
185
+ const [items, setItems] = useState<Array<ConditionType<T> | ConditionGroupType<T>>>(() =>
186
+ cloneWithIds(options.conditions || []) as Array<ConditionType<T> | ConditionGroupType<T>>
185
187
  );
186
188
 
187
189
  const [operator, setOperatorState] = useState<ConditionGroupOperatorType>(
188
- options.initialOperator || "And"
190
+ options.operator || "And"
189
191
  );
190
192
 
191
193
  // Build payload for API (strip ids)
192
- const payload = useMemo((): FilterType | undefined => {
194
+ const payload = useMemo((): FilterType<T> | undefined => {
193
195
  if (items.length === 0) return undefined;
194
196
  return {
195
197
  Operator: operator,
196
- Condition: stripIds(items),
198
+ Condition: stripIds(items) as Array<ConditionType<T> | ConditionGroupType<T>>,
197
199
  };
198
200
  }, [items, operator]);
199
201
 
@@ -204,11 +206,11 @@ export function useFilter(options: UseFilterOptionsType = {}): UseFilterReturnTy
204
206
  // ============================================================
205
207
 
206
208
  const addCondition = useCallback(
207
- (condition: Omit<ConditionType, "id">, parentId?: string): string => {
209
+ (condition: Omit<ConditionType<T>, "id">, parentId?: string): string => {
208
210
  const id = generateId();
209
- const newCondition: ConditionType = { ...condition, id };
211
+ const newCondition = { ...condition, id } as ConditionType<T>;
210
212
  if (parentId) {
211
- setItems((prev) => addToParent(prev, parentId, newCondition));
213
+ setItems((prev) => addToParent(prev, parentId, newCondition) as Array<ConditionType<T> | ConditionGroupType<T>>);
212
214
  } else {
213
215
  setItems((prev) => [...prev, newCondition]);
214
216
  }
@@ -220,13 +222,13 @@ export function useFilter(options: UseFilterOptionsType = {}): UseFilterReturnTy
220
222
  const addConditionGroup = useCallback(
221
223
  (groupOperator: ConditionGroupOperatorType, parentId?: string): string => {
222
224
  const id = generateId();
223
- const newGroup: ConditionGroupType = {
225
+ const newGroup: ConditionGroupType<T> = {
224
226
  id,
225
227
  Operator: groupOperator,
226
228
  Condition: [],
227
229
  };
228
230
  if (parentId) {
229
- setItems((prev) => addToParent(prev, parentId, newGroup));
231
+ setItems((prev) => addToParent(prev, parentId, newGroup) as Array<ConditionType<T> | ConditionGroupType<T>>);
230
232
  } else {
231
233
  setItems((prev) => [...prev, newGroup]);
232
234
  }
@@ -240,14 +242,14 @@ export function useFilter(options: UseFilterOptionsType = {}): UseFilterReturnTy
240
242
  // ============================================================
241
243
 
242
244
  const updateCondition = useCallback(
243
- (id: string, updates: Partial<Omit<ConditionType, "id">>): void => {
245
+ (id: string, updates: Partial<Omit<ConditionType<T>, "id">>): void => {
244
246
  setItems((prev) =>
245
247
  updateInTree(prev, id, (item) => {
246
248
  if (!isConditionGroup(item)) {
247
249
  return { ...item, ...updates };
248
250
  }
249
251
  return item;
250
- })
252
+ }) as Array<ConditionType<T> | ConditionGroupType<T>>
251
253
  );
252
254
  },
253
255
  []
@@ -261,7 +263,7 @@ export function useFilter(options: UseFilterOptionsType = {}): UseFilterReturnTy
261
263
  return { ...item, Operator: newOperator };
262
264
  }
263
265
  return item;
264
- })
266
+ }) as Array<ConditionType<T> | ConditionGroupType<T>>
265
267
  );
266
268
  },
267
269
  []
@@ -272,12 +274,12 @@ export function useFilter(options: UseFilterOptionsType = {}): UseFilterReturnTy
272
274
  // ============================================================
273
275
 
274
276
  const removeCondition = useCallback((id: string): void => {
275
- setItems((prev) => removeFromTree(prev, id));
277
+ setItems((prev) => removeFromTree(prev, id) as Array<ConditionType<T> | ConditionGroupType<T>>);
276
278
  }, []);
277
279
 
278
280
  const getCondition = useCallback(
279
- (id: string): ConditionType | ConditionGroupType | undefined => {
280
- return findById(items, id);
281
+ (id: string): ConditionType<T> | ConditionGroupType<T> | undefined => {
282
+ return findById(items, id) as ConditionType<T> | ConditionGroupType<T> | undefined;
281
283
  },
282
284
  [items]
283
285
  );