From 94e73343d9a66365b4d70e34c828c4d579eb19ab Mon Sep 17 00:00:00 2001 From: Mirko Stamm Date: Sun, 1 Nov 2026 21:02:24 +0000 Subject: [PATCH 1/4] fix(widget): el orbe avisa la falta de credencial con badge y motivo (#214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Badge ámbar (data-kind='action', .orb__badge ya existente) para needsLogin y unconfigured, los estados con acción asociada; se suma al atenuado, no lo reemplaza. - Motivo accesible (aria-label/title) con details.status.* para los tres estados atenuados (needsLogin, unconfigured, error); error sin badge. - Sin cambios en Rust, ventanas, DetailPanel ni catálogos i18n (#230 decidió no integrar el fix transient_for; el badge es la vía mínima). Tests: RED/GREEN en ProviderGauge.test.tsx (4 casos); vitest 904/0, scripts 298/0, typecheck, cargo test --locked 402/0, fmt, inventario i18n --check sin cambios; validación manual en Wayland nativo y GDK_BACKEND=x11 (evidencia en odd/tasks/214-orb-status-badge.md). --- odd/tasks/214-orb-status-badge.md | 57 +++++++++++++++++++++++++++ src/components/ProviderGauge.test.tsx | 34 ++++++++++++++++ src/components/ProviderGauge.tsx | 23 +++++++++-- 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 odd/tasks/214-orb-status-badge.md diff --git a/odd/tasks/214-orb-status-badge.md b/odd/tasks/214-orb-status-badge.md new file mode 100644 index 00000000..50588f00 --- /dev/null +++ b/odd/tasks/214-orb-status-badge.md @@ -0,0 +1,57 @@ +# ODD — #214 B mínima: indicador de falta de credencial en el orbe + +Issue: https://github.com/egdev6/quorb/issues/214 (labels `status:approved`, `type:bug`) +Assignee verificado: SrStamm (Mirko Alexander Stamm). Decisión del mantenedor (#214, 2026-11-01): **no integrar C** (`transient_for`, rama `fix/214-wayland-detail-transient` de #230); ir con la **versión mínima de B**, con el ajuste aprobado: badge ámbar para `needsLogin` **y** `unconfigured`; `error` queda solo con motivo en texto accesible. + +## Diseño aprobado por el mantenedor + +- Badge visual **ámbar** (`data-kind='action'`, ya definido en `src/styles.css:185`) cuando `status === 'needsLogin'` o `status === 'unconfigured'`. Solo los estados que tienen acción asociada (`model.rs:120-124`: `needsLogin → 'login'`, `unconfigured → 'settings'`). +- Se suma al atenuado, no lo reemplaza: el orbe sigue con `is-muted` (clases en `gaugeState`, `ProviderGauge.tsx`). +- `aria-label` y `title` incorporan el motivo para los tres estados atenuados (`needsLogin`, `unconfigured`, `error`) reusando `details.status.*` de `src/i18n/es.ts:477-481` y `src/i18n/en.ts:452-456`. Orden del título: `[nombre, motivo, validez, reinicios]`. +- `title` deja de condicionarse solo a `account || validity || resets`: con motivo presente siempre se incluye. +- Badge decorativo (`aria-hidden`), esquina superior derecha, escala con `calc(9px * var(--k))`. Validar a mano que no choque con el chip de cuenta (`.orb__acct`) ni con `ValidityBadge` en 24/48/56 px. +- Plataformas: **todas** (el estado viene del modelo, no del compositor). Sin código Wayland-específico, sin Rust, sin ventanas nuevas, sin tocar `DetailPanel` ni `widget_placement`. + +## Alcance por archivo + +| Archivo | Cambio | +| --- | --- | +| `src/components/ProviderGauge.tsx` | Badge `` con `needsLogin`/`unconfigured`; `accessibleTitle` incorpora el motivo (`t(locale, 'details.status.')` para estados atenuados); `title` se muestra siempre que haya motivo | +| `src/styles.css` | Sin cambios (`.orb__badge` ya existe; solo si la validación manual pide un ajuste mínimo) | +| `src/i18n/es.ts`, `src/i18n/en.ts` | Sin cambios (claves reutilizadas) | +| `src/components/ProviderGauge.test.tsx` | Tests RED/GREEN del punto de tests | +| `docs/i18n-inventory.md` | Sin cambios | + +No-goals: C (`transient_for`), detalle B completo (panel inline en la tira), cambios Rust/i18n. + +## Tareas + +1. **[x] Tests RED** en `ProviderGauge.test.tsx` + - Rojo observado (vitest): 3 fallidos — falta badge en `needsLogin`/`unconfigured` y motivo en los tres estados atenuados; caso `ok` pasa como guardia de regresión del comportamiento actual. + - Bloque: `describe('ProviderGauge credential status badge')` (4 tests, estilista `renderToStaticMarkup` igual que el resto del archivo). + - La tarea original decía: (estilo `renderToStaticMarkup` existente): + - `needsLogin`: badge ámbar presente + motivo «Sin sesión» en `aria-label`. + - `unconfigured`: badge ámbar presente + motivo. + - `error`: motivo en `aria-label`/`title` pero **sin** badge. + - `ok`: ni badge ni motivo; título igual que hoy. +2. **[x] Implementación** en `src/components/ProviderGauge.tsx`: bloque `STATUS_REASON` (mapa cerrado `needsLogin`/`unconfigured`/`error` → `details.status.*`), `reason` entra al título accesible entre nombre y validez, `title` condicionado también por motivo, badge ` + {/* Badge ámbar (#214): solo los estados con acción; decorativo, el motivo legible va en el título. */} + {/* Sin badge para `error`: la señal queda solo en el texto accesible. */} + {report.status === 'needsLogin' || report.status === 'unconfigured' ? ( +