# AdvancedTable - Plan de Mejoras y Corrección de Bugs > **Fecha:** 2025-12-17 > **Estado:** ✅ COMPLETADO > **Consensuado:** Sí --- ## Contexto Primera prueba real de AdvancedTable en zsimifactu (WHO panel) reveló 2 bugs críticos. Además, el widget Table existente tiene funcionalidades que AdvancedTable debería incorporar. **Decisión:** Incorporar funcionalidades de Table mientras corregimos los bugs. --- ## Bugs Detectados | # | Bug | Severidad | Descripción | |---|-----|-----------|-------------| | 1 | **Navegación teclado** | ALTA | Flechas ↑↓←→ no mueven selección | | 2 | **Sorting** | MEDIA | Click en header no reordena filas (solo cambia estado visual) | --- ## Funcionalidades a Incorporar de Table ### Fase A: Multi-Select + Search (~160 LOC) **Origen:** `src/widgets/table/state.zig`, `src/widgets/table/keyboard.zig` #### A1. Multi-Row Selection ```zig // Añadir a AdvancedTableState selected_rows: [128]u8 = [_]u8{0} ** 128, // Bit array (1024 rows) selection_anchor: i32 = -1, // Para Shift+click // Funciones a añadir pub fn isRowSelected(row: usize) bool pub fn addRowToSelection(row: usize) void pub fn removeRowFromSelection(row: usize) void pub fn toggleRowSelection(row: usize) void pub fn clearRowSelection() void pub fn selectAllRows() void pub fn selectRowRange(from: usize, to: usize) void pub fn getSelectedRowCount() usize pub fn getSelectedRows(buffer: []usize) usize ``` **Keyboard:** - Ctrl+click → toggle individual row - Shift+click → select range from anchor - Ctrl+A → select all (si config.allow_multi_select) **LOC estimadas:** ~90 #### A2. Incremental Search (Type-to-Search) ```zig // Añadir a AdvancedTableState search_buffer: [64]u8 = [_]u8{0} ** 64, search_len: usize = 0, search_last_time: u64 = 0, search_timeout_ms: u64 = 1000, // Reset después de 1s // Funciones a añadir pub fn addSearchChar(char: u8, current_time: u64) []const u8 pub fn getSearchTerm() []const u8 pub fn clearSearch() void ``` **Helper:** ```zig // En advanced_table.zig o utils pub fn startsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool ``` **Comportamiento:** - Usuario teclea sin Ctrl/Alt → acumula en search_buffer - Salta a primera fila cuya columna 0 empieza con el término - Timeout 1s → reset buffer **LOC estimadas:** ~70 ### Fase B: Cell Validation (~80 LOC) **Origen:** `src/widgets/table/state.zig` ```zig // Añadir a AdvancedTableState validation_errors: [256]u32 = [_]u32{0xFFFFFFFF} ** 256, // cell IDs validation_error_count: usize = 0, last_validation_message: [128]u8 = [_]u8{0} ** 128, last_validation_message_len: usize = 0, // Funciones a añadir pub fn hasCellError(row: usize, col: usize) bool pub fn addCellError(row: usize, col: usize, message: []const u8) void pub fn clearCellError(row: usize, col: usize) void pub fn clearAllCellErrors() void pub fn hasAnyCellErrors() bool pub fn getLastValidationMessage() []const u8 ``` **Visual:** - Celdas con error: borde rojo + fondo rojizo **LOC estimadas:** ~80 --- ## Plan de Implementación ### Paso 1: Fase A1 - Multi-Row Selection **Archivos a modificar:** - `src/widgets/advanced_table/state.zig` - Añadir campos y funciones - `src/widgets/advanced_table/advanced_table.zig` - Keyboard handling - `src/widgets/advanced_table/types.zig` - Añadir config.allow_multi_select **Tests a añadir:** - `test "AdvancedTableState multi-row selection"` ### Paso 2: Fase A2 - Incremental Search **Archivos a modificar:** - `src/widgets/advanced_table/state.zig` - Añadir campos y funciones - `src/widgets/advanced_table/advanced_table.zig` - handleKeyboard **Tests a añadir:** - `test "AdvancedTableState incremental search"` - `test "startsWithIgnoreCase"` ### Paso 3: Bug 1 - Navegación Teclado **Diagnóstico durante Fase A:** Al incorporar el código de keyboard handling de Table, comparar línea por línea con AdvancedTable para identificar por qué no funciona. **Posibles causas:** 1. `has_focus` no es true cuando debería 2. `ctx.input.keyPressed()` no detecta las teclas 3. `config.keyboard_nav` es false 4. `handleKeyboard()` no se está llamando **Verificación:** ```zig // Debug temporal if (has_focus) { std.debug.print("AdvancedTable has_focus=true, keyboard_nav={}\n", .{config.keyboard_nav}); } ``` ### Paso 4: Bug 2 - Sorting Real **Problema:** `toggleSort()` solo cambia `sort_column` y `sort_direction` pero NO reordena `state.rows`. **Solución:** ```zig // En advanced_table.zig, después de toggleSort() if (result.sort_changed) { sortRows(table_state, table_schema, result.sort_column.?, result.sort_direction); } fn sortRows( state: *AdvancedTableState, schema: *const TableSchema, col_idx: usize, direction: SortDirection, ) void { if (direction == .none) { // Restaurar orden original si guardado return; } const col_name = schema.columns[col_idx].name; // Sort using std.mem.sort with comparator std.mem.sort(Row, state.rows.items, .{}, struct { fn lessThan(context: anytype, a: Row, b: Row) bool { const val_a = a.get(context.col_name); const val_b = b.get(context.col_name); return val_a.compare(val_b) < 0; } }.lessThan); // Si descending, invertir if (direction == .descending) { std.mem.reverse(Row, state.rows.items); } } ``` **Nota:** Hay que sincronizar los state maps (dirty_rows, new_rows, etc.) después del sort. ### Paso 5: Fase B - Cell Validation (Opcional) Implementar si hay tiempo después de resolver bugs. --- ## Orden de Ejecución ``` ┌─────────────────────────────────────────┐ │ 1. Fase A1: Multi-Row Selection │ │ - Copiar código de table/state.zig │ │ - Adaptar a AdvancedTableState │ │ - Añadir keyboard handling │ ├─────────────────────────────────────────┤ │ 2. Fase A2: Incremental Search │ │ - Copiar código de table/state.zig │ │ - Añadir startsWithIgnoreCase │ │ - Integrar en handleKeyboard │ ├─────────────────────────────────────────┤ │ 3. Bug 1: Diagnosticar Teclado │ │ - Comparar con Table funcional │ │ - Añadir debug prints │ │ - Identificar causa raíz │ ├─────────────────────────────────────────┤ │ 4. Bug 2: Sorting Real │ │ - Implementar sortRows() │ │ - Sincronizar state maps │ │ - Guardar/restaurar orden original │ ├─────────────────────────────────────────┤ │ 5. Tests y Verificación │ │ - Todos los tests pasan │ │ - Probar en zsimifactu │ └─────────────────────────────────────────┘ ``` --- ## Estimación | Tarea | LOC | Tiempo estimado | |-------|-----|-----------------| | Fase A1 (multi-select) | ~90 | - | | Fase A2 (search) | ~70 | - | | Bug 1 (teclado) | ~20 | - | | Bug 2 (sorting) | ~60 | - | | Tests | ~40 | - | | **Total** | **~280** | - | --- ## Criterios de Éxito 1. ✅ Flechas ↑↓←→ mueven selección en AdvancedTable 2. ✅ Click en header ordena las filas visualmente 3. ✅ Ctrl+click selecciona múltiples filas 4. ✅ Shift+click selecciona rango 5. ✅ Ctrl+A selecciona todas las filas 6. ✅ Teclear busca en primera columna 7. ✅ Todos los tests pasan 8. ✅ zsimifactu WHO panel funciona correctamente --- ## Resultados de Implementación ### Bug 1: Navegación teclado - ✅ CORREGIDO **Causa raíz:** AdvancedTable usaba `ctx.input.keyPressed()` que solo detecta el primer press, no key repeats. Table usa `ctx.input.navKeyPressed()` que incluye key repeats. **Solución:** Cambiar `handleKeyboard()` para usar `navKeyPressed()` con switch exhaustivo. ### Bug 2: Sorting real - ✅ CORREGIDO **Causa raíz:** `toggleSort()` solo cambiaba el estado visual pero no reordenaba `state.rows`. **Solución:** Implementar función `sortRows()` con bubble sort estable + sincronización de state maps. ### Fase A1: Multi-Row Selection - ✅ IMPLEMENTADO **Añadido a AdvancedTableState:** - `selected_rows: [128]u8` - bit array para 1024 filas - `selection_anchor: i32` - para Shift+click - Funciones: `isRowSelected`, `addRowToSelection`, `removeRowFromSelection`, `toggleRowSelection`, `clearRowSelection`, `selectAllRows`, `selectRowRange`, `getSelectedRowCount`, `getSelectedRows`, `selectSingleRow` ### Fase A2: Incremental Search - ✅ IMPLEMENTADO **Añadido a AdvancedTableState:** - `search_buffer: [64]u8` - buffer de búsqueda - `search_len`, `search_last_time`, `search_timeout_ms` (1000ms) - Funciones: `addSearchChar`, `getSearchTerm`, `clearSearch` - Helper: `startsWithIgnoreCase()` ### Fase B: Cell Validation - ✅ IMPLEMENTADO **Añadido a AdvancedTableState:** - `cell_validation_errors: [256]u32` - cell IDs con error - `cell_validation_error_count`, `last_validation_message` - Funciones: `hasCellError`, `addCellError`, `clearCellError`, `clearAllCellErrors`, `hasAnyCellErrors`, `getLastValidationMessage` ### Tests añadidos - `test "AdvancedTableState multi-row selection"` - `test "AdvancedTableState select row range"` - `test "AdvancedTableState incremental search"` - `test "AdvancedTableState cell validation"` - `test "CellValue compare"` - `test "startsWithIgnoreCase"` **Total tests:** 379 (todos pasan) ### LOC añadidas | Archivo | LOC | |---------|-----| | `state.zig` | ~200 | | `advanced_table.zig` | ~100 | | `types.zig` | ~45 | | **Total** | ~345 | --- *Plan creado por: Claude Code (Opus 4.5)* *Fecha: 2025-12-17* *Estado: ✅ COMPLETADO*