@ram_28/kf-ai-sdk 1.0.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/LICENSE +21 -0
- package/README.md +840 -0
- package/dist/api/client.d.ts +78 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/datetime.d.ts +21 -0
- package/dist/api/datetime.d.ts.map +1 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/metadata.d.ts +75 -0
- package/dist/api/metadata.d.ts.map +1 -0
- package/dist/components/hooks/index.d.ts +8 -0
- package/dist/components/hooks/index.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/index.d.ts +5 -0
- package/dist/components/hooks/useFilter/index.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts +33 -0
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/types.d.ts +137 -0
- package/dist/components/hooks/useFilter/types.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/useFilter.d.ts +3 -0
- package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/validation.utils.d.ts +38 -0
- package/dist/components/hooks/useFilter/validation.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/apiClient.d.ts +71 -0
- package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -0
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts +28 -0
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/index.d.ts +6 -0
- package/dist/components/hooks/useForm/index.d.ts.map +1 -0
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts +88 -0
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts +28 -0
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts +29 -0
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/types.d.ts +412 -0
- package/dist/components/hooks/useForm/types.d.ts.map +1 -0
- package/dist/components/hooks/useForm/useForm.d.ts +3 -0
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/apiClient.d.ts +99 -0
- package/dist/components/hooks/useKanban/apiClient.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/context.d.ts +4 -0
- package/dist/components/hooks/useKanban/context.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/dragDropManager.d.ts +27 -0
- package/dist/components/hooks/useKanban/dragDropManager.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/index.d.ts +6 -0
- package/dist/components/hooks/useKanban/index.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/types.d.ts +438 -0
- package/dist/components/hooks/useKanban/types.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/useKanban.d.ts +3 -0
- package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/useKanbanSimple.d.ts +62 -0
- package/dist/components/hooks/useKanban/useKanbanSimple.d.ts.map +1 -0
- package/dist/components/hooks/useTable/index.d.ts +3 -0
- package/dist/components/hooks/useTable/index.d.ts.map +1 -0
- package/dist/components/hooks/useTable/types.d.ts +107 -0
- package/dist/components/hooks/useTable/types.d.ts.map +1 -0
- package/dist/components/hooks/useTable/useTable.d.ts +8 -0
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/kanban/Kanban.d.ts +12 -0
- package/dist/components/ui/kanban/Kanban.d.ts.map +1 -0
- package/dist/components/ui/kanban/index.d.ts +2 -0
- package/dist/components/ui/kanban/index.d.ts.map +1 -0
- package/dist/index.cjs +45 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +6522 -0
- package/dist/types/base-fields.d.ts +182 -0
- package/dist/types/base-fields.d.ts.map +1 -0
- package/dist/types/common.d.ts +238 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/cn.d.ts +7 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/formatting.d.ts +52 -0
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/package.json +98 -0
- package/sdk/api/client.ts +447 -0
- package/sdk/api/datetime.ts +33 -0
- package/sdk/api/index.ts +61 -0
- package/sdk/api/metadata.ts +148 -0
- package/sdk/components/hooks/index.ts +34 -0
- package/sdk/components/hooks/useFilter/index.ts +37 -0
- package/sdk/components/hooks/useFilter/payloadBuilder.utils.ts +298 -0
- package/sdk/components/hooks/useFilter/types.ts +158 -0
- package/sdk/components/hooks/useFilter/useFilter.llm.txt +497 -0
- package/sdk/components/hooks/useFilter/useFilter.ts +494 -0
- package/sdk/components/hooks/useFilter/validation.utils.ts +401 -0
- package/sdk/components/hooks/useForm/apiClient.ts +441 -0
- package/sdk/components/hooks/useForm/expressionValidator.utils.ts +444 -0
- package/sdk/components/hooks/useForm/index.ts +64 -0
- package/sdk/components/hooks/useForm/optimizedExpressionValidator.utils.ts +482 -0
- package/sdk/components/hooks/useForm/ruleClassifier.utils.ts +424 -0
- package/sdk/components/hooks/useForm/schemaParser.utils.ts +519 -0
- package/sdk/components/hooks/useForm/types.ts +630 -0
- package/sdk/components/hooks/useForm/useForm.llm.txt +340 -0
- package/sdk/components/hooks/useForm/useForm.ts +821 -0
- package/sdk/components/hooks/useKanban/apiClient.ts +494 -0
- package/sdk/components/hooks/useKanban/context.ts +14 -0
- package/sdk/components/hooks/useKanban/dragDropManager.ts +529 -0
- package/sdk/components/hooks/useKanban/index.ts +63 -0
- package/sdk/components/hooks/useKanban/types.ts +606 -0
- package/sdk/components/hooks/useKanban/useKanban.llm.txt +482 -0
- package/sdk/components/hooks/useKanban/useKanban.ts +725 -0
- package/sdk/components/hooks/useKanban/useKanbanSimple.ts +389 -0
- package/sdk/components/hooks/useTable/index.ts +5 -0
- package/sdk/components/hooks/useTable/types.ts +154 -0
- package/sdk/components/hooks/useTable/useTable.llm.txt +344 -0
- package/sdk/components/hooks/useTable/useTable.ts +413 -0
- package/sdk/components/index.ts +15 -0
- package/sdk/components/ui/index.ts +2 -0
- package/sdk/components/ui/kanban/Kanban.tsx +134 -0
- package/sdk/components/ui/kanban/index.ts +11 -0
- package/sdk/index.ts +13 -0
- package/sdk/types/base-fields.ts +221 -0
- package/sdk/types/common.ts +306 -0
- package/sdk/types/index.ts +5 -0
- package/sdk/utils/cn.ts +10 -0
- package/sdk/utils/formatting.ts +212 -0
- package/sdk/utils/index.ts +5 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// USE KANBAN HOOK - Simplified Implementation
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Clean hook following useTable pattern for API integration
|
|
5
|
+
|
|
6
|
+
import { useState, useMemo, useCallback, useRef } from "react";
|
|
7
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
8
|
+
import { api } from "../../../api";
|
|
9
|
+
import type { ListOptions, ListResponse } from "../../../types/common";
|
|
10
|
+
|
|
11
|
+
// ============================================================
|
|
12
|
+
// TYPE DEFINITIONS
|
|
13
|
+
// ============================================================
|
|
14
|
+
|
|
15
|
+
export interface KanbanCard<T = Record<string, any>> {
|
|
16
|
+
_id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
columnId: string;
|
|
20
|
+
position: number;
|
|
21
|
+
data?: T; // Custom fields
|
|
22
|
+
_created_at?: Date;
|
|
23
|
+
_modified_at?: Date;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface KanbanColumn<T = Record<string, any>> {
|
|
27
|
+
_id: string;
|
|
28
|
+
title: string;
|
|
29
|
+
position: number;
|
|
30
|
+
color?: string;
|
|
31
|
+
cards: KanbanCard<T>[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ColumnDefinition {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
position: number;
|
|
38
|
+
color?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface UseKanbanOptions<T = Record<string, any>> {
|
|
42
|
+
/** Data source identifier for API calls */
|
|
43
|
+
source: string;
|
|
44
|
+
/** Column configurations */
|
|
45
|
+
columns: ColumnDefinition[];
|
|
46
|
+
/** Enable drag and drop functionality */
|
|
47
|
+
enableDragDrop?: boolean;
|
|
48
|
+
/** Initial search query */
|
|
49
|
+
initialSearch?: string;
|
|
50
|
+
/** Error callback */
|
|
51
|
+
onError?: (error: Error) => void;
|
|
52
|
+
/** Custom card data type validator (optional) */
|
|
53
|
+
validateCardData?: (data: T) => boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface UseKanbanReturn<T> {
|
|
57
|
+
// Data
|
|
58
|
+
columns: KanbanColumn<T>[];
|
|
59
|
+
totalCards: number;
|
|
60
|
+
|
|
61
|
+
// Loading States
|
|
62
|
+
isLoading: boolean;
|
|
63
|
+
isFetching: boolean;
|
|
64
|
+
isUpdating: boolean;
|
|
65
|
+
|
|
66
|
+
// Error Handling
|
|
67
|
+
error: Error | null;
|
|
68
|
+
|
|
69
|
+
// Search
|
|
70
|
+
searchQuery: string;
|
|
71
|
+
setSearchQuery: (query: string) => void;
|
|
72
|
+
clearSearch: () => void;
|
|
73
|
+
|
|
74
|
+
// Card Operations
|
|
75
|
+
createCard: (card: Partial<KanbanCard<T>> & { columnId: string }) => Promise<void>;
|
|
76
|
+
updateCard: (id: string, updates: Partial<KanbanCard<T>>) => Promise<void>;
|
|
77
|
+
deleteCard: (id: string) => Promise<void>;
|
|
78
|
+
moveCard: (cardId: string, toColumnId: string, position?: number) => Promise<void>;
|
|
79
|
+
|
|
80
|
+
// Drag Drop State (if enabled)
|
|
81
|
+
isDragging: boolean;
|
|
82
|
+
draggedCard: KanbanCard<T> | null;
|
|
83
|
+
handleDragStart: (card: KanbanCard<T>) => void;
|
|
84
|
+
handleDragEnd: () => void;
|
|
85
|
+
handleKeyboardMove: (cardId: string, direction: "left" | "right") => Promise<void>;
|
|
86
|
+
|
|
87
|
+
// Utilities
|
|
88
|
+
refresh: () => Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================
|
|
92
|
+
// MAIN HOOK IMPLEMENTATION
|
|
93
|
+
// ============================================================
|
|
94
|
+
|
|
95
|
+
export function useKanban<T extends Record<string, any> = Record<string, any>>(
|
|
96
|
+
options: UseKanbanOptions<T>
|
|
97
|
+
): UseKanbanReturn<T> {
|
|
98
|
+
const {
|
|
99
|
+
source,
|
|
100
|
+
columns: columnConfigs,
|
|
101
|
+
enableDragDrop = false,
|
|
102
|
+
initialSearch = "",
|
|
103
|
+
onError,
|
|
104
|
+
} = options;
|
|
105
|
+
|
|
106
|
+
// ============================================================
|
|
107
|
+
// STATE MANAGEMENT
|
|
108
|
+
// ============================================================
|
|
109
|
+
|
|
110
|
+
const [searchQuery, setSearchQuery] = useState(initialSearch);
|
|
111
|
+
const [dragState, setDragState] = useState({
|
|
112
|
+
isDragging: false,
|
|
113
|
+
draggedCard: null as KanbanCard<T> | null,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Query client for cache management
|
|
117
|
+
const queryClient = useQueryClient();
|
|
118
|
+
const onErrorRef = useRef(onError);
|
|
119
|
+
|
|
120
|
+
// ============================================================
|
|
121
|
+
// API OPTIONS
|
|
122
|
+
// ============================================================
|
|
123
|
+
|
|
124
|
+
const apiOptions = useMemo((): ListOptions => {
|
|
125
|
+
const opts: ListOptions = {};
|
|
126
|
+
|
|
127
|
+
// Default sorting by column and position - using correct API format
|
|
128
|
+
opts.Sort = [
|
|
129
|
+
{ columnId: "ASC" },
|
|
130
|
+
{ position: "ASC" },
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
// Add search query
|
|
134
|
+
if (searchQuery) {
|
|
135
|
+
opts.Search = searchQuery;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return opts;
|
|
139
|
+
}, [searchQuery]);
|
|
140
|
+
|
|
141
|
+
// ============================================================
|
|
142
|
+
// DATA FETCHING
|
|
143
|
+
// ============================================================
|
|
144
|
+
|
|
145
|
+
// Fetch cards
|
|
146
|
+
const {
|
|
147
|
+
data: cardsResponse,
|
|
148
|
+
isLoading: isLoadingCards,
|
|
149
|
+
isFetching: isFetchingCards,
|
|
150
|
+
error: cardsError,
|
|
151
|
+
refetch: refetchCards,
|
|
152
|
+
} = useQuery({
|
|
153
|
+
queryKey: ["kanban-cards", source, apiOptions],
|
|
154
|
+
queryFn: async (): Promise<ListResponse<KanbanCard<T>>> => {
|
|
155
|
+
try {
|
|
156
|
+
return await api<KanbanCard<T>>(source).list(apiOptions);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
onErrorRef.current?.(err as Error);
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
staleTime: 30 * 1000,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Get total card count
|
|
166
|
+
const {
|
|
167
|
+
data: countData,
|
|
168
|
+
isLoading: isLoadingCount,
|
|
169
|
+
error: countError,
|
|
170
|
+
} = useQuery({
|
|
171
|
+
queryKey: ["kanban-count", source, apiOptions],
|
|
172
|
+
queryFn: async () => {
|
|
173
|
+
try {
|
|
174
|
+
return await api<KanbanCard<T>>(source).count(apiOptions);
|
|
175
|
+
} catch (err) {
|
|
176
|
+
onErrorRef.current?.(err as Error);
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
staleTime: 30 * 1000,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ============================================================
|
|
184
|
+
// CARD MUTATIONS
|
|
185
|
+
// ============================================================
|
|
186
|
+
|
|
187
|
+
const createCardMutation = useMutation({
|
|
188
|
+
mutationFn: async (card: Partial<KanbanCard<T>> & { columnId: string }) => {
|
|
189
|
+
// Calculate position
|
|
190
|
+
const currentCards = cardsResponse?.Data?.filter((c) => c.columnId === card.columnId) || [];
|
|
191
|
+
const position = card.position ?? currentCards.length;
|
|
192
|
+
|
|
193
|
+
return await api<KanbanCard<T>>(source).create({
|
|
194
|
+
...card,
|
|
195
|
+
position,
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
onSuccess: () => refetchCards(),
|
|
199
|
+
onError: (error) => onErrorRef.current?.(error as Error),
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const updateCardMutation = useMutation({
|
|
203
|
+
mutationFn: async ({ id, updates }: { id: string; updates: Partial<KanbanCard<T>> }) => {
|
|
204
|
+
return await api<KanbanCard<T>>(source).update(id, updates);
|
|
205
|
+
},
|
|
206
|
+
onSuccess: () => refetchCards(),
|
|
207
|
+
onError: (error) => onErrorRef.current?.(error as Error),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const deleteCardMutation = useMutation({
|
|
211
|
+
mutationFn: async (id: string) => {
|
|
212
|
+
return await api(source).delete(id);
|
|
213
|
+
},
|
|
214
|
+
onSuccess: () => refetchCards(),
|
|
215
|
+
onError: (error) => onErrorRef.current?.(error as Error),
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const moveCardMutation = useMutation({
|
|
219
|
+
mutationFn: async ({ cardId, toColumnId, position }: {
|
|
220
|
+
cardId: string;
|
|
221
|
+
toColumnId: string;
|
|
222
|
+
position?: number;
|
|
223
|
+
}) => {
|
|
224
|
+
const updates: any = { columnId: toColumnId };
|
|
225
|
+
if (position !== undefined) {
|
|
226
|
+
updates.position = position;
|
|
227
|
+
}
|
|
228
|
+
return await api<KanbanCard<T>>(source).update(cardId, updates);
|
|
229
|
+
},
|
|
230
|
+
onSuccess: () => refetchCards(),
|
|
231
|
+
onError: (error) => onErrorRef.current?.(error as Error),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// ============================================================
|
|
235
|
+
// OPERATIONS
|
|
236
|
+
// ============================================================
|
|
237
|
+
|
|
238
|
+
const clearSearch = useCallback(() => {
|
|
239
|
+
setSearchQuery("");
|
|
240
|
+
}, []);
|
|
241
|
+
|
|
242
|
+
const handleDragStart = useCallback((card: KanbanCard<T>) => {
|
|
243
|
+
if (!enableDragDrop) return;
|
|
244
|
+
setDragState({
|
|
245
|
+
isDragging: true,
|
|
246
|
+
draggedCard: card,
|
|
247
|
+
});
|
|
248
|
+
}, [enableDragDrop]);
|
|
249
|
+
|
|
250
|
+
const handleDragEnd = useCallback(() => {
|
|
251
|
+
setDragState({
|
|
252
|
+
isDragging: false,
|
|
253
|
+
draggedCard: null,
|
|
254
|
+
});
|
|
255
|
+
}, []);
|
|
256
|
+
|
|
257
|
+
const handleKeyboardMove = useCallback(async (
|
|
258
|
+
cardId: string,
|
|
259
|
+
direction: "left" | "right"
|
|
260
|
+
) => {
|
|
261
|
+
if (!enableDragDrop) return;
|
|
262
|
+
|
|
263
|
+
const card = cardsResponse?.Data?.find(c => c._id === cardId);
|
|
264
|
+
if (!card) return;
|
|
265
|
+
|
|
266
|
+
const currentColumnIndex = columnConfigs.findIndex(col => col.id === card.columnId);
|
|
267
|
+
if (currentColumnIndex === -1) return;
|
|
268
|
+
|
|
269
|
+
let targetColumnIndex = currentColumnIndex;
|
|
270
|
+
|
|
271
|
+
if (direction === "left") {
|
|
272
|
+
targetColumnIndex = Math.max(0, currentColumnIndex - 1);
|
|
273
|
+
} else if (direction === "right") {
|
|
274
|
+
targetColumnIndex = Math.min(columnConfigs.length - 1, currentColumnIndex + 1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (targetColumnIndex !== currentColumnIndex) {
|
|
278
|
+
const targetColumn = columnConfigs[targetColumnIndex];
|
|
279
|
+
await moveCardMutation.mutateAsync({
|
|
280
|
+
cardId,
|
|
281
|
+
toColumnId: targetColumn.id,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}, [enableDragDrop, cardsResponse?.Data, columnConfigs, moveCardMutation]);
|
|
285
|
+
|
|
286
|
+
// ============================================================
|
|
287
|
+
// COMPUTED VALUES
|
|
288
|
+
// ============================================================
|
|
289
|
+
|
|
290
|
+
const processedColumns = useMemo(() => {
|
|
291
|
+
const cards = cardsResponse?.Data || [];
|
|
292
|
+
|
|
293
|
+
// Group cards by columnId
|
|
294
|
+
const cardsByColumn = cards.reduce(
|
|
295
|
+
(acc, card) => {
|
|
296
|
+
if (!acc[card.columnId]) {
|
|
297
|
+
acc[card.columnId] = [];
|
|
298
|
+
}
|
|
299
|
+
acc[card.columnId].push(card);
|
|
300
|
+
return acc;
|
|
301
|
+
},
|
|
302
|
+
{} as Record<string, KanbanCard<T>[]>
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// Map column configs to columns with cards
|
|
306
|
+
return columnConfigs
|
|
307
|
+
.sort((a, b) => a.position - b.position)
|
|
308
|
+
.map((config) => ({
|
|
309
|
+
_id: config.id,
|
|
310
|
+
title: config.title,
|
|
311
|
+
position: config.position,
|
|
312
|
+
color: config.color,
|
|
313
|
+
cards: (cardsByColumn[config.id] || []).sort(
|
|
314
|
+
(a, b) => a.position - b.position
|
|
315
|
+
),
|
|
316
|
+
}));
|
|
317
|
+
}, [columnConfigs, cardsResponse]);
|
|
318
|
+
|
|
319
|
+
const refresh = useCallback(async () => {
|
|
320
|
+
await queryClient.invalidateQueries({
|
|
321
|
+
predicate: (query) =>
|
|
322
|
+
query.queryKey[0] === "kanban-cards" ||
|
|
323
|
+
query.queryKey[0] === "kanban-count",
|
|
324
|
+
});
|
|
325
|
+
}, [queryClient]);
|
|
326
|
+
|
|
327
|
+
// ============================================================
|
|
328
|
+
// RETURN VALUES
|
|
329
|
+
// ============================================================
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
// Data
|
|
333
|
+
columns: processedColumns,
|
|
334
|
+
totalCards: countData?.Count || 0,
|
|
335
|
+
|
|
336
|
+
// Loading States
|
|
337
|
+
isLoading: isLoadingCards || isLoadingCount,
|
|
338
|
+
isFetching: isFetchingCards,
|
|
339
|
+
isUpdating:
|
|
340
|
+
createCardMutation.isPending ||
|
|
341
|
+
updateCardMutation.isPending ||
|
|
342
|
+
deleteCardMutation.isPending ||
|
|
343
|
+
moveCardMutation.isPending,
|
|
344
|
+
|
|
345
|
+
// Error Handling
|
|
346
|
+
error: (cardsError || countError) as Error | null,
|
|
347
|
+
|
|
348
|
+
// Search
|
|
349
|
+
searchQuery,
|
|
350
|
+
setSearchQuery,
|
|
351
|
+
clearSearch,
|
|
352
|
+
|
|
353
|
+
// Card Operations
|
|
354
|
+
createCard: useCallback(
|
|
355
|
+
async (card: Partial<KanbanCard<T>> & { columnId: string }) => {
|
|
356
|
+
await createCardMutation.mutateAsync(card);
|
|
357
|
+
},
|
|
358
|
+
[createCardMutation]
|
|
359
|
+
),
|
|
360
|
+
updateCard: useCallback(
|
|
361
|
+
async (id: string, updates: Partial<KanbanCard<T>>) => {
|
|
362
|
+
await updateCardMutation.mutateAsync({ id, updates });
|
|
363
|
+
},
|
|
364
|
+
[updateCardMutation]
|
|
365
|
+
),
|
|
366
|
+
deleteCard: useCallback(
|
|
367
|
+
async (id: string) => {
|
|
368
|
+
await deleteCardMutation.mutateAsync(id);
|
|
369
|
+
},
|
|
370
|
+
[deleteCardMutation]
|
|
371
|
+
),
|
|
372
|
+
moveCard: useCallback(
|
|
373
|
+
async (cardId: string, toColumnId: string, position?: number) => {
|
|
374
|
+
await moveCardMutation.mutateAsync({ cardId, toColumnId, position });
|
|
375
|
+
},
|
|
376
|
+
[moveCardMutation]
|
|
377
|
+
),
|
|
378
|
+
|
|
379
|
+
// Drag Drop
|
|
380
|
+
isDragging: dragState.isDragging,
|
|
381
|
+
draggedCard: dragState.draggedCard,
|
|
382
|
+
handleDragStart,
|
|
383
|
+
handleDragEnd,
|
|
384
|
+
handleKeyboardMove,
|
|
385
|
+
|
|
386
|
+
// Utilities
|
|
387
|
+
refresh,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { ListResponse, LogicalOperator } from "../../../types/common";
|
|
2
|
+
import type {
|
|
3
|
+
FilterConditionWithId,
|
|
4
|
+
TypedFilterConditionInput,
|
|
5
|
+
ValidationError,
|
|
6
|
+
FieldDefinition,
|
|
7
|
+
} from "../useFilter";
|
|
8
|
+
|
|
9
|
+
// ============================================================
|
|
10
|
+
// TYPE DEFINITIONS
|
|
11
|
+
// ============================================================
|
|
12
|
+
|
|
13
|
+
export interface ColumnDefinition<T> {
|
|
14
|
+
/** Field name from the data type */
|
|
15
|
+
fieldId: keyof T;
|
|
16
|
+
/** Display label (optional, defaults to fieldId) */
|
|
17
|
+
label?: string;
|
|
18
|
+
/** Enable sorting for this column */
|
|
19
|
+
enableSorting?: boolean;
|
|
20
|
+
/** Enable filtering for this column */
|
|
21
|
+
enableFiltering?: boolean;
|
|
22
|
+
/** Custom transform function (overrides auto-formatting) */
|
|
23
|
+
transform?: (value: any, row: T) => React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface UseTableOptions<T> {
|
|
27
|
+
/** Data source identifier */
|
|
28
|
+
source: string;
|
|
29
|
+
/** Column configurations */
|
|
30
|
+
columns: ColumnDefinition<T>[];
|
|
31
|
+
/** Enable sorting functionality */
|
|
32
|
+
enableSorting?: boolean;
|
|
33
|
+
/** Enable filtering functionality */
|
|
34
|
+
enableFiltering?: boolean;
|
|
35
|
+
/** Enable pagination */
|
|
36
|
+
enablePagination?: boolean;
|
|
37
|
+
/** Field definitions for filter validation */
|
|
38
|
+
fieldDefinitions?: Record<keyof T, FieldDefinition>;
|
|
39
|
+
/** Initial state */
|
|
40
|
+
initialState?: {
|
|
41
|
+
pagination?: {
|
|
42
|
+
pageNo: number; // 1-indexed page number
|
|
43
|
+
pageSize: number;
|
|
44
|
+
};
|
|
45
|
+
sorting?: {
|
|
46
|
+
field: keyof T;
|
|
47
|
+
direction: "asc" | "desc";
|
|
48
|
+
};
|
|
49
|
+
globalFilter?: string;
|
|
50
|
+
filters?: FilterConditionWithId[];
|
|
51
|
+
filterOperator?: LogicalOperator;
|
|
52
|
+
};
|
|
53
|
+
/** Error callback */
|
|
54
|
+
onError?: (error: Error) => void;
|
|
55
|
+
/** Success callback */
|
|
56
|
+
onSuccess?: (data: T[]) => void;
|
|
57
|
+
/** Filter error callback */
|
|
58
|
+
onFilterError?: (errors: ValidationError[]) => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface UseTableReturn<T> {
|
|
62
|
+
// Data
|
|
63
|
+
rows: T[];
|
|
64
|
+
totalItems: number;
|
|
65
|
+
|
|
66
|
+
// Loading States
|
|
67
|
+
isLoading: boolean;
|
|
68
|
+
isFetching: boolean;
|
|
69
|
+
|
|
70
|
+
// Error Handling
|
|
71
|
+
error: Error | null;
|
|
72
|
+
|
|
73
|
+
// Search (Flat Access)
|
|
74
|
+
search: {
|
|
75
|
+
query: string;
|
|
76
|
+
setQuery: (value: string) => void;
|
|
77
|
+
clear: () => void;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Sorting (Flat Access)
|
|
81
|
+
sort: {
|
|
82
|
+
field: keyof T | null;
|
|
83
|
+
direction: "asc" | "desc" | null;
|
|
84
|
+
toggle: (field: keyof T) => void;
|
|
85
|
+
clear: () => void;
|
|
86
|
+
set: (field: keyof T, direction: "asc" | "desc") => void;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Legacy Global Filtering (Flat Access)
|
|
90
|
+
globalFilter: {
|
|
91
|
+
value: string;
|
|
92
|
+
setValue: (value: string) => void;
|
|
93
|
+
clear: () => void;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Advanced Filtering (Filter Conditions) - Type-safe: lhsField constrained to keyof T
|
|
97
|
+
filter: {
|
|
98
|
+
// State
|
|
99
|
+
conditions: FilterConditionWithId[];
|
|
100
|
+
logicalOperator: LogicalOperator;
|
|
101
|
+
isValid: boolean;
|
|
102
|
+
validationErrors: ValidationError[];
|
|
103
|
+
hasConditions: boolean;
|
|
104
|
+
|
|
105
|
+
// Condition Management (type-safe)
|
|
106
|
+
addCondition: (condition: TypedFilterConditionInput<T>) => string;
|
|
107
|
+
updateCondition: (
|
|
108
|
+
id: string,
|
|
109
|
+
updates: Partial<TypedFilterConditionInput<T>>
|
|
110
|
+
) => boolean;
|
|
111
|
+
removeCondition: (id: string) => boolean;
|
|
112
|
+
clearConditions: () => void;
|
|
113
|
+
getCondition: (id: string) => FilterConditionWithId | undefined;
|
|
114
|
+
|
|
115
|
+
// Logical Operator
|
|
116
|
+
setLogicalOperator: (operator: LogicalOperator) => void;
|
|
117
|
+
|
|
118
|
+
// Bulk Operations
|
|
119
|
+
setConditions: (conditions: FilterConditionWithId[]) => void;
|
|
120
|
+
replaceCondition: (
|
|
121
|
+
id: string,
|
|
122
|
+
newCondition: TypedFilterConditionInput<T>
|
|
123
|
+
) => boolean;
|
|
124
|
+
|
|
125
|
+
// Validation
|
|
126
|
+
validateCondition: (condition: Partial<FilterConditionWithId>) => any;
|
|
127
|
+
validateAllConditions: () => any;
|
|
128
|
+
|
|
129
|
+
// State Management
|
|
130
|
+
exportState: () => any;
|
|
131
|
+
importState: (state: any) => void;
|
|
132
|
+
resetToInitial: () => void;
|
|
133
|
+
|
|
134
|
+
// Utilities
|
|
135
|
+
getConditionCount: () => number;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Pagination (Flat Access)
|
|
139
|
+
pagination: {
|
|
140
|
+
currentPage: number;
|
|
141
|
+
pageSize: number;
|
|
142
|
+
totalPages: number;
|
|
143
|
+
totalItems: number;
|
|
144
|
+
canGoNext: boolean;
|
|
145
|
+
canGoPrevious: boolean;
|
|
146
|
+
goToNext: () => void;
|
|
147
|
+
goToPrevious: () => void;
|
|
148
|
+
goToPage: (page: number) => void;
|
|
149
|
+
setPageSize: (size: number) => void;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Operations
|
|
153
|
+
refetch: () => Promise<ListResponse<T>>;
|
|
154
|
+
}
|