documentar aprendizajes de Alpha para RUMBO: utilidades, patrones, arquitectura, lecciones
This commit is contained in:
@@ -0,0 +1,258 @@
|
|||||||
|
# Alpha → RUMBO — Decisiones y Aprendizajes
|
||||||
|
|
||||||
|
> Documento vivo. Cada patrón, utilidad y decisión de Alpha que se reutilizará en RUMBO.
|
||||||
|
> K-20: Documentar learnings → feed a RUMBO.
|
||||||
|
|
||||||
|
**Actualizado**: 2026-05-26
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Utilidades TypeScript
|
||||||
|
|
||||||
|
### 1.1 Limpieza de HTML (`clean-html.ts`)
|
||||||
|
|
||||||
|
**Problema**: KAPPA devuelve descripciones con HTML inline (`<p>`, `<span style="...">`, `<strong>`). RUMBO recibirá contenido similar de múltiples fuentes (actas .docx, correos, web).
|
||||||
|
|
||||||
|
**Solución**:
|
||||||
|
```typescript
|
||||||
|
// src/services/clean-html.ts
|
||||||
|
export function stripHtml(html: string): string {
|
||||||
|
return html
|
||||||
|
.replace(/<[^>]*>/g, '') // eliminar tags
|
||||||
|
.replace(/ /g, ' ') // entidades HTML
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
.replace(/\s+/g, ' ') // normalizar espacios
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dónde usarlo en RUMBO**:
|
||||||
|
- Pipeline de ingesta: después de parsear .docx, .html, o APIs que devuelvan HTML
|
||||||
|
- Antes de enviar texto a la IA (DeepSeek) para ahorrar tokens
|
||||||
|
- Antes de guardar en SQLite/Turso
|
||||||
|
|
||||||
|
**Alternativas evaluadas**: DOMParser (requiere DOM, no disponible en Node/SSR), cheerio (dependencia extra). Regex simple es suficiente para el 95% de los casos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.2 Puente Tauri ↔ Frontend (`tauri-db.ts`)
|
||||||
|
|
||||||
|
**Problema**: `invoke()` de Tauri es tipado como `Promise<any>`. Sin tipos, cada llamada requiere casting manual.
|
||||||
|
|
||||||
|
**Solución**: Un archivo de servicio que envuelve todos los comandos Tauri con tipos explícitos:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const tauriDb = {
|
||||||
|
getUsers(): Promise<AlphaUserRecord[]> {
|
||||||
|
return invoke('get_users')
|
||||||
|
},
|
||||||
|
saveUser(user: AlphaUserRecord): Promise<number> {
|
||||||
|
return invoke('save_user', { user })
|
||||||
|
},
|
||||||
|
// ... todos los comandos tipados
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Por qué sirve para RUMBO**:
|
||||||
|
- Mismo patrón con los comandos de transcripción, documentos, etc.
|
||||||
|
- Un solo archivo = una sola fuente de verdad de la API Rust↔TS
|
||||||
|
- Facilita el autocompletado en el IDE
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.3 Store Pattern (Pinia + sync bidireccional)
|
||||||
|
|
||||||
|
**Problema**: Datos vienen de KAPPA (remoto) pero se extienden localmente (Turso). Hay que mergear ambas fuentes.
|
||||||
|
|
||||||
|
**Solución en `users.ts`**:
|
||||||
|
```typescript
|
||||||
|
async function fetchAll() {
|
||||||
|
const [rawUsers, page1, localUsers, localCells] = await Promise.all([
|
||||||
|
kappa.getUsers(), // remoto
|
||||||
|
kappa.getEmployees(1), // remoto
|
||||||
|
tauriDb.getUsers(), // local (Turso)
|
||||||
|
tauriDb.getCells(), // local (Turso)
|
||||||
|
])
|
||||||
|
|
||||||
|
const localMap = new Map(localUsers.map(u => [u.id, u]))
|
||||||
|
users.value = buildUsers(rawUsers, employees.value, localMap)
|
||||||
|
|
||||||
|
syncUsersToTurso(users.value) // persistir merge
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Patrón**: Fetch remoto + fetch local → merge (local pisa remoto para campos extendidos) → sync inverso.
|
||||||
|
|
||||||
|
**Para RUMBO**: Mismo patrón con:
|
||||||
|
- KAPPA API (remoto) + SQLite (local) para HUs
|
||||||
|
- OpenRouter (remoto) + caché local para respuestas IA
|
||||||
|
- Archivos locales + metadatos en SQLite
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.4 AG Grid + Tema Shadcn
|
||||||
|
|
||||||
|
**Problema**: Necesitábamos una tabla potente (filtros, ordenamiento, paginación) pero con el estilo visual de shadcn-vue.
|
||||||
|
|
||||||
|
**Solución**: AG Grid Community + archivo CSS que mapea variables shadcn:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* src/assets/ag-grid-alpha.css */
|
||||||
|
.ag-theme-alpha-shadcn {
|
||||||
|
--ag-background-color: var(--card);
|
||||||
|
--ag-header-background-color: var(--muted);
|
||||||
|
--ag-border-color: var(--border);
|
||||||
|
--ag-row-hover-color: var(--accent);
|
||||||
|
--ag-font-family: var(--font-sans);
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Importante**: En Vite, importar AG Grid CSS directamente en `main.ts` (no con `@import` en CSS), porque Vite procesa mejor los imports JS:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// main.ts
|
||||||
|
import 'ag-grid-community/styles/ag-grid.css'
|
||||||
|
import './assets/ag-grid-alpha.css'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Para RUMBO**: Usar AG Grid para tablas de HUs, Work Items, métricas. El tema se reutiliza cambiando solo el nombre de clase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.5 SVG Logo Inline vs `<img>`
|
||||||
|
|
||||||
|
**Problema**: El SVG del logo de Alpha se veía negro en tema oscuro. `<img>` no permite `currentColor`.
|
||||||
|
|
||||||
|
**Intentos**:
|
||||||
|
1. `<img class="dark:invert">` — no funcionó en todos los casos
|
||||||
|
2. Componente Vue inline con `fill="currentColor"` — funcionó pero era complejo
|
||||||
|
3. Al final, `<img class="dark:invert">` fue suficiente una vez corregido el path
|
||||||
|
|
||||||
|
**Lección**: Para SVGs simples, `<img>` con `dark:invert` basta. Para SVGs que necesitan heredar color del tema, crear componente Vue con el SVG inline usando `fill="currentColor"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Decisiones de Arquitectura
|
||||||
|
|
||||||
|
### 2.1 SQLite local-first con sync a remoto
|
||||||
|
|
||||||
|
| Fuente | Dirección | Frecuencia |
|
||||||
|
|--------|-----------|------------|
|
||||||
|
| KAPPA → Turso | Pull | On-demand (al abrir vista) |
|
||||||
|
| KAPPA ← Turso | Push | Solo campos locales (rol, célula) |
|
||||||
|
| Turso ↔ UI | Bidireccional | En tiempo real (reactivo) |
|
||||||
|
|
||||||
|
**Para RUMBO**: Mismo modelo. La fuente de verdad es el archivo local. KAPPA es solo un espejo para compartir datos entre máquinas.
|
||||||
|
|
||||||
|
### 2.2 Limpieza de datos en el frontend
|
||||||
|
|
||||||
|
**Decisión**: Limpiar HTML en TypeScript (no en Rust).
|
||||||
|
|
||||||
|
**Razón**:
|
||||||
|
- Las APIs externas (KAPPA) se consumen desde TS vía `fetch()`
|
||||||
|
- La limpieza es transformación de presentación, no de negocio
|
||||||
|
- Rust solo recibe datos ya limpios para persistir
|
||||||
|
|
||||||
|
**Para RUMBO**: Limpiar en el pipeline de ingesta (TypeScript), antes de pasar a Rust para transcripción o a OpenRouter para análisis.
|
||||||
|
|
||||||
|
### 2.3 Tablas separadas vs JSON embebido
|
||||||
|
|
||||||
|
**Decisión**: Usar tablas relacionales para usuarios, células, proyectos. No JSON embebido.
|
||||||
|
|
||||||
|
**Razón**:
|
||||||
|
- SQLite soporta JOINs eficientes
|
||||||
|
- Los datos deben ser consultables desde DBeaver/externos
|
||||||
|
- Facilita reportes y métricas (SQL directo)
|
||||||
|
|
||||||
|
**Para RUMBO**: Mantener schema relacional. No caer en "guardar todo como JSON" por comodidad.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Patrones de UI
|
||||||
|
|
||||||
|
### 3.1 Card con degradado sutil
|
||||||
|
|
||||||
|
```html
|
||||||
|
<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">
|
||||||
|
```
|
||||||
|
|
||||||
|
Degradado en modo claro, color sólido en oscuro. Tomado del bloque `dashboard-01` de shadcn.
|
||||||
|
|
||||||
|
### 3.2 Layout de vista: Stats → Cards → Tabla
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ Header (título + badge) ──────────┐
|
||||||
|
├─ Stats (tarjetas por categoría) ───┤
|
||||||
|
├─ Cards (vista principal) ──────────┤
|
||||||
|
└─ AG Grid (tabla completa) ─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Aplicado en UsersView. Reutilizable para HUs, proyectos, documentos.
|
||||||
|
|
||||||
|
### 3.3 Badges de rol por color
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const badgeColors: Record<string, string> = {
|
||||||
|
BA: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
|
||||||
|
DEV: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',
|
||||||
|
PM: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400',
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Para RUMBO**: Mismo patrón para estados de HU (backlog, in progress, done), prioridades, tipos de work item.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Lecciones aprendidas
|
||||||
|
|
||||||
|
### 4.1 Rust warnings no son errores
|
||||||
|
|
||||||
|
Los warnings de Rust (variables no usadas, structs sin construir) no impiden que la app compile y ejecute. Solo indican código que puede limpiarse.
|
||||||
|
|
||||||
|
### 4.2 DBeaver + SQLite con espacios en path
|
||||||
|
|
||||||
|
macOS con home en volumen externo produce paths con espacios. DBeaver requiere path sin espacios. Solución: symlink a `/tmp/`.
|
||||||
|
|
||||||
|
### 4.3 AG Grid en Vite
|
||||||
|
|
||||||
|
El CSS de AG Grid debe importarse en `main.ts` (JS import), no con `@import` en CSS. La razón es que Vite resuelve mejor los módulos JS que los `@import` de node_modules.
|
||||||
|
|
||||||
|
### 4.4 iCloud sync no es git
|
||||||
|
|
||||||
|
Los archivos en iCloud se sincronizan automáticamente, pero `node_modules/` y `src-tauri/target/` deben estar en `.gitignore`. Cada máquina necesita `bun install` y `cargo build` propios.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Lo que NO funcionó
|
||||||
|
|
||||||
|
| Intento | Resultado | Alternativa |
|
||||||
|
|---------|-----------|-------------|
|
||||||
|
| DOMParser para limpiar HTML | Requiere DOM, error en SSR | Regex simple (`stripHtml`) |
|
||||||
|
| `@import` de AG Grid en CSS | Vite no lo resolvió | `import` en `main.ts` |
|
||||||
|
| `sharp` con Bun | Error de binario nativo | Python PIL (ya instalado) |
|
||||||
|
| Pasar SVG como componente | Complejo, muchos paths | `<img>` con `dark:invert` |
|
||||||
|
| Driver LibSQL en DBeaver | Pide token (es para Cloud) | Driver SQLite clásico |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Checklist para RUMBO
|
||||||
|
|
||||||
|
Cosas que ya están resueltas en Alpha y RUMBO debe heredar:
|
||||||
|
|
||||||
|
- [ ] Utilidad `stripHtml()` (`clean-html.ts`)
|
||||||
|
- [ ] Patrón `tauri-db.ts` (puente tipado Rust↔TS)
|
||||||
|
- [ ] Tema AG Grid + shadcn (`ag-grid-alpha.css` → `ag-grid-rumbo.css`)
|
||||||
|
- [ ] Store pattern con merge remoto + local
|
||||||
|
- [ ] Schema SQLite relacional (no JSON)
|
||||||
|
- [ ] Card con degradado sutil
|
||||||
|
- [ ] Badges de estado/rol por color
|
||||||
|
- [ ] Layout Stats → Cards → Tabla
|
||||||
|
- [ ] Import AG Grid CSS en `main.ts`
|
||||||
|
- [ ] SVG logo con `dark:invert`
|
||||||
|
- [ ] Documentación de métricas PMI (`metricas_pmi.md`)
|
||||||
Reference in New Issue
Block a user