@bit.rhplus/ui.grid-layout 0.0.2 → 0.0.4
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/ColumnEditorModal.jsx +426 -369
- package/dist/ColumnEditorModal.js +134 -88
- package/dist/ColumnEditorModal.js.map +1 -1
- package/dist/useGridLayout.d.ts +1 -1
- package/dist/useGridLayout.js +1115 -221
- package/dist/useGridLayout.js.map +1 -1
- package/dist/useGridLayoutApi.d.ts +1 -1
- package/dist/useGridLayoutApi.js +102 -65
- package/dist/useGridLayoutApi.js.map +1 -1
- package/gridLayout.js +106 -106
- package/package.json +4 -4
- package/useGridLayout.js +1714 -625
- package/useGridLayoutApi.js +382 -296
- /package/dist/{preview-1755027648540.js → preview-1755777309104.js} +0 -0
package/dist/useGridLayout.js
CHANGED
|
@@ -25,10 +25,12 @@ import { debounce } from 'lodash';
|
|
|
25
25
|
* @returns {Object} Grid layout management interface
|
|
26
26
|
*/
|
|
27
27
|
export const useGridLayout = ({ userKey, applicationName, gridName, filterName, enabled = true, autoSave = true, autoSaveDelay = 2000, columnDefs = [], accessToken, waitForSavedFields = false, // Nový prop pro odložené zobrazení columnDefs
|
|
28
|
-
onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
28
|
+
onLayoutLoaded, onLayoutSaved, onError, }) => {
|
|
29
29
|
// Validace columnDefs - musí být array
|
|
30
|
-
if (columnDefs !== undefined &&
|
|
31
|
-
|
|
30
|
+
if (columnDefs !== undefined &&
|
|
31
|
+
columnDefs !== null &&
|
|
32
|
+
!Array.isArray(columnDefs)) {
|
|
33
|
+
console.error('[GridLayout] columnDefs is not an array:', typeof columnDefs, columnDefs);
|
|
32
34
|
throw new Error('useGridLayout: columnDefs musí být array');
|
|
33
35
|
}
|
|
34
36
|
// Refs pro AG-Grid API
|
|
@@ -36,142 +38,601 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
36
38
|
const columnApiRef = useRef(null);
|
|
37
39
|
// State
|
|
38
40
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
41
|
+
const [isGridReady, setIsGridReady] = useState(false);
|
|
39
42
|
const [isLoading, setIsLoading] = useState(false);
|
|
40
43
|
const [isSaving, setIsSaving] = useState(false);
|
|
41
44
|
const [error, setError] = useState(null);
|
|
42
45
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
43
46
|
const [isColumnEditorOpen, setIsColumnEditorOpen] = useState(false);
|
|
47
|
+
const [lastKnownColumnState, setLastKnownColumnState] = useState(null);
|
|
48
|
+
const [columnDefsVersion, setColumnDefsVersion] = useState(0); // Pro tracking změn v stableColumnDefsRef
|
|
44
49
|
// Grid Layout API hook
|
|
45
50
|
const gridLayoutApi = useGridLayoutApi({
|
|
46
51
|
userKey,
|
|
47
52
|
applicationName,
|
|
48
53
|
gridName,
|
|
49
54
|
filterName,
|
|
50
|
-
accessToken
|
|
55
|
+
accessToken,
|
|
51
56
|
});
|
|
52
57
|
// Query pro načtení saved layoutu
|
|
53
|
-
const { data: savedFields, isLoading: isFieldsLoading, error: fieldsError, refetch: refetchFields } = gridLayoutApi.useUserFields((columnDefs || []).map((colDef, index) => ({
|
|
54
|
-
name: colDef.field || `column_${index}`,
|
|
55
|
-
displayName: colDef.headerName ||
|
|
58
|
+
const { data: savedFields, isLoading: isFieldsLoading, error: fieldsError, refetch: refetchFields, } = gridLayoutApi.useUserFields((columnDefs || []).map((colDef, index) => ({
|
|
59
|
+
name: colDef.field || colDef.colId || `column_${index}`,
|
|
60
|
+
displayName: colDef.headerName ||
|
|
61
|
+
colDef.field ||
|
|
62
|
+
colDef.colId ||
|
|
63
|
+
`Column ${index + 1}`,
|
|
56
64
|
dataType: colDef.type || 'string',
|
|
57
65
|
isVisible: !colDef.hide,
|
|
58
66
|
width: colDef.width || 100,
|
|
59
|
-
order: index
|
|
60
|
-
})), {
|
|
67
|
+
order: index,
|
|
68
|
+
})), {
|
|
69
|
+
enabled: enabled &&
|
|
70
|
+
!!userKey &&
|
|
71
|
+
!!gridName &&
|
|
72
|
+
Array.isArray(columnDefs) &&
|
|
73
|
+
columnDefs.length > 0,
|
|
74
|
+
});
|
|
75
|
+
// // Bezpečné aktualizování lastKnownColumnState při otevření editoru
|
|
76
|
+
// useEffect(() => {
|
|
77
|
+
// if (isColumnEditorOpen && columnApiRef.current && typeof columnApiRef.current.getColumnState === 'function') {
|
|
78
|
+
// try {
|
|
79
|
+
// const currentState = columnApiRef.current.getColumnState();
|
|
80
|
+
// if (Array.isArray(currentState) && currentState.length > 0) {
|
|
81
|
+
// // Aktualizujeme lastKnownColumnState po otevření editoru
|
|
82
|
+
// const validColumnDefs = Array.isArray(columnDefs) ? columnDefs : [];
|
|
83
|
+
// const formattedState = currentState.map((columnStateItem, index) => {
|
|
84
|
+
// const colDef = validColumnDefs.find(cd => cd.field === columnStateItem.colId) || {};
|
|
85
|
+
// const savedField = savedFields?.records?.find(sf => sf.fieldName === columnStateItem.colId);
|
|
86
|
+
// return {
|
|
87
|
+
// id: columnStateItem.colId,
|
|
88
|
+
// field: columnStateItem.colId,
|
|
89
|
+
// headerName: savedField?.headerName || colDef.headerName || columnStateItem.colId,
|
|
90
|
+
// originalHeaderName: colDef.headerName || columnStateItem.colId,
|
|
91
|
+
// width: columnStateItem.width || colDef.width || 100,
|
|
92
|
+
// originalWidth: colDef.width || 100,
|
|
93
|
+
// visible: !columnStateItem.hide,
|
|
94
|
+
// order: index
|
|
95
|
+
// };
|
|
96
|
+
// });
|
|
97
|
+
// setLastKnownColumnState(formattedState);
|
|
98
|
+
// console.log('[GridLayout] Updated lastKnownColumnState after opening editor');
|
|
99
|
+
// }
|
|
100
|
+
// } catch (error) {
|
|
101
|
+
// console.error('[GridLayout] Error updating column state after opening editor:', error);
|
|
102
|
+
// }
|
|
103
|
+
// }
|
|
104
|
+
// }, [isColumnEditorOpen, columnDefs, savedFields, columnApiRef]);
|
|
105
|
+
// MutationObserver pro sledování změn v DOM a okamžitou aktualizaci headerName
|
|
106
|
+
// useEffect(() => {
|
|
107
|
+
// if (!savedFields?.records || !isInitialized || !enabled) return;
|
|
108
|
+
// // Reference na observer pro cleanup
|
|
109
|
+
// let observer = null;
|
|
110
|
+
// try {
|
|
111
|
+
// console.log('[GridLayout] Setting up MutationObserver for header changes');
|
|
112
|
+
// // Funkce pro aktualizaci headerName
|
|
113
|
+
// const updateHeaderNames = () => {
|
|
114
|
+
// try {
|
|
115
|
+
// // Vytvoříme mapu fieldName -> headerName z API dat
|
|
116
|
+
// const headerNameMap = new Map();
|
|
117
|
+
// savedFields.records.forEach(field => {
|
|
118
|
+
// if (field.fieldName && field.headerName) {
|
|
119
|
+
// headerNameMap.set(field.fieldName, field.headerName);
|
|
120
|
+
// }
|
|
121
|
+
// });
|
|
122
|
+
// // Najdeme všechny hlavičky sloupců v DOM
|
|
123
|
+
// const headerCells = document.querySelectorAll('.ag-header-cell');
|
|
124
|
+
// // Aktualizujeme texty hlaviček
|
|
125
|
+
// headerCells.forEach(headerCell => {
|
|
126
|
+
// try {
|
|
127
|
+
// // Získáme ID sloupce z DOM atributů
|
|
128
|
+
// const colId = headerCell.getAttribute('col-id');
|
|
129
|
+
// if (colId && headerNameMap.has(colId)) {
|
|
130
|
+
// // Najdeme element s textem hlavičky
|
|
131
|
+
// const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
132
|
+
// if (headerTextEl) {
|
|
133
|
+
// const newHeaderName = headerNameMap.get(colId);
|
|
134
|
+
// const currentText = headerTextEl.textContent;
|
|
135
|
+
// if (currentText !== newHeaderName) {
|
|
136
|
+
// console.log(`[GridLayout] MutationObserver update: Column '${colId}' header from '${currentText}' to '${newHeaderName}'`);
|
|
137
|
+
// headerTextEl.textContent = newHeaderName;
|
|
138
|
+
// }
|
|
139
|
+
// }
|
|
140
|
+
// }
|
|
141
|
+
// } catch (cellError) {
|
|
142
|
+
// // Tiché selhání - nechceme, aby MutationObserver padal
|
|
143
|
+
// }
|
|
144
|
+
// });
|
|
145
|
+
// } catch (error) {
|
|
146
|
+
// // Tiché selhání - nechceme, aby MutationObserver padal
|
|
147
|
+
// }
|
|
148
|
+
// };
|
|
149
|
+
// // Najdeme element hlavičky
|
|
150
|
+
// const headerElement = document.querySelector('.ag-header');
|
|
151
|
+
// if (headerElement) {
|
|
152
|
+
// // Vytvoříme observer, který bude sledovat změny v hlavičce
|
|
153
|
+
// observer = new MutationObserver((mutations) => {
|
|
154
|
+
// // Detekovali jsme změnu v DOM hlavičky, aktualizujeme headerName
|
|
155
|
+
// updateHeaderNames();
|
|
156
|
+
// });
|
|
157
|
+
// // Začneme sledovat změny v hlavičce
|
|
158
|
+
// observer.observe(headerElement, {
|
|
159
|
+
// childList: true, // sledujeme přidání/odebírání elementů
|
|
160
|
+
// subtree: true, // sledujeme změny i v potomcích
|
|
161
|
+
// characterData: true, // sledujeme změny textu
|
|
162
|
+
// attributeFilter: ['col-id', 'class'] // sledujeme změny těchto atributů
|
|
163
|
+
// });
|
|
164
|
+
// console.log('[GridLayout] MutationObserver set up successfully');
|
|
165
|
+
// } else {
|
|
166
|
+
// console.log('[GridLayout] Header element not found for MutationObserver');
|
|
167
|
+
// }
|
|
168
|
+
// } catch (error) {
|
|
169
|
+
// console.error('[GridLayout] Error setting up MutationObserver:', error);
|
|
170
|
+
// }
|
|
171
|
+
// // Cleanup - odpojit observer při unmount
|
|
172
|
+
// return () => {
|
|
173
|
+
// if (observer) {
|
|
174
|
+
// observer.disconnect();
|
|
175
|
+
// console.log('[GridLayout] MutationObserver disconnected');
|
|
176
|
+
// }
|
|
177
|
+
// };
|
|
178
|
+
// }, [savedFields, isInitialized, enabled]);
|
|
61
179
|
/**
|
|
62
180
|
* Error handler
|
|
63
181
|
*/
|
|
64
182
|
const handleError = useCallback((error, context = '') => {
|
|
65
|
-
console.error
|
|
183
|
+
// Removed console.error for production
|
|
66
184
|
setError(error);
|
|
67
185
|
if (onError) {
|
|
68
186
|
onError(error, context);
|
|
69
187
|
}
|
|
70
188
|
}, [onError]);
|
|
189
|
+
// Stabilní reference pro debouncedSave
|
|
190
|
+
const stableColumnDefsRef = useRef(columnDefs);
|
|
191
|
+
const stableGridLayoutApiRef = useRef(gridLayoutApi);
|
|
192
|
+
const stableOnLayoutSavedRef = useRef(onLayoutSaved);
|
|
193
|
+
const stableHandleErrorRef = useRef(handleError);
|
|
194
|
+
// Reference pro ukládání stavu sloupců bez state update v render cyklu
|
|
195
|
+
const stableCurrentColumnsRef = useRef(null);
|
|
196
|
+
// Aktualizujeme ref hodnoty
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
// Pro columnDefs zachováváme aktualizované šířky pokud už existují
|
|
199
|
+
if (stableColumnDefsRef.current && Array.isArray(stableColumnDefsRef.current) && Array.isArray(columnDefs)) {
|
|
200
|
+
// Vytvoříme mapu existujících šířek
|
|
201
|
+
const existingWidthsMap = new Map();
|
|
202
|
+
stableColumnDefsRef.current.forEach(colDef => {
|
|
203
|
+
const fieldId = colDef.field || colDef.colId;
|
|
204
|
+
if (fieldId && colDef.width) {
|
|
205
|
+
existingWidthsMap.set(fieldId, colDef.width);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
// Aktualizujeme columnDefs s existujícími šířkami
|
|
209
|
+
const mergedColumnDefs = columnDefs.map(colDef => {
|
|
210
|
+
const fieldId = colDef.field || colDef.colId;
|
|
211
|
+
if (fieldId && existingWidthsMap.has(fieldId)) {
|
|
212
|
+
return {
|
|
213
|
+
...colDef,
|
|
214
|
+
width: existingWidthsMap.get(fieldId)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return colDef;
|
|
218
|
+
});
|
|
219
|
+
stableColumnDefsRef.current = mergedColumnDefs;
|
|
220
|
+
setColumnDefsVersion(prev => prev + 1); // Trigger useMemo přepočet
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
// První nastavení nebo pokud nejsou dostupné předchozí data
|
|
224
|
+
stableColumnDefsRef.current = columnDefs;
|
|
225
|
+
}
|
|
226
|
+
stableGridLayoutApiRef.current = gridLayoutApi;
|
|
227
|
+
stableOnLayoutSavedRef.current = onLayoutSaved;
|
|
228
|
+
stableHandleErrorRef.current = handleError;
|
|
229
|
+
});
|
|
230
|
+
// Efekt pro bezpečnou aktualizaci lastKnownColumnState z ref
|
|
231
|
+
// useEffect(() => {
|
|
232
|
+
// if (stableCurrentColumnsRef.current) {
|
|
233
|
+
// setLastKnownColumnState(stableCurrentColumnsRef.current);
|
|
234
|
+
// console.log('[GridLayout] Updated lastKnownColumnState from ref');
|
|
235
|
+
// // Vymazat po použití
|
|
236
|
+
// stableCurrentColumnsRef.current = null;
|
|
237
|
+
// }
|
|
238
|
+
// }, [stableCurrentColumnsRef.current]);
|
|
71
239
|
/**
|
|
72
240
|
* Uloží současný stav sloupců do API
|
|
73
241
|
*/
|
|
74
242
|
const saveCurrentLayout = useCallback(async () => {
|
|
75
|
-
if (!enabled || !gridApiRef.current
|
|
243
|
+
if (!enabled || !gridApiRef.current) {
|
|
76
244
|
return;
|
|
245
|
+
}
|
|
77
246
|
try {
|
|
78
247
|
setIsSaving(true);
|
|
79
248
|
setError(null);
|
|
80
|
-
// Získáme současný column state z AG-Grid
|
|
81
|
-
|
|
249
|
+
// Získáme současný column state z AG-Grid s fallbackem pro AG Grid v31+
|
|
250
|
+
let columnState = null;
|
|
251
|
+
if (gridApiRef.current &&
|
|
252
|
+
typeof gridApiRef.current.getColumnState === 'function') {
|
|
253
|
+
// AG Grid v31+ - getColumnState je přímo v main API
|
|
254
|
+
try {
|
|
255
|
+
columnState = gridApiRef.current.getColumnState();
|
|
256
|
+
// Získáme aktuální headerName hodnoty z DOM pro každý sloupec
|
|
257
|
+
if (columnState && Array.isArray(columnState)) {
|
|
258
|
+
columnState = columnState.map(colState => {
|
|
259
|
+
// Pokusíme se získat aktuální headerName z DOM
|
|
260
|
+
try {
|
|
261
|
+
const headerCell = document.querySelector(`[col-id="${colState.colId}"]`);
|
|
262
|
+
if (headerCell) {
|
|
263
|
+
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
264
|
+
if (headerTextEl && headerTextEl.textContent) {
|
|
265
|
+
return {
|
|
266
|
+
...colState,
|
|
267
|
+
headerName: headerTextEl.textContent
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (headerError) {
|
|
273
|
+
console.log(`[GridLayout] Could not get headerName from DOM for ${colState.colId}`);
|
|
274
|
+
}
|
|
275
|
+
return colState;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
// Pokud getColumnState vrátí undefined, grid je v nekonzistentním stavu
|
|
279
|
+
// Zkusíme alternativní metodu přes getColumnDefs()
|
|
280
|
+
if (columnState === undefined || columnState === null) {
|
|
281
|
+
console.warn('[GridLayout] getColumnState returned undefined/null, trying getColumnDefs() alternative');
|
|
282
|
+
console.warn('[GridLayout] Full gridApiRef.current object:', gridApiRef.current);
|
|
283
|
+
console.warn('[GridLayout] Available methods on gridApiRef.current:', gridApiRef.current
|
|
284
|
+
? Object.getOwnPropertyNames(gridApiRef.current).filter((name) => typeof gridApiRef.current[name] === 'function')
|
|
285
|
+
: 'NO_GRID_API');
|
|
286
|
+
try {
|
|
287
|
+
// Alternativní přístup: použijeme getColumnDefs() a vytvoříme fake column state
|
|
288
|
+
const columnDefs = gridApiRef.current.getColumnDefs();
|
|
289
|
+
if (columnDefs &&
|
|
290
|
+
Array.isArray(columnDefs) &&
|
|
291
|
+
columnDefs.length > 0) {
|
|
292
|
+
// Vytvoříme column state z column defs
|
|
293
|
+
columnState = columnDefs.map((colDef, index) => {
|
|
294
|
+
// Zkusíme získat aktuální šířku sloupce z DOM
|
|
295
|
+
let currentWidth = colDef.width || 100;
|
|
296
|
+
try {
|
|
297
|
+
const fieldId = colDef.field || colDef.colId;
|
|
298
|
+
const headerElement = document.querySelector(`[col-id="${fieldId}"]`);
|
|
299
|
+
if (headerElement && headerElement.offsetWidth) {
|
|
300
|
+
currentWidth = headerElement.offsetWidth;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (domError) {
|
|
304
|
+
console.log('[GridLayout] Could not get width from DOM for', colDef.field);
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
colId: colDef.field || colDef.colId,
|
|
308
|
+
hide: colDef.hide || false,
|
|
309
|
+
width: currentWidth,
|
|
310
|
+
headerName: colDef.headerName, // Přidáme aktuální headerName
|
|
311
|
+
sort: null, // Nebudeme zachovávat sort při této fallback metodě
|
|
312
|
+
sortIndex: null,
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
console.error('[GridLayout] getColumnDefs() also failed or returned empty array');
|
|
318
|
+
const cachedColumnDefs = stableColumnDefsRef.current;
|
|
319
|
+
if (cachedColumnDefs &&
|
|
320
|
+
Array.isArray(cachedColumnDefs) &&
|
|
321
|
+
cachedColumnDefs.length > 0) {
|
|
322
|
+
columnState = cachedColumnDefs.map((colDef, index) => {
|
|
323
|
+
// Použijeme cached definice
|
|
324
|
+
let currentWidth = colDef.width || 100;
|
|
325
|
+
return {
|
|
326
|
+
colId: colDef.field || colDef.colId,
|
|
327
|
+
hide: colDef.hide || false,
|
|
328
|
+
width: currentWidth,
|
|
329
|
+
headerName: colDef.headerName, // Přidáme aktuální headerName
|
|
330
|
+
sort: null,
|
|
331
|
+
sortIndex: null,
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
console.error('[GridLayout] All fallback methods failed - no column data available');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch (columnDefError) {
|
|
342
|
+
console.error('[GridLayout] getColumnDefs() alternative failed:', columnDefError);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
console.error('[GridLayout] Error calling gridApiRef.current.getColumnState():', error);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else if (columnApiRef.current &&
|
|
353
|
+
typeof columnApiRef.current.getColumnState === 'function') {
|
|
354
|
+
// Starší verze AG Grid - getColumnState je v columnApi
|
|
355
|
+
try {
|
|
356
|
+
columnState = columnApiRef.current.getColumnState();
|
|
357
|
+
// Získáme aktuální headerName hodnoty z DOM pro každý sloupec
|
|
358
|
+
if (columnState && Array.isArray(columnState)) {
|
|
359
|
+
columnState = columnState.map(colState => {
|
|
360
|
+
// Pokusíme se získat aktuální headerName z DOM
|
|
361
|
+
try {
|
|
362
|
+
const headerCell = document.querySelector(`[col-id="${colState.colId}"]`);
|
|
363
|
+
if (headerCell) {
|
|
364
|
+
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
365
|
+
if (headerTextEl && headerTextEl.textContent) {
|
|
366
|
+
return {
|
|
367
|
+
...colState,
|
|
368
|
+
headerName: headerTextEl.textContent
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch (headerError) {
|
|
374
|
+
console.log(`[GridLayout] Could not get headerName from DOM for ${colState.colId}`);
|
|
375
|
+
}
|
|
376
|
+
return colState;
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
console.error('[GridLayout] Error calling columnApiRef.current.getColumnState():', error);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
console.warn('[GridLayout] getColumnState method not available for saving layout');
|
|
387
|
+
console.warn('[GridLayout] Debug info:', {
|
|
388
|
+
gridApiRef: gridApiRef.current,
|
|
389
|
+
columnApiRef: columnApiRef.current,
|
|
390
|
+
gridApiRefKeys: gridApiRef.current
|
|
391
|
+
? Object.keys(gridApiRef.current)
|
|
392
|
+
: 'undefined',
|
|
393
|
+
columnApiRefKeys: columnApiRef.current
|
|
394
|
+
? Object.keys(columnApiRef.current)
|
|
395
|
+
: 'undefined',
|
|
396
|
+
});
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
// Kontrola validity columnDefs a použití fallback pokud je potřeba
|
|
400
|
+
let columnDefsToUse = stableColumnDefsRef.current;
|
|
401
|
+
if (!columnDefsToUse ||
|
|
402
|
+
!Array.isArray(columnDefsToUse) ||
|
|
403
|
+
columnDefsToUse.length === 0) {
|
|
404
|
+
console.warn('[GridLayout] stableColumnDefsRef is empty, using fallback from stableCurrentColumnsRef');
|
|
405
|
+
if (stableCurrentColumnsRef.current &&
|
|
406
|
+
Array.isArray(stableCurrentColumnsRef.current)) {
|
|
407
|
+
// Převedeme cached sloupce zpět na columnDefs format
|
|
408
|
+
columnDefsToUse = stableCurrentColumnsRef.current.map((col) => ({
|
|
409
|
+
field: col.field,
|
|
410
|
+
headerName: col.headerName || col.field,
|
|
411
|
+
width: col.width || 100,
|
|
412
|
+
hide: !col.visible,
|
|
413
|
+
}));
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
console.error('[GridLayout] No valid columnDefs available for saving');
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
82
420
|
// Transformujeme na Grid API format
|
|
83
|
-
const fields =
|
|
421
|
+
const fields = stableGridLayoutApiRef.current.transformColumnStateToFields(columnState, columnDefsToUse);
|
|
84
422
|
// Uložíme do API
|
|
85
|
-
const result =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
423
|
+
const result = stableGridLayoutApiRef.current
|
|
424
|
+
.saveGridLayout(fields)
|
|
425
|
+
.then((result) => {
|
|
426
|
+
if (result.success) {
|
|
427
|
+
setHasUnsavedChanges(false);
|
|
428
|
+
if (stableOnLayoutSavedRef.current) {
|
|
429
|
+
stableOnLayoutSavedRef.current(fields, columnState);
|
|
430
|
+
}
|
|
90
431
|
}
|
|
91
|
-
}
|
|
432
|
+
})
|
|
433
|
+
.catch((error) => {
|
|
434
|
+
stableHandleErrorRef.current(error, 'při ukládání layoutu');
|
|
435
|
+
});
|
|
92
436
|
}
|
|
93
437
|
catch (error) {
|
|
94
|
-
|
|
438
|
+
stableHandleErrorRef.current(error, 'při ukládání layoutu');
|
|
95
439
|
}
|
|
96
440
|
finally {
|
|
97
441
|
setIsSaving(false);
|
|
98
442
|
}
|
|
99
|
-
}, [enabled
|
|
443
|
+
}, [enabled]);
|
|
100
444
|
/**
|
|
101
445
|
* Debounced auto-save pro předcházení častým voláním API
|
|
102
446
|
*/
|
|
103
|
-
const debouncedSave = useMemo(() =>
|
|
447
|
+
const debouncedSave = useMemo(() => {
|
|
448
|
+
const debouncedFn = debounce((...args) => {
|
|
449
|
+
return saveCurrentLayout(...args);
|
|
450
|
+
}, autoSaveDelay);
|
|
451
|
+
return debouncedFn;
|
|
452
|
+
}, [saveCurrentLayout, autoSaveDelay]);
|
|
104
453
|
/**
|
|
105
454
|
* Aplikuje saved layout na AG-Grid
|
|
455
|
+
* @param {boolean} forceApply - Vynucené aplikování ignorující isInitialized stav
|
|
106
456
|
*/
|
|
107
|
-
const applySavedLayout = useCallback(() => {
|
|
108
|
-
|
|
457
|
+
const applySavedLayout = useCallback((forceApply = false) => {
|
|
458
|
+
// Ověříme dostupnost API a inicializaci
|
|
459
|
+
if (!savedFields?.records ||
|
|
460
|
+
savedFields.records.length === 0 ||
|
|
461
|
+
(!forceApply && isInitialized)) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
// Ověříme dostupnost applyColumnState metody (AG Grid v31+ má ji v main API)
|
|
465
|
+
let applyColumnStateApi = null;
|
|
466
|
+
if (gridApiRef.current &&
|
|
467
|
+
typeof gridApiRef.current.applyColumnState === 'function') {
|
|
468
|
+
applyColumnStateApi = gridApiRef.current;
|
|
469
|
+
}
|
|
470
|
+
else if (columnApiRef.current &&
|
|
471
|
+
typeof columnApiRef.current.applyColumnState === 'function') {
|
|
472
|
+
applyColumnStateApi = columnApiRef.current;
|
|
473
|
+
}
|
|
474
|
+
if (!applyColumnStateApi) {
|
|
475
|
+
console.warn('[GridLayout] applyColumnState method not available');
|
|
109
476
|
return;
|
|
477
|
+
}
|
|
110
478
|
try {
|
|
111
479
|
setIsLoading(true);
|
|
112
480
|
// Transformujeme Grid API fields na AG-Grid column state
|
|
113
|
-
|
|
114
|
-
|
|
481
|
+
// Použijeme stableColumnDefsRef.current místo columnDefs pro zachování aktuálních šířek
|
|
482
|
+
const columnDefsToUse = stableColumnDefsRef.current || columnDefs;
|
|
483
|
+
const columnState = gridLayoutApi.transformFieldsToColumnState(savedFields.records, columnDefsToUse);
|
|
115
484
|
if (columnState && columnState.length > 0) {
|
|
116
|
-
// Pokud je waitForSavedFields true, columnDefs jsou už pre-transformované,
|
|
485
|
+
// Pokud je waitForSavedFields true, columnDefs jsou už pre-transformované,
|
|
117
486
|
// takže aplikujeme jen width a hide vlastnosti bez delay pro pořadí
|
|
118
487
|
const applyFunction = () => {
|
|
119
488
|
try {
|
|
120
|
-
|
|
121
|
-
columnState,
|
|
122
|
-
columnCount: columnState.length,
|
|
123
|
-
columnIds: columnState.map(c => c.colId),
|
|
124
|
-
hasOrder: true // pořadí je v poli implicitně
|
|
125
|
-
});
|
|
126
|
-
// Debug info o columnApiRef
|
|
127
|
-
console.log("🔍 columnApiRef debug:", {
|
|
128
|
-
hasColumnApiRef: !!columnApiRef.current,
|
|
129
|
-
columnApiType: typeof columnApiRef.current,
|
|
130
|
-
columnApiMethods: columnApiRef.current ? Object.getOwnPropertyNames(columnApiRef.current).filter(name => typeof columnApiRef.current[name] === 'function') : [],
|
|
131
|
-
hasApplyColumnState: columnApiRef.current && typeof columnApiRef.current.applyColumnState === 'function'
|
|
132
|
-
});
|
|
133
|
-
// Ověření, že columnApi podporuje applyColumnState
|
|
134
|
-
if (!columnApiRef.current) {
|
|
135
|
-
throw new Error('columnApiRef.current is null or undefined');
|
|
136
|
-
}
|
|
137
|
-
if (typeof columnApiRef.current.applyColumnState !== 'function') {
|
|
138
|
-
throw new Error(`applyColumnState is not a function. Available methods: ${Object.getOwnPropertyNames(columnApiRef.current).filter(name => typeof columnApiRef.current[name] === 'function').join(', ')}`);
|
|
139
|
-
}
|
|
140
|
-
console.log("test2");
|
|
141
|
-
// Aplikujeme column state na AG-Grid
|
|
489
|
+
// Aplikujeme column state na AG-Grid
|
|
142
490
|
let result;
|
|
143
491
|
try {
|
|
144
492
|
const applyOrderEnabled = !waitForSavedFields; // Při waitForSavedFields už je pořadí v columnDefs
|
|
145
|
-
|
|
146
|
-
|
|
493
|
+
// Získáme aktuální stav sloupců před aplikováním
|
|
494
|
+
const currentColumnState = gridApiRef.current?.getColumnState?.() || [];
|
|
495
|
+
const currentWidthMap = new Map();
|
|
496
|
+
currentColumnState.forEach(colState => {
|
|
497
|
+
if (colState.colId && colState.width) {
|
|
498
|
+
currentWidthMap.set(colState.colId, colState.width);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
// Upravíme columnState tak, aby zachoval aktuální šířky pokud existují
|
|
502
|
+
const adjustedColumnState = columnState.map(colState => {
|
|
503
|
+
const currentWidth = currentWidthMap.get(colState.colId);
|
|
504
|
+
if (currentWidth && currentWidth !== colState.width) {
|
|
505
|
+
return {
|
|
506
|
+
...colState,
|
|
507
|
+
width: currentWidth
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
return colState;
|
|
511
|
+
});
|
|
512
|
+
result = applyColumnStateApi.applyColumnState({
|
|
513
|
+
state: adjustedColumnState,
|
|
147
514
|
applyOrder: applyOrderEnabled, // Pořadí jen když není waitForSavedFields
|
|
148
515
|
defaultState: {
|
|
149
516
|
sort: null, // Reset sorting na všech sloupcích
|
|
150
|
-
sortIndex: null, // Reset sort index
|
|
517
|
+
sortIndex: null, // Reset sort index
|
|
151
518
|
pivot: null, // Reset pivot
|
|
152
|
-
rowGroup: null // Reset row grouping
|
|
153
|
-
}
|
|
519
|
+
rowGroup: null, // Reset row grouping
|
|
520
|
+
},
|
|
154
521
|
});
|
|
155
|
-
|
|
522
|
+
// Explicitně aktualizujeme headerName pro každý sloupec, protože AG-Grid
|
|
523
|
+
// nepodporuje nastavení headerName přes applyColumnState
|
|
524
|
+
if (savedFields.records &&
|
|
525
|
+
Array.isArray(savedFields.records) &&
|
|
526
|
+
gridApiRef.current) {
|
|
527
|
+
// Nejprve zkusíme použít refreshHeader funkci, pokud je dostupná
|
|
528
|
+
try {
|
|
529
|
+
if (typeof gridApiRef.current.refreshHeader === 'function') {
|
|
530
|
+
gridApiRef.current.refreshHeader();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch (refreshError) {
|
|
534
|
+
console.error('[GridLayout] Error in refreshHeader:', refreshError);
|
|
535
|
+
}
|
|
536
|
+
// Získáme aktuální definice sloupců z gridu
|
|
537
|
+
let currentColDefs;
|
|
538
|
+
try {
|
|
539
|
+
currentColDefs = gridApiRef.current.getColumnDefs
|
|
540
|
+
? gridApiRef.current.getColumnDefs()
|
|
541
|
+
: null;
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
console.error('[GridLayout] Error getting column definitions:', error);
|
|
545
|
+
currentColDefs = null;
|
|
546
|
+
}
|
|
547
|
+
if (currentColDefs && Array.isArray(currentColDefs)) {
|
|
548
|
+
// Vytvoříme mapu fieldName -> headerName z API dat
|
|
549
|
+
const headerNameMap = new Map();
|
|
550
|
+
savedFields.records.forEach((field) => {
|
|
551
|
+
if (field.fieldName && field.headerName) {
|
|
552
|
+
headerNameMap.set(field.fieldName, field.headerName);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
// Aktualizujeme headerName pro každý sloupec
|
|
556
|
+
const updatedColDefs = currentColDefs.map((colDef) => {
|
|
557
|
+
const fieldName = colDef.field;
|
|
558
|
+
if (fieldName && headerNameMap.has(fieldName)) {
|
|
559
|
+
const newHeaderName = headerNameMap.get(fieldName);
|
|
560
|
+
// Vytvoříme novou kopii definice sloupce s aktualizovaným headerName
|
|
561
|
+
return {
|
|
562
|
+
...colDef,
|
|
563
|
+
headerName: newHeaderName,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
return colDef;
|
|
567
|
+
});
|
|
568
|
+
// Aplikujeme aktualizované definice sloupců zpět do gridu
|
|
569
|
+
try {
|
|
570
|
+
if (typeof gridApiRef.current.setColumnDefs === 'function') {
|
|
571
|
+
gridApiRef.current.setColumnDefs(updatedColDefs);
|
|
572
|
+
// Pro jistotu zkontrolujeme, zda byly změny aplikovány
|
|
573
|
+
setTimeout(() => {
|
|
574
|
+
try {
|
|
575
|
+
const afterUpdateColDefs = gridApiRef.current.getColumnDefs();
|
|
576
|
+
}
|
|
577
|
+
catch (checkError) {
|
|
578
|
+
console.error('[GridLayout] Error checking updated columns:', checkError);
|
|
579
|
+
}
|
|
580
|
+
}, 100);
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
// Alternativní řešení - přímá manipulace s DOM
|
|
584
|
+
// Počkáme, až se grid vyrenderuje
|
|
585
|
+
setTimeout(() => {
|
|
586
|
+
try {
|
|
587
|
+
// Vytvoříme mapu pro rychlou identifikaci
|
|
588
|
+
const headerUpdates = new Map();
|
|
589
|
+
updatedColDefs.forEach((colDef) => {
|
|
590
|
+
if (colDef.field && colDef.headerName) {
|
|
591
|
+
headerUpdates.set(colDef.field, colDef.headerName);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
// Najdeme všechny hlavičky sloupců pomocí DOM
|
|
595
|
+
const headerCells = document.querySelectorAll('.ag-header-cell');
|
|
596
|
+
headerCells.forEach((headerCell) => {
|
|
597
|
+
try {
|
|
598
|
+
// Získáme ID sloupce z DOM atributů
|
|
599
|
+
const colId = headerCell.getAttribute('col-id');
|
|
600
|
+
if (colId && headerUpdates.has(colId)) {
|
|
601
|
+
// Najdeme element s textem hlavičky
|
|
602
|
+
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
603
|
+
if (headerTextEl) {
|
|
604
|
+
const newHeaderName = headerUpdates.get(colId);
|
|
605
|
+
const currentText = headerTextEl.textContent;
|
|
606
|
+
headerTextEl.textContent = newHeaderName;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
catch (cellError) {
|
|
611
|
+
console.error('[GridLayout] Error updating header cell:', cellError);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
catch (domError) {
|
|
616
|
+
console.error('[GridLayout] Error in DOM manipulation:', domError);
|
|
617
|
+
}
|
|
618
|
+
}, 200);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
catch (setError) {
|
|
622
|
+
console.error('[GridLayout] Error applying column definitions:', setError);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
156
626
|
}
|
|
157
627
|
catch (applyError) {
|
|
158
|
-
console.error("❌ Error during applyColumnState:", applyError);
|
|
159
628
|
throw applyError;
|
|
160
629
|
}
|
|
161
|
-
console.log("✅ Column state applied successfully:", {
|
|
162
|
-
result,
|
|
163
|
-
appliedColumns: columnState.length
|
|
164
|
-
});
|
|
165
|
-
// Ověření, že se sloupce skutečně přeuspořádaly
|
|
166
|
-
const newColumnState = columnApiRef.current.getColumnState();
|
|
167
|
-
console.log("🚀 ~ test3:", newColumnState);
|
|
168
|
-
console.log("🔍 Current column state after apply:", newColumnState.map(c => c.colId));
|
|
169
630
|
if (onLayoutLoaded) {
|
|
170
631
|
onLayoutLoaded(savedFields.records, columnState);
|
|
171
632
|
}
|
|
172
633
|
}
|
|
173
634
|
catch (delayedError) {
|
|
174
|
-
console.error
|
|
635
|
+
// Removed console.error for production
|
|
175
636
|
handleError(delayedError, 'při delayed aplikování layoutu');
|
|
176
637
|
}
|
|
177
638
|
};
|
|
@@ -190,72 +651,328 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
190
651
|
}
|
|
191
652
|
finally {
|
|
192
653
|
setIsInitialized(true);
|
|
654
|
+
setIsGridReady(true); // Obnovíme také isGridReady pro event handlery
|
|
193
655
|
setIsLoading(false);
|
|
194
656
|
}
|
|
195
|
-
}, [
|
|
657
|
+
}, [
|
|
658
|
+
savedFields,
|
|
659
|
+
gridLayoutApi,
|
|
660
|
+
isInitialized,
|
|
661
|
+
onLayoutLoaded,
|
|
662
|
+
handleError,
|
|
663
|
+
columnDefs,
|
|
664
|
+
waitForSavedFields,
|
|
665
|
+
]);
|
|
666
|
+
/**
|
|
667
|
+
* Odložené akce pro případ rychlé interakce před dokončením inicializace
|
|
668
|
+
*/
|
|
669
|
+
const [pendingActions, setPendingActions] = useState([]);
|
|
670
|
+
// Effect pro zpracování pending actions po dokončení inicializace
|
|
671
|
+
useEffect(() => {
|
|
672
|
+
if (isInitialized && pendingActions.length > 0) {
|
|
673
|
+
// Zpracujeme pending akce s krátkým delay
|
|
674
|
+
setTimeout(() => {
|
|
675
|
+
pendingActions.forEach((action) => {
|
|
676
|
+
switch (action.type) {
|
|
677
|
+
case 'dragStopped':
|
|
678
|
+
if (autoSave && debouncedSave) {
|
|
679
|
+
debouncedSave();
|
|
680
|
+
}
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
setPendingActions([]);
|
|
685
|
+
}, 100);
|
|
686
|
+
}
|
|
687
|
+
}, [isInitialized, pendingActions, autoSave, debouncedSave]);
|
|
196
688
|
/**
|
|
197
689
|
* Event handlers pro AG-Grid
|
|
198
690
|
*/
|
|
199
691
|
const handleColumnMoved = useCallback(() => {
|
|
200
|
-
if (!enabled || !
|
|
692
|
+
if (!enabled || !isGridReady) {
|
|
201
693
|
return;
|
|
694
|
+
}
|
|
202
695
|
setHasUnsavedChanges(true);
|
|
203
696
|
// Neukládáme při každém pohybu - čekáme na onDragStopped
|
|
204
|
-
}, [enabled,
|
|
697
|
+
}, [enabled, isGridReady]);
|
|
205
698
|
const handleDragStopped = useCallback(() => {
|
|
206
|
-
if (!enabled || !
|
|
699
|
+
if (!enabled || !isGridReady) {
|
|
207
700
|
return;
|
|
701
|
+
}
|
|
208
702
|
setHasUnsavedChanges(true);
|
|
209
|
-
|
|
210
|
-
|
|
703
|
+
// Aktualizujeme šířky sloupců v columnDefs před uložením
|
|
704
|
+
try {
|
|
705
|
+
if (gridApiRef.current && typeof gridApiRef.current.getColumnState === 'function') {
|
|
706
|
+
const currentColumnState = gridApiRef.current.getColumnState();
|
|
707
|
+
if (currentColumnState && Array.isArray(currentColumnState)) {
|
|
708
|
+
// Aktualizujeme šířky v původních columnDefs
|
|
709
|
+
const widthUpdatesMap = new Map();
|
|
710
|
+
currentColumnState.forEach(colState => {
|
|
711
|
+
if (colState.colId && colState.width) {
|
|
712
|
+
widthUpdatesMap.set(colState.colId, colState.width);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
// Aktualizujeme stableColumnDefsRef s novými šířkami
|
|
716
|
+
if (stableColumnDefsRef.current && Array.isArray(stableColumnDefsRef.current)) {
|
|
717
|
+
const updatedColumnDefs = stableColumnDefsRef.current.map(colDef => {
|
|
718
|
+
const fieldId = colDef.field || colDef.colId;
|
|
719
|
+
if (fieldId && widthUpdatesMap.has(fieldId)) {
|
|
720
|
+
return {
|
|
721
|
+
...colDef,
|
|
722
|
+
width: widthUpdatesMap.get(fieldId)
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
return colDef;
|
|
726
|
+
});
|
|
727
|
+
stableColumnDefsRef.current = updatedColumnDefs;
|
|
728
|
+
setColumnDefsVersion(prev => prev + 1); // Trigger useMemo přepočet
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
console.error('[GridLayout] Error updating columnDefs widths in handleDragStopped:', error);
|
|
735
|
+
}
|
|
736
|
+
// Pokud ještě není inicializované, přidáme akci do pending
|
|
737
|
+
if (!isInitialized && autoSave) {
|
|
738
|
+
setPendingActions((prev) => [
|
|
739
|
+
...prev,
|
|
740
|
+
{ type: 'dragStopped', timestamp: Date.now() },
|
|
741
|
+
]);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
// Normální proces pokud je vše inicializované
|
|
745
|
+
if (autoSave && debouncedSave) {
|
|
211
746
|
debouncedSave();
|
|
212
747
|
}
|
|
213
|
-
|
|
748
|
+
else {
|
|
749
|
+
}
|
|
750
|
+
}, [enabled, isGridReady, isInitialized, autoSave, debouncedSave]);
|
|
214
751
|
const handleColumnResized = useCallback(() => {
|
|
215
|
-
if (!enabled || !
|
|
752
|
+
if (!enabled || !isGridReady)
|
|
216
753
|
return;
|
|
217
754
|
setHasUnsavedChanges(true);
|
|
218
|
-
|
|
755
|
+
// Aktualizujeme šířky sloupců v columnDefs při resize
|
|
756
|
+
try {
|
|
757
|
+
if (gridApiRef.current && typeof gridApiRef.current.getColumnState === 'function') {
|
|
758
|
+
const currentColumnState = gridApiRef.current.getColumnState();
|
|
759
|
+
if (currentColumnState && Array.isArray(currentColumnState)) {
|
|
760
|
+
// Aktualizujeme šířky v původních columnDefs
|
|
761
|
+
const widthUpdatesMap = new Map();
|
|
762
|
+
currentColumnState.forEach(colState => {
|
|
763
|
+
if (colState.colId && colState.width) {
|
|
764
|
+
widthUpdatesMap.set(colState.colId, colState.width);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
// Aktualizujeme stableColumnDefsRef s novými šířkami
|
|
768
|
+
if (stableColumnDefsRef.current && Array.isArray(stableColumnDefsRef.current)) {
|
|
769
|
+
const updatedColumnDefs = stableColumnDefsRef.current.map(colDef => {
|
|
770
|
+
const fieldId = colDef.field || colDef.colId;
|
|
771
|
+
if (fieldId && widthUpdatesMap.has(fieldId)) {
|
|
772
|
+
return {
|
|
773
|
+
...colDef,
|
|
774
|
+
width: widthUpdatesMap.get(fieldId)
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
return colDef;
|
|
778
|
+
});
|
|
779
|
+
stableColumnDefsRef.current = updatedColumnDefs;
|
|
780
|
+
setColumnDefsVersion(prev => prev + 1); // Trigger useMemo přepočet
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
console.error('[GridLayout] Error updating columnDefs widths in handleColumnResized:', error);
|
|
787
|
+
}
|
|
788
|
+
if (autoSave && isInitialized && debouncedSave) {
|
|
219
789
|
debouncedSave();
|
|
220
790
|
}
|
|
221
|
-
}, [enabled, isInitialized, autoSave, debouncedSave]);
|
|
791
|
+
}, [enabled, isGridReady, isInitialized, autoSave, debouncedSave]);
|
|
222
792
|
const handleColumnVisible = useCallback(() => {
|
|
223
|
-
if (!enabled || !
|
|
793
|
+
if (!enabled || !isGridReady)
|
|
224
794
|
return;
|
|
225
795
|
setHasUnsavedChanges(true);
|
|
226
|
-
if (autoSave) {
|
|
796
|
+
if (autoSave && isInitialized && debouncedSave) {
|
|
227
797
|
debouncedSave();
|
|
228
798
|
}
|
|
229
|
-
}, [enabled, isInitialized, autoSave, debouncedSave]);
|
|
799
|
+
}, [enabled, isGridReady, isInitialized, autoSave, debouncedSave]);
|
|
230
800
|
const handleColumnPinned = useCallback(() => {
|
|
231
|
-
if (!enabled || !
|
|
801
|
+
if (!enabled || !isGridReady)
|
|
232
802
|
return;
|
|
233
803
|
setHasUnsavedChanges(true);
|
|
234
|
-
if (autoSave) {
|
|
804
|
+
if (autoSave && isInitialized && debouncedSave) {
|
|
235
805
|
debouncedSave();
|
|
236
806
|
}
|
|
237
|
-
}, [enabled, isInitialized, autoSave, debouncedSave]);
|
|
807
|
+
}, [enabled, isGridReady, isInitialized, autoSave, debouncedSave]);
|
|
238
808
|
/**
|
|
239
809
|
* AG-Grid Ready handler
|
|
240
810
|
*/
|
|
241
811
|
const handleGridReady = useCallback((params) => {
|
|
242
|
-
if (!enabled)
|
|
812
|
+
if (!enabled) {
|
|
243
813
|
return;
|
|
814
|
+
}
|
|
244
815
|
gridApiRef.current = params.api;
|
|
245
|
-
|
|
246
|
-
// Pokud
|
|
247
|
-
|
|
248
|
-
|
|
816
|
+
// AG Grid v31+ - columnApi je deprecated, všechny metody jsou v hlavním api
|
|
817
|
+
// Pokud columnApi neexistuje, použijeme main api jako fallback
|
|
818
|
+
columnApiRef.current = params.columnApi || params.api;
|
|
819
|
+
// Okamžitě označíme grid jako připravený pro event handlery
|
|
820
|
+
setIsGridReady(true);
|
|
821
|
+
// Po reloadLayout() NEPOTŘEBUJEME znovu aplikovat layout - už je v DB a grid ho má správně
|
|
822
|
+
// Jen nastavíme isInitialized na true
|
|
823
|
+
if (isInitialized === false) {
|
|
824
|
+
setIsInitialized(true);
|
|
249
825
|
}
|
|
250
826
|
}, [enabled, savedFields, isInitialized, applySavedLayout]);
|
|
251
827
|
/**
|
|
252
|
-
* Effect pro aplikování layoutu když se načtou data
|
|
828
|
+
* Effect pro aplikování layoutu když se načtou data po reloadLayout
|
|
829
|
+
* ODSTRANĚNO - způsobovalo předčasné volání applySavedLayout() před grid ready
|
|
253
830
|
*/
|
|
254
|
-
useEffect(() => {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
831
|
+
// useEffect(() => {
|
|
832
|
+
// // Po refetchFields() (z reloadLayout) potřebujeme aplikovat layout a obnovit isGridReady
|
|
833
|
+
// if (savedFields?.records && !isInitialized && gridApiRef.current) {
|
|
834
|
+
// console.log('[GridLayout] Effect: Applying layout after refetch and setting isGridReady=true');
|
|
835
|
+
// applySavedLayout();
|
|
836
|
+
// }
|
|
837
|
+
// }, [savedFields, isInitialized, applySavedLayout]);
|
|
838
|
+
/**
|
|
839
|
+
* Původní zakomentovaný effect pro aplikování layoutu když se načtou data
|
|
840
|
+
*/
|
|
841
|
+
// useEffect(() => {
|
|
842
|
+
// if (savedFields?.records && columnApiRef.current && !isInitialized) {
|
|
843
|
+
// console.log('[GridLayout] Layout will be applied from effect');
|
|
844
|
+
// applySavedLayout();
|
|
845
|
+
// } else if (savedFields?.records && columnApiRef.current && isInitialized) {
|
|
846
|
+
// // Pokud je již inicializován, ale přišla nová data, aktualizujeme jen DOM (ne celý layout)
|
|
847
|
+
// console.log('[GridLayout] Already initialized, but received new data - updating headers directly');
|
|
848
|
+
// // Počkáme moment, než se nová data zpracují, a pak aktualizujeme headerName
|
|
849
|
+
// setTimeout(() => {
|
|
850
|
+
// try {
|
|
851
|
+
// // Vytvoříme mapu fieldName -> headerName z API dat
|
|
852
|
+
// const headerNameMap = new Map();
|
|
853
|
+
// savedFields.records.forEach(field => {
|
|
854
|
+
// if (field.fieldName && field.headerName) {
|
|
855
|
+
// headerNameMap.set(field.fieldName, field.headerName);
|
|
856
|
+
// console.log(`[GridLayout] Update after reload: '${field.fieldName}' -> '${field.headerName}'`);
|
|
857
|
+
// }
|
|
858
|
+
// });
|
|
859
|
+
// // Najdeme všechny hlavičky sloupců v DOM
|
|
860
|
+
// const headerCells = document.querySelectorAll('.ag-header-cell');
|
|
861
|
+
// console.log('[GridLayout] Found header cells after reload:', headerCells.length);
|
|
862
|
+
// // Aktualizujeme texty hlaviček
|
|
863
|
+
// headerCells.forEach(headerCell => {
|
|
864
|
+
// try {
|
|
865
|
+
// // Získáme ID sloupce z DOM atributů
|
|
866
|
+
// const colId = headerCell.getAttribute('col-id');
|
|
867
|
+
// if (colId && headerNameMap.has(colId)) {
|
|
868
|
+
// // Najdeme element s textem hlavičky
|
|
869
|
+
// const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
870
|
+
// if (headerTextEl) {
|
|
871
|
+
// const newHeaderName = headerNameMap.get(colId);
|
|
872
|
+
// const currentText = headerTextEl.textContent;
|
|
873
|
+
// console.log(`[GridLayout] DOM update after reload: Column '${colId}' header from '${currentText}' to '${newHeaderName}'`);
|
|
874
|
+
// headerTextEl.textContent = newHeaderName;
|
|
875
|
+
// }
|
|
876
|
+
// }
|
|
877
|
+
// } catch (cellError) {
|
|
878
|
+
// console.error('[GridLayout] Error updating header cell after reload:', cellError);
|
|
879
|
+
// }
|
|
880
|
+
// });
|
|
881
|
+
// // Zkusíme vynutit překreslení hlavičky
|
|
882
|
+
// if (typeof gridApiRef.current.refreshHeader === 'function') {
|
|
883
|
+
// console.log('[GridLayout] Forcing header refresh after reload');
|
|
884
|
+
// gridApiRef.current.refreshHeader();
|
|
885
|
+
// }
|
|
886
|
+
// } catch (error) {
|
|
887
|
+
// console.error('[GridLayout] Error updating headers after reload:', error);
|
|
888
|
+
// }
|
|
889
|
+
// }, 200);
|
|
890
|
+
// }
|
|
891
|
+
// }, [savedFields, applySavedLayout, isInitialized, columnApiRef]);
|
|
892
|
+
/**
|
|
893
|
+
* Alternativní metoda pro aktualizaci headerName po inicializaci gridu
|
|
894
|
+
*/
|
|
895
|
+
// useEffect(() => {
|
|
896
|
+
// // Pokud již proběhla inicializace a máme uložená data z API
|
|
897
|
+
// if (isInitialized && savedFields?.records && gridApiRef.current) {
|
|
898
|
+
// console.log('[GridLayout] Attempting alternative header update after initialization');
|
|
899
|
+
// // Zkusíme použít metodu columnDefHeaderNameChanged, pokud je dostupná
|
|
900
|
+
// try {
|
|
901
|
+
// if (typeof gridApiRef.current.columnDefHeaderNameChanged === 'function') {
|
|
902
|
+
// console.log('[GridLayout] Trying columnDefHeaderNameChanged method');
|
|
903
|
+
// // Vytvoříme mapu fieldName -> headerName z API dat
|
|
904
|
+
// const headerNameMap = new Map();
|
|
905
|
+
// savedFields.records.forEach(field => {
|
|
906
|
+
// if (field.fieldName && field.headerName) {
|
|
907
|
+
// headerNameMap.set(field.fieldName, field.headerName);
|
|
908
|
+
// }
|
|
909
|
+
// });
|
|
910
|
+
// // Získáme aktuální definice sloupců
|
|
911
|
+
// const currentColumnDefs = gridApiRef.current.getColumnDefs();
|
|
912
|
+
// if (currentColumnDefs && Array.isArray(currentColumnDefs)) {
|
|
913
|
+
// currentColumnDefs.forEach(colDef => {
|
|
914
|
+
// if (colDef.field && headerNameMap.has(colDef.field)) {
|
|
915
|
+
// const newHeaderName = headerNameMap.get(colDef.field);
|
|
916
|
+
// if (colDef.headerName !== newHeaderName) {
|
|
917
|
+
// console.log(`[GridLayout] Using columnDefHeaderNameChanged for '${colDef.field}': '${colDef.headerName}' -> '${newHeaderName}'`);
|
|
918
|
+
// colDef.headerName = newHeaderName;
|
|
919
|
+
// gridApiRef.current.columnDefHeaderNameChanged(colDef);
|
|
920
|
+
// }
|
|
921
|
+
// }
|
|
922
|
+
// });
|
|
923
|
+
// }
|
|
924
|
+
// }
|
|
925
|
+
// } catch (error) {
|
|
926
|
+
// console.error('[GridLayout] Error using columnDefHeaderNameChanged:', error);
|
|
927
|
+
// }
|
|
928
|
+
// // Počkáme chvíli, aby se grid stihl plně vyrenderovat
|
|
929
|
+
// setTimeout(() => {
|
|
930
|
+
// try {
|
|
931
|
+
// // Získáme aktuální definice sloupců z gridu
|
|
932
|
+
// const currentColDefs = gridApiRef.current.getColumnDefs ? gridApiRef.current.getColumnDefs() : null;
|
|
933
|
+
// if (currentColDefs && Array.isArray(currentColDefs)) {
|
|
934
|
+
// // Vytvoříme mapu fieldName -> headerName z API dat
|
|
935
|
+
// const headerNameMap = new Map();
|
|
936
|
+
// savedFields.records.forEach(field => {
|
|
937
|
+
// if (field.fieldName && field.headerName) {
|
|
938
|
+
// headerNameMap.set(field.fieldName, field.headerName);
|
|
939
|
+
// }
|
|
940
|
+
// });
|
|
941
|
+
// // Zkontrolujeme, zda je třeba aktualizovat nějaké headery
|
|
942
|
+
// let needsUpdate = false;
|
|
943
|
+
// const updatedColDefs = currentColDefs.map(colDef => {
|
|
944
|
+
// const fieldName = colDef.field;
|
|
945
|
+
// if (fieldName && headerNameMap.has(fieldName)) {
|
|
946
|
+
// const newHeaderName = headerNameMap.get(fieldName);
|
|
947
|
+
// if (colDef.headerName !== newHeaderName) {
|
|
948
|
+
// needsUpdate = true;
|
|
949
|
+
// console.log(`[GridLayout] Alternative update: Column '${fieldName}' header from '${colDef.headerName}' to '${newHeaderName}'`);
|
|
950
|
+
// return {
|
|
951
|
+
// ...colDef,
|
|
952
|
+
// headerName: newHeaderName
|
|
953
|
+
// };
|
|
954
|
+
// }
|
|
955
|
+
// }
|
|
956
|
+
// return colDef;
|
|
957
|
+
// });
|
|
958
|
+
// if (needsUpdate && typeof gridApiRef.current.setColumnDefs === 'function') {
|
|
959
|
+
// console.log('[GridLayout] Applying alternative column header update');
|
|
960
|
+
// gridApiRef.current.setColumnDefs(updatedColDefs);
|
|
961
|
+
// // Zkusíme vynutit překreslení hlavičky
|
|
962
|
+
// setTimeout(() => {
|
|
963
|
+
// if (typeof gridApiRef.current.refreshHeader === 'function') {
|
|
964
|
+
// console.log('[GridLayout] Forcing header refresh');
|
|
965
|
+
// gridApiRef.current.refreshHeader();
|
|
966
|
+
// }
|
|
967
|
+
// }, 50);
|
|
968
|
+
// }
|
|
969
|
+
// }
|
|
970
|
+
// } catch (error) {
|
|
971
|
+
// console.error('[GridLayout] Error in alternative header update:', error);
|
|
972
|
+
// }
|
|
973
|
+
// }, 300);
|
|
974
|
+
// }
|
|
975
|
+
// }, [isInitialized, savedFields, gridApiRef]);
|
|
259
976
|
/**
|
|
260
977
|
* Effect pro error handling
|
|
261
978
|
*/
|
|
@@ -276,12 +993,26 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
276
993
|
* Resetuje layout na default
|
|
277
994
|
*/
|
|
278
995
|
const resetToDefault = useCallback(async () => {
|
|
279
|
-
if (!enabled
|
|
996
|
+
if (!enabled)
|
|
997
|
+
return;
|
|
998
|
+
// Ověříme dostupnost resetColumnState metody (AG Grid v31+ má ji v main API)
|
|
999
|
+
let resetColumnStateApi = null;
|
|
1000
|
+
if (gridApiRef.current &&
|
|
1001
|
+
typeof gridApiRef.current.resetColumnState === 'function') {
|
|
1002
|
+
resetColumnStateApi = gridApiRef.current;
|
|
1003
|
+
}
|
|
1004
|
+
else if (columnApiRef.current &&
|
|
1005
|
+
typeof columnApiRef.current.resetColumnState === 'function') {
|
|
1006
|
+
resetColumnStateApi = columnApiRef.current;
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
console.warn('[GridLayout] resetColumnState method not available');
|
|
280
1010
|
return;
|
|
1011
|
+
}
|
|
281
1012
|
try {
|
|
282
1013
|
setIsLoading(true);
|
|
283
1014
|
// Resetujeme AG-Grid na původní column definitions
|
|
284
|
-
|
|
1015
|
+
resetColumnStateApi.resetColumnState();
|
|
285
1016
|
// Pokud je autoSave zapnuté, uložíme resetovaný stav
|
|
286
1017
|
if (autoSave) {
|
|
287
1018
|
await saveCurrentLayout();
|
|
@@ -308,8 +1039,22 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
308
1039
|
*/
|
|
309
1040
|
const reloadLayout = useCallback(async () => {
|
|
310
1041
|
try {
|
|
1042
|
+
// Save a reference to the current grid API before resetting states
|
|
1043
|
+
const savedGridApiRef = gridApiRef.current;
|
|
1044
|
+
const savedColumnApiRef = columnApiRef.current;
|
|
311
1045
|
setIsInitialized(false);
|
|
1046
|
+
setIsGridReady(false);
|
|
312
1047
|
await refetchFields();
|
|
1048
|
+
// Restore saved references if they were lost during the reload process
|
|
1049
|
+
if (!gridApiRef.current && savedGridApiRef) {
|
|
1050
|
+
gridApiRef.current = savedGridApiRef;
|
|
1051
|
+
}
|
|
1052
|
+
if (!columnApiRef.current && savedColumnApiRef) {
|
|
1053
|
+
columnApiRef.current = savedColumnApiRef;
|
|
1054
|
+
}
|
|
1055
|
+
// Po refetch dat obnovíme oba stavy (layout je už v DB)
|
|
1056
|
+
setIsInitialized(true);
|
|
1057
|
+
setIsGridReady(true);
|
|
313
1058
|
}
|
|
314
1059
|
catch (error) {
|
|
315
1060
|
handleError(error, 'při reload layoutu');
|
|
@@ -319,83 +1064,102 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
319
1064
|
* Získá aktuální column state pro Column Editor
|
|
320
1065
|
*/
|
|
321
1066
|
const getCurrentColumnsForEditor = useCallback(() => {
|
|
322
|
-
console.log("🔍 getCurrentColumnsForEditor called", {
|
|
323
|
-
hasColumnApiRef: !!columnApiRef.current,
|
|
324
|
-
columnDefs: columnDefs?.length || 0,
|
|
325
|
-
savedFieldsCount: savedFields?.records?.length || 0
|
|
326
|
-
});
|
|
327
1067
|
// Zajistíme, že máme validní columnDefs
|
|
328
1068
|
const validColumnDefs = Array.isArray(columnDefs) ? columnDefs : [];
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
1069
|
+
try {
|
|
1070
|
+
let currentColumnState = null;
|
|
1071
|
+
// Pokusíme se získat column state s různými fallbacky pro AG Grid v31+
|
|
1072
|
+
if (gridApiRef.current &&
|
|
1073
|
+
typeof gridApiRef.current.getColumnState === 'function') {
|
|
1074
|
+
// AG Grid v31+ - getColumnState je přímo v main API
|
|
1075
|
+
currentColumnState = gridApiRef.current.getColumnState();
|
|
1076
|
+
}
|
|
1077
|
+
else if (columnApiRef.current &&
|
|
1078
|
+
typeof columnApiRef.current.getColumnState === 'function') {
|
|
1079
|
+
// Starší verze AG Grid - getColumnState je v columnApi
|
|
1080
|
+
currentColumnState = columnApiRef.current.getColumnState();
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
throw new Error('getColumnState method is not available on gridApiRef or columnApiRef');
|
|
1084
|
+
}
|
|
1085
|
+
if (!Array.isArray(currentColumnState) ||
|
|
1086
|
+
currentColumnState.length === 0) {
|
|
1087
|
+
// Fallback pokud getColumnState() nevrátí validní array
|
|
1088
|
+
if (lastKnownColumnState) {
|
|
1089
|
+
return lastKnownColumnState;
|
|
1090
|
+
}
|
|
1091
|
+
const defaultColumns = validColumnDefs.map((col, index) => ({
|
|
1092
|
+
id: col.field,
|
|
1093
|
+
field: col.field,
|
|
1094
|
+
headerName: col.headerName || col.field,
|
|
1095
|
+
originalHeaderName: col.headerName || col.field,
|
|
1096
|
+
width: col.width || 100,
|
|
1097
|
+
originalWidth: col.width || 100,
|
|
1098
|
+
visible: !col.hide,
|
|
1099
|
+
order: index,
|
|
1100
|
+
originalOrder: index, // Původní pořadí z columnDefs
|
|
1101
|
+
}));
|
|
1102
|
+
// POZN: Neukládáme do state, aby nedocházelo k infinite loop
|
|
1103
|
+
return defaultColumns;
|
|
1104
|
+
}
|
|
1105
|
+
const result = currentColumnState.map((columnStateItem, index) => {
|
|
1106
|
+
// Najdeme odpovídající column definition podle colId (zohledníme field i colId)
|
|
1107
|
+
const colDef = validColumnDefs.find((cd) => cd.field === columnStateItem.colId ||
|
|
1108
|
+
cd.colId === columnStateItem.colId) || {};
|
|
1109
|
+
// Pokusíme se najít saved hodnotu pro headerName
|
|
1110
|
+
const savedField = savedFields?.records?.find((sf) => sf.fieldName === columnStateItem.colId);
|
|
1111
|
+
// Najdeme původní index z columnDefs
|
|
1112
|
+
const originalIndex = validColumnDefs.findIndex((cd) => (cd.field || cd.colId) === columnStateItem.colId);
|
|
1113
|
+
return {
|
|
1114
|
+
id: columnStateItem.colId,
|
|
1115
|
+
field: columnStateItem.colId,
|
|
1116
|
+
headerName: savedField?.headerName ||
|
|
1117
|
+
colDef.headerName ||
|
|
1118
|
+
columnStateItem.colId,
|
|
1119
|
+
// Původní headerName z columnDefs (ne z uložených uživatelských preferencí)
|
|
1120
|
+
originalHeaderName: colDef.headerName || columnStateItem.colId,
|
|
1121
|
+
width: columnStateItem.width || colDef.width || 100,
|
|
1122
|
+
originalWidth: colDef.width || 100,
|
|
1123
|
+
visible: !columnStateItem.hide,
|
|
1124
|
+
order: index,
|
|
1125
|
+
originalOrder: originalIndex !== -1 ? originalIndex : index, // Původní pořadí z columnDefs
|
|
1126
|
+
};
|
|
1127
|
+
});
|
|
1128
|
+
// POZN: Neukládáme do state, aby nedocházelo k infinite loop
|
|
1129
|
+
// Tato aktualizace proběhne v useEffect
|
|
1130
|
+
return result;
|
|
342
1131
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
headerName: col.headerName || col.field,
|
|
357
|
-
originalHeaderName: col.headerName || col.field,
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
console.error('[GridLayout] Error in getCurrentColumnsForEditor:', error);
|
|
1134
|
+
// Použijeme lastKnownColumnState, pokud existuje
|
|
1135
|
+
if (lastKnownColumnState) {
|
|
1136
|
+
return lastKnownColumnState;
|
|
1137
|
+
}
|
|
1138
|
+
// Poslední fallback na columnDefs
|
|
1139
|
+
const defaultColumns = validColumnDefs.map((col, index) => ({
|
|
1140
|
+
id: col.field || col.colId,
|
|
1141
|
+
field: col.field || col.colId,
|
|
1142
|
+
headerName: col.headerName || col.field || col.colId,
|
|
1143
|
+
// Vždy používáme původní definici jako referenci pro porovnání
|
|
1144
|
+
originalHeaderName: col.headerName || col.field || col.colId,
|
|
358
1145
|
width: col.width || 100,
|
|
359
1146
|
originalWidth: col.width || 100,
|
|
360
1147
|
visible: !col.hide,
|
|
361
|
-
order: index
|
|
1148
|
+
order: index,
|
|
1149
|
+
originalOrder: index, // Původní pořadí z columnDefs
|
|
362
1150
|
}));
|
|
1151
|
+
// POZN: Neukládáme do state, aby nedocházelo k infinite loop
|
|
1152
|
+
return defaultColumns;
|
|
363
1153
|
}
|
|
364
|
-
|
|
365
|
-
// Najdeme odpovídající column definition podle colId
|
|
366
|
-
const colDef = validColumnDefs.find(cd => cd.field === columnStateItem.colId) || {};
|
|
367
|
-
// Pokusíme se najít saved hodnotu pro headerName
|
|
368
|
-
const savedField = savedFields?.records?.find(sf => sf.fieldName === columnStateItem.colId);
|
|
369
|
-
return {
|
|
370
|
-
id: columnStateItem.colId,
|
|
371
|
-
field: columnStateItem.colId,
|
|
372
|
-
headerName: savedField?.headerName || colDef.headerName || columnStateItem.colId,
|
|
373
|
-
originalHeaderName: colDef.headerName || columnStateItem.colId,
|
|
374
|
-
width: columnStateItem.width || colDef.width || 100,
|
|
375
|
-
originalWidth: colDef.width || 100,
|
|
376
|
-
visible: !columnStateItem.hide,
|
|
377
|
-
order: index
|
|
378
|
-
};
|
|
379
|
-
});
|
|
380
|
-
console.log("✅ Using AG-Grid state - result:", result.map(r => ({
|
|
381
|
-
field: r.field,
|
|
382
|
-
headerName: r.headerName,
|
|
383
|
-
width: r.width,
|
|
384
|
-
visible: r.visible
|
|
385
|
-
})));
|
|
386
|
-
return result;
|
|
387
|
-
}, [columnDefs, savedFields]);
|
|
1154
|
+
}, [columnDefs, savedFields, lastKnownColumnState]);
|
|
388
1155
|
/**
|
|
389
1156
|
* Otevře Column Editor modal
|
|
390
1157
|
*/
|
|
391
1158
|
const openColumnEditor = useCallback(() => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
isInitialized,
|
|
395
|
-
columnDefsCount: columnDefs?.length || 0
|
|
396
|
-
});
|
|
1159
|
+
// Před otevřením editoru si pouze zaznamenáme, že chceme aktualizovat stav sloupců
|
|
1160
|
+
// Samotná aktualizace proběhne v useEffect, ne během renderu
|
|
397
1161
|
setIsColumnEditorOpen(true);
|
|
398
|
-
}, [
|
|
1162
|
+
}, []);
|
|
399
1163
|
/**
|
|
400
1164
|
* Zavře Column Editor modal
|
|
401
1165
|
*/
|
|
@@ -406,72 +1170,126 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
406
1170
|
* Uloží změny z Column Editoru
|
|
407
1171
|
* @param {Array} editedColumns - Editované sloupce z modalu
|
|
408
1172
|
*/
|
|
409
|
-
const saveColumnEditorChanges =
|
|
410
|
-
|
|
411
|
-
if (!enabled || !columnApiRef.current)
|
|
1173
|
+
const saveColumnEditorChanges = async (editedColumns) => {
|
|
1174
|
+
if (!enabled)
|
|
412
1175
|
return;
|
|
413
1176
|
try {
|
|
414
1177
|
setIsLoading(true);
|
|
1178
|
+
setIsSaving(true);
|
|
415
1179
|
// Filtrujeme pouze validní sloupce (s definovaným field)
|
|
416
|
-
const validColumns = editedColumns.filter(col => col.field && col.field !== undefined);
|
|
417
|
-
//
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
sortIndex: null
|
|
425
|
-
}));
|
|
426
|
-
// Aplikujeme změny na AG-Grid
|
|
427
|
-
columnApiRef.current.applyColumnState({
|
|
428
|
-
state: columnState,
|
|
429
|
-
applyOrder: true,
|
|
430
|
-
defaultState: {
|
|
431
|
-
sort: null,
|
|
432
|
-
sortIndex: null,
|
|
433
|
-
pivot: null,
|
|
434
|
-
rowGroup: null
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
// Připravíme UserFields pro save podle C# SaveUserFieldModel struktury
|
|
438
|
-
const userFields = validColumns.map((col, index) => ({
|
|
1180
|
+
const validColumns = editedColumns.filter((col) => col.field && col.field !== undefined);
|
|
1181
|
+
// Připravíme UserFields pro API podle C# SaveUserFieldModel struktury
|
|
1182
|
+
const completeUserFields = validColumns.map((col, index) => ({
|
|
1183
|
+
Id: 0, // Nové pole má ID = 0, existující budou mít správné ID z API
|
|
1184
|
+
UserKey: userKey,
|
|
1185
|
+
ApplicationName: applicationName,
|
|
1186
|
+
GridName: gridName,
|
|
1187
|
+
FilterName: filterName || null,
|
|
439
1188
|
FieldName: col.field,
|
|
440
|
-
HeaderName: col.headerName || col.field,
|
|
1189
|
+
HeaderName: col.headerName || col.field,
|
|
441
1190
|
Order: index, // Pořadí podle pozice v editedColumns
|
|
442
1191
|
Show: col.visible,
|
|
443
|
-
Width: col.width
|
|
1192
|
+
Width: col.width != null ? col.width : null, // Zachováváme i nulovou šířku
|
|
1193
|
+
System: false,
|
|
444
1194
|
}));
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
|
|
1195
|
+
// Uložení do API
|
|
1196
|
+
gridLayoutApi
|
|
1197
|
+
.saveGridLayout(completeUserFields)
|
|
1198
|
+
.then((result) => {
|
|
448
1199
|
if (result.success) {
|
|
449
1200
|
setHasUnsavedChanges(false);
|
|
450
|
-
//
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
1201
|
+
// Znovunačtení layoutu z API a aplikování na grid
|
|
1202
|
+
reloadLayout().then(async () => {
|
|
1203
|
+
// Po reloadLayout() počkáme na aktualizaci savedFields a aplikujeme layout
|
|
1204
|
+
try {
|
|
1205
|
+
// Znovu načteme fieldy pro zajištění synchronizace
|
|
1206
|
+
const freshFields = await refetchFields();
|
|
1207
|
+
setTimeout(() => {
|
|
1208
|
+
if (gridApiRef.current && freshFields?.data?.records) {
|
|
1209
|
+
// Aplikujeme layout s novými daty
|
|
1210
|
+
const columnState = gridLayoutApi.transformFieldsToColumnState(freshFields.data.records, columnDefs);
|
|
1211
|
+
if (columnState && columnState.length > 0) {
|
|
1212
|
+
// Najdeme správné API pro applyColumnState
|
|
1213
|
+
let applyColumnStateApi = null;
|
|
1214
|
+
if (gridApiRef.current?.applyColumnState) {
|
|
1215
|
+
applyColumnStateApi = gridApiRef.current;
|
|
1216
|
+
}
|
|
1217
|
+
else if (columnApiRef.current?.applyColumnState) {
|
|
1218
|
+
applyColumnStateApi = columnApiRef.current;
|
|
1219
|
+
}
|
|
1220
|
+
if (applyColumnStateApi) {
|
|
1221
|
+
applyColumnStateApi.applyColumnState({
|
|
1222
|
+
state: columnState,
|
|
1223
|
+
applyOrder: true,
|
|
1224
|
+
defaultState: {
|
|
1225
|
+
sort: null,
|
|
1226
|
+
sortIndex: null,
|
|
1227
|
+
pivot: null,
|
|
1228
|
+
rowGroup: null,
|
|
1229
|
+
},
|
|
1230
|
+
});
|
|
1231
|
+
// Aktualizujeme headerName pro každý sloupec
|
|
1232
|
+
const headerNameMap = new Map();
|
|
1233
|
+
freshFields.data.records.forEach((field) => {
|
|
1234
|
+
if (field.fieldName && field.headerName) {
|
|
1235
|
+
headerNameMap.set(field.fieldName, field.headerName);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
// Aplikujeme nové headerName hodnoty přes DOM
|
|
1239
|
+
setTimeout(() => {
|
|
1240
|
+
try {
|
|
1241
|
+
const headerCells = document.querySelectorAll('.ag-header-cell');
|
|
1242
|
+
headerCells.forEach((headerCell) => {
|
|
1243
|
+
const colId = headerCell.getAttribute('col-id');
|
|
1244
|
+
if (colId && headerNameMap.has(colId)) {
|
|
1245
|
+
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
1246
|
+
if (headerTextEl) {
|
|
1247
|
+
headerTextEl.textContent = headerNameMap.get(colId);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
// Vynutíme refresh hlavičky
|
|
1252
|
+
if (gridApiRef.current?.refreshHeader) {
|
|
1253
|
+
gridApiRef.current.refreshHeader();
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
catch (headerError) {
|
|
1257
|
+
console.error('[GridLayout] Error updating headers:', headerError);
|
|
1258
|
+
}
|
|
1259
|
+
}, 50);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}, 150);
|
|
1264
|
+
}
|
|
1265
|
+
catch (refreshError) {
|
|
1266
|
+
console.error('[GridLayout] Error in post-save refresh:', refreshError);
|
|
1267
|
+
// Fallback na standardní applySavedLayout
|
|
1268
|
+
setTimeout(() => {
|
|
1269
|
+
if (gridApiRef.current) {
|
|
1270
|
+
applySavedLayout(true);
|
|
1271
|
+
}
|
|
1272
|
+
}, 200);
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
458
1275
|
if (onLayoutSaved) {
|
|
459
|
-
onLayoutSaved(
|
|
1276
|
+
onLayoutSaved(completeUserFields);
|
|
460
1277
|
}
|
|
461
1278
|
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
console.log("✅ Column Editor changes applied successfully");
|
|
1279
|
+
})
|
|
1280
|
+
.catch((error) => {
|
|
1281
|
+
handleError(error, 'při ukládání změn z Column Editoru');
|
|
1282
|
+
});
|
|
467
1283
|
}
|
|
468
1284
|
catch (error) {
|
|
469
|
-
handleError(error, 'při
|
|
1285
|
+
handleError(error, 'při ukládání změn z Column Editoru');
|
|
470
1286
|
}
|
|
471
1287
|
finally {
|
|
472
1288
|
setIsLoading(false);
|
|
1289
|
+
setIsSaving(false);
|
|
473
1290
|
}
|
|
474
|
-
}
|
|
1291
|
+
};
|
|
1292
|
+
//, [enabled, gridLayoutApi, onLayoutSaved, handleError, userKey, applicationName, gridName, filterName, reloadLayout]);
|
|
475
1293
|
// Logika pro zobrazení columnDefs
|
|
476
1294
|
const shouldShowColumns = useMemo(() => {
|
|
477
1295
|
if (!waitForSavedFields)
|
|
@@ -481,34 +1299,108 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
481
1299
|
}, [waitForSavedFields, isFieldsLoading, isLoading]);
|
|
482
1300
|
// Pre-transformované columnDefs podle savedFields pro waitForSavedFields mode
|
|
483
1301
|
const preTransformedColumnDefs = useMemo(() => {
|
|
484
|
-
|
|
485
|
-
|
|
1302
|
+
// Použijeme aktualizované columnDefs ze stableColumnDefsRef pokud existují
|
|
1303
|
+
const columnDefsToUse = stableColumnDefsRef.current || columnDefs;
|
|
1304
|
+
if (!waitForSavedFields ||
|
|
1305
|
+
!savedFields?.records ||
|
|
1306
|
+
!Array.isArray(columnDefsToUse)) {
|
|
1307
|
+
return columnDefsToUse; // Aktualizované columnDefs
|
|
486
1308
|
}
|
|
487
1309
|
// Transformujeme columnDefs podle savedFields PŘED zobrazením gridu
|
|
488
|
-
const columnState = gridLayoutApi.transformFieldsToColumnState(savedFields.records,
|
|
1310
|
+
const columnState = gridLayoutApi.transformFieldsToColumnState(savedFields.records, columnDefsToUse);
|
|
489
1311
|
if (!columnState || columnState.length === 0) {
|
|
490
|
-
return
|
|
1312
|
+
return columnDefsToUse; // Pokud není co transformovat, vrátíme aktualizované
|
|
491
1313
|
}
|
|
492
1314
|
// Vytvoříme mapu pro rychlé vyhledávání
|
|
493
1315
|
const columnStateMap = new Map();
|
|
494
1316
|
columnState.forEach((colState, index) => {
|
|
495
1317
|
columnStateMap.set(colState.colId, { ...colState, __order: index });
|
|
496
1318
|
});
|
|
497
|
-
//
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
1319
|
+
// Vytvoříme mapu fieldName -> headerName z API dat
|
|
1320
|
+
const headerNameMap = new Map();
|
|
1321
|
+
savedFields.records.forEach((field) => {
|
|
1322
|
+
if (field.fieldName && field.headerName) {
|
|
1323
|
+
headerNameMap.set(field.fieldName, field.headerName);
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
// Seřadíme columnDefs podle pořadí z columnState a upravíme headerName podle savedFields
|
|
1327
|
+
const sortedColumnDefs = [...columnDefsToUse]
|
|
1328
|
+
.sort((a, b) => {
|
|
1329
|
+
const fieldA = a.field || a.colId;
|
|
1330
|
+
const fieldB = b.field || b.colId;
|
|
1331
|
+
const aState = columnStateMap.get(fieldA);
|
|
1332
|
+
const bState = columnStateMap.get(fieldB);
|
|
501
1333
|
const aOrder = aState?.__order ?? 999;
|
|
502
1334
|
const bOrder = bState?.__order ?? 999;
|
|
503
1335
|
return aOrder - bOrder;
|
|
504
|
-
})
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
1336
|
+
})
|
|
1337
|
+
.map((colDef) => {
|
|
1338
|
+
const fieldId = colDef.field || colDef.colId;
|
|
1339
|
+
const columnStateItem = columnStateMap.get(fieldId);
|
|
1340
|
+
// Aplikujeme jak headerName tak hide hodnotu z API
|
|
1341
|
+
if (fieldId && (headerNameMap.has(fieldId) || columnStateItem)) {
|
|
1342
|
+
return {
|
|
1343
|
+
...colDef,
|
|
1344
|
+
// Aplikujeme headerName z savedFields
|
|
1345
|
+
...(headerNameMap.has(fieldId) && { headerName: headerNameMap.get(fieldId) }),
|
|
1346
|
+
// Aplikujeme hide hodnotu z columnState (z API show hodnoty)
|
|
1347
|
+
...(columnStateItem && { hide: columnStateItem.hide }),
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
return colDef;
|
|
509
1351
|
});
|
|
510
1352
|
return sortedColumnDefs;
|
|
511
|
-
}, [waitForSavedFields, savedFields?.records, columnDefs, gridLayoutApi]);
|
|
1353
|
+
}, [waitForSavedFields, savedFields?.records, columnDefs, gridLayoutApi, columnDefsVersion]);
|
|
1354
|
+
/**
|
|
1355
|
+
* Handler pro změnu zobrazených dat - aktualizace headerName
|
|
1356
|
+
*/
|
|
1357
|
+
const handleRowDataChanged = useCallback((params) => {
|
|
1358
|
+
// if (!enabled || !savedFields?.records || !isInitialized) return;
|
|
1359
|
+
// console.log('[GridLayout] Row data changed - ensuring header names stay updated');
|
|
1360
|
+
// // Počkáme chvíli, až se vše vyrenderuje, a pak aktualizujeme headerName
|
|
1361
|
+
// setTimeout(() => {
|
|
1362
|
+
// try {
|
|
1363
|
+
// // Vytvoříme mapu fieldName -> headerName z API dat
|
|
1364
|
+
// const headerNameMap = new Map();
|
|
1365
|
+
// savedFields.records.forEach(field => {
|
|
1366
|
+
// if (field.fieldName && field.headerName) {
|
|
1367
|
+
// headerNameMap.set(field.fieldName, field.headerName);
|
|
1368
|
+
// }
|
|
1369
|
+
// });
|
|
1370
|
+
// // Najdeme všechny hlavičky sloupců v DOM
|
|
1371
|
+
// const headerCells = document.querySelectorAll('.ag-header-cell');
|
|
1372
|
+
// console.log('[GridLayout] Found header cells after data change:', headerCells.length);
|
|
1373
|
+
// // Aktualizujeme texty hlaviček
|
|
1374
|
+
// headerCells.forEach(headerCell => {
|
|
1375
|
+
// try {
|
|
1376
|
+
// // Získáme ID sloupce z DOM atributů
|
|
1377
|
+
// const colId = headerCell.getAttribute('col-id');
|
|
1378
|
+
// if (colId && headerNameMap.has(colId)) {
|
|
1379
|
+
// // Najdeme element s textem hlavičky
|
|
1380
|
+
// const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
1381
|
+
// if (headerTextEl) {
|
|
1382
|
+
// const newHeaderName = headerNameMap.get(colId);
|
|
1383
|
+
// const currentText = headerTextEl.textContent;
|
|
1384
|
+
// if (currentText !== newHeaderName) {
|
|
1385
|
+
// console.log(`[GridLayout] Data change update: Column '${colId}' header from '${currentText}' to '${newHeaderName}'`);
|
|
1386
|
+
// headerTextEl.textContent = newHeaderName;
|
|
1387
|
+
// }
|
|
1388
|
+
// }
|
|
1389
|
+
// }
|
|
1390
|
+
// } catch (cellError) {
|
|
1391
|
+
// console.error('[GridLayout] Error updating header cell after data change:', cellError);
|
|
1392
|
+
// }
|
|
1393
|
+
// });
|
|
1394
|
+
// // Zkusíme vynutit překreslení hlavičky
|
|
1395
|
+
// if (typeof gridApiRef.current.refreshHeader === 'function') {
|
|
1396
|
+
// console.log('[GridLayout] Forcing header refresh after data change');
|
|
1397
|
+
// gridApiRef.current.refreshHeader();
|
|
1398
|
+
// }
|
|
1399
|
+
// } catch (error) {
|
|
1400
|
+
// console.error('[GridLayout] Error updating headers after data change:', error);
|
|
1401
|
+
// }
|
|
1402
|
+
// }, 100);
|
|
1403
|
+
}, [enabled, savedFields, isInitialized]);
|
|
512
1404
|
return {
|
|
513
1405
|
// State
|
|
514
1406
|
isLoading: isLoading || isFieldsLoading,
|
|
@@ -516,6 +1408,7 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
516
1408
|
error,
|
|
517
1409
|
hasUnsavedChanges,
|
|
518
1410
|
isInitialized,
|
|
1411
|
+
isGridReady, // Nový stav pro rychlejší aktivaci event handlerů
|
|
519
1412
|
shouldShowColumns, // Nové pole pro řízení zobrazení columnDefs
|
|
520
1413
|
preTransformedColumnDefs, // Pre-transformované columnDefs pro waitForSavedFields
|
|
521
1414
|
// AG-Grid event handlers
|
|
@@ -525,6 +1418,7 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
525
1418
|
onColumnResized: handleColumnResized,
|
|
526
1419
|
onColumnVisible: handleColumnVisible,
|
|
527
1420
|
onColumnPinned: handleColumnPinned,
|
|
1421
|
+
onRowDataChanged: handleRowDataChanged, // Nový handler pro změnu dat
|
|
528
1422
|
// Manual actions
|
|
529
1423
|
saveLayout,
|
|
530
1424
|
resetToDefault,
|
|
@@ -539,7 +1433,7 @@ onLayoutLoaded, onLayoutSaved, onError }) => {
|
|
|
539
1433
|
savedFields: savedFields?.records || [],
|
|
540
1434
|
columnDefs, // Původní columnDefs pro Column Editor
|
|
541
1435
|
// Utils
|
|
542
|
-
gridLayoutApi
|
|
1436
|
+
gridLayoutApi,
|
|
543
1437
|
};
|
|
544
1438
|
};
|
|
545
1439
|
export default useGridLayout;
|