|
60 | 60 | let configDebounceTimer = null; // Debounce timer for config updates |
61 | 61 | let renderInProgress = false; // Prevent overlapping renders |
62 | 62 | const CONFIG_DEBOUNCE_MS = 300; // 300ms debounce delay |
63 | | - let currentTasks = []; // Store tasks for expected progress markers |
| 63 | + let allTasks = []; // Store ALL tasks from backend (unfiltered) |
| 64 | + let currentTasks = []; // Store filtered tasks for current display |
| 65 | + let lastGanttConfig = null; // Store last config for filter re-renders (#51) |
64 | 66 | let activeFilters = ['all']; // Track active filter buttons (#51) |
65 | 67 |
|
66 | 68 | // Zoom State |
|
864 | 866 |
|
865 | 867 | // ===== GANTT RENDERING ===== |
866 | 868 |
|
867 | | - function renderGantt(tasks, config) { |
868 | | - console.log(`Rendering Gantt with ${tasks.length} tasks`); |
869 | | - // console.log('Gantt config:', JSON.stringify(config, null, 2)); |
| 869 | + function renderGantt(tasks, config, isFilterRerender = false) { |
| 870 | + // Store all tasks on initial load (not filter re-renders) |
| 871 | + if (!isFilterRerender) { |
| 872 | + allTasks = tasks; |
| 873 | + lastGanttConfig = config; |
| 874 | + } |
| 875 | + |
| 876 | + // Apply status filters to get visible tasks (#51) |
| 877 | + const filteredTasks = filterTasksByStatus(tasks); |
| 878 | + console.log(`Rendering Gantt with ${filteredTasks.length}/${tasks.length} tasks (filtered)`); |
| 879 | + |
| 880 | + // Store filtered tasks for expected progress markers |
| 881 | + currentTasks = filteredTasks; |
870 | 882 |
|
871 | | - // Store tasks for expected progress markers |
872 | | - currentTasks = tasks; |
| 883 | + // Handle empty filtered result |
| 884 | + if (filteredTasks.length === 0) { |
| 885 | + const container = document.getElementById('gantt-container'); |
| 886 | + container.innerHTML = ''; |
| 887 | + ganttInstance = null; |
| 888 | + updateFilterEmptyState(0); |
| 889 | + return; |
| 890 | + } |
| 891 | + updateFilterEmptyState(filteredTasks.length); |
873 | 892 |
|
874 | 893 | const container = document.getElementById('gantt-container'); |
875 | 894 |
|
|
966 | 985 | ensureStackingOrder(); // Ensure today line and markers on top (#57) |
967 | 986 | ensureEdgeToEdgeContent(); // Check edge-to-edge, zoom if needed (#21) |
968 | 987 | updateZoomIndicator(); // Update indicator for new view's zoom |
969 | | - applyTaskFilters(); // Re-apply task filters after view change (#51) |
970 | 988 | }); |
971 | 989 | } |
972 | 990 | }; |
|
984 | 1002 |
|
985 | 1003 | // Initialize Frappe Gantt |
986 | 1004 | try { |
987 | | - ganttInstance = new Gantt('#gantt-svg', tasks, ganttOptions); |
988 | | - console.log(`Gantt chart created successfully with ${tasks.length} tasks`); |
| 1005 | + ganttInstance = new Gantt('#gantt-svg', filteredTasks, ganttOptions); |
| 1006 | + console.log(`Gantt chart created successfully with ${filteredTasks.length} tasks`); |
989 | 1007 |
|
990 | 1008 | // Debug: Log gantt instance date boundaries |
991 | 1009 | console.log('Gantt date debug:', { |
|
1213 | 1231 | addCompletionIndicators(); // Add checkmarks to 100% tasks (#31) |
1214 | 1232 | ensureStackingOrder(); // Ensure today line and markers on top (#57) |
1215 | 1233 | ensureEdgeToEdgeContent(); // Zoom if needed to fill viewport (#21) |
1216 | | - applyTaskFilters(); // Apply task filters after render (#51) |
1217 | 1234 |
|
1218 | 1235 | // Restore view state if we have saved state from config update |
1219 | 1236 | if (window._ganttRestoreState) { |
|
2318 | 2335 | } |
2319 | 2336 |
|
2320 | 2337 | /** |
2321 | | - * Apply active filters to hide/show task bars in DOM. |
2322 | | - * Uses display:none on bar-wrapper elements. |
| 2338 | + * Filter tasks array based on active status filters. |
| 2339 | + * @param {Array} tasks - Array of task objects |
| 2340 | + * @returns {Array} Filtered tasks matching active filters |
2323 | 2341 | */ |
2324 | | - function applyTaskFilters() { |
2325 | | - const barWrappers = document.querySelectorAll('.gantt .bar-wrapper'); |
2326 | | - let visibleCount = 0; |
2327 | | - |
2328 | | - barWrappers.forEach(wrapper => { |
2329 | | - const taskId = wrapper.getAttribute('data-id'); |
2330 | | - const task = currentTasks.find(t => t.id === taskId); |
2331 | | - if (!task) return; |
| 2342 | + function filterTasksByStatus(tasks) { |
| 2343 | + // If 'all' is active, return all tasks |
| 2344 | + if (activeFilters.includes('all')) { |
| 2345 | + return tasks; |
| 2346 | + } |
2332 | 2347 |
|
| 2348 | + return tasks.filter(task => { |
2333 | 2349 | const taskStatuses = getTaskStatuses(task); |
2334 | | - |
2335 | | - // Show if 'all' is active OR any of task's statuses match active filters |
2336 | | - const shouldShow = activeFilters.includes('all') || |
2337 | | - taskStatuses.some(status => activeFilters.includes(status)); |
2338 | | - |
2339 | | - wrapper.style.display = shouldShow ? '' : 'none'; |
2340 | | - if (shouldShow) visibleCount++; |
| 2350 | + // Show if any of task's statuses match active filters (OR logic) |
| 2351 | + return taskStatuses.some(status => activeFilters.includes(status)); |
2341 | 2352 | }); |
| 2353 | + } |
| 2354 | + |
| 2355 | + /** |
| 2356 | + * Apply active filters by re-rendering the Gantt chart. |
| 2357 | + * This properly resizes the chart container. |
| 2358 | + */ |
| 2359 | + function applyTaskFilters() { |
| 2360 | + if (!allTasks.length || !lastGanttConfig) { |
| 2361 | + console.log('No tasks or config available for filter re-render'); |
| 2362 | + return; |
| 2363 | + } |
2342 | 2364 |
|
2343 | | - // Update empty state if needed |
2344 | | - updateFilterEmptyState(visibleCount); |
| 2365 | + // Re-render with filtered tasks |
| 2366 | + renderGantt(allTasks, lastGanttConfig, true); |
2345 | 2367 | } |
2346 | 2368 |
|
2347 | 2369 | /** |
|
0 commit comments