Files
qwen3-6-lora/data/raw/sanitized/plans/podemos-realizar-el-plan-expressive-crown.md

101 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GFIBER-603 — Make the UI fully responsive
## Context
Jira **GFIBER-603** (High priority, "Tarea", assigned to a.barrientos, In Development):
> The team has observed during shadowing that agents normally have multiple windows open on the same screen. Upsell Evaluator would usually occupy between 1/4 and 1/3 of the total width. The app should be adaptable so that it's perfectly usable in these sizes.
>
> **Acceptance Criteria:**
> - The app is fully responsive
> - All input fields are easily accessible and readable from 1/3 and 1/4 of the screen width
**This is not mobile-phone responsiveness** — it's a desktop browser window resized narrow (≈320640px: 1/4 of a 1366px laptop ≈340px, 1/3 of a 1920px monitor ≈640px), mouse-driven, used by call-center agents running the Upsell Evaluator alongside CRM/phone software. That range sits entirely below Tailwind's default `sm` breakpoint (640px), so a mobile-first stacking approach (unstyled/base = narrow, `sm:`+ = normal desktop) fits naturally with **zero custom breakpoints needed** — the project has no `tailwind.config.js` (Tailwind v4, CSS-based config, defaults `sm:640 md:768 lg:1024 xl:1280` untouched).
The repo has an established design system (`packages/design-system/`, 51 components, consumed via `transpilePackages` from source, DS-first convention: new/changed UI goes in the DS with story+Jest+Cypress). Investigation (Explore agent + manual verification) found the actual defects are **app-level compositions** (inline styles / missing responsive classes in page/NavBar code), not DS component bugs — `FlowStepper`, `TextField`, `Select`, and `Textarea` were all checked and are already width-agnostic (no fixed-px widths). So **no new DS components are required**; fixes are Tailwind-class changes in existing app files, consistent with the DS-first convention (these are app-level compositions with no 1:1 DS component, same category as `UpsellEvaluator.tsx`'s existing `upsell.css`).
## Scope
Per user decision, this covers the full AC-literal reading ("the app is fully responsive") — Upsell Evaluator/My Usage **and** ranking/admin grids, not just the narrow-window tool.
1. `app/(protected)/upsell-evaluator/_flow/UpsellEvaluator.tsx` (lines 183218) — the core two-column flow shell, currently **inline `style={{}}`**, zero responsive behavior: outer wrapper `maxWidth: 1080`, `<aside>` `width: 220, flexShrink: 0, position: sticky`, `<main>` `flex: 1`. This is the highest-priority fix — it's the actual tool agents run in the narrow window.
2. `components/NavBar.tsx` — present on every page including Upsell Evaluator. Single always-visible flex row (brand + 24 links + up to 5 right-side icons: StreakBell, AttendanceBell, ThemeToggle, LogoutButton, TourButton), only one responsive class in the whole file. Will overflow/clip at 320480px. Fixed via a collapsible hamburger menu (see below).
3. `app/(protected)/upsell-evaluator/_history/TicketDetailView.tsx:44` (`grid-cols-2`) and `SessionHistoryView.tsx:119` (`grid-cols-3`) — confirmed (via `grep`) these render under `/my-metrics` (`app/(protected)/my-metrics/page.tsx` and `[id]/page.tsx`), reached from the "My Usage" NavBar link right next to Upsell Evaluator.
4. Two dashboard tables missing `overflow-x-auto` (have `overflow-y-auto` only, forcing page-level horizontal overflow instead of a contained scroll): `app/(protected)/dashboard/page.tsx:607-608` (`min-w-[760px]`) and `components/dashboard/AllTeamsCard.tsx:321-322` (`min-w-[700px]`). One-line fix each, matching the already-correct sibling `components/dashboard/ValidationsCard.tsx:123-124` (`overflow-x-auto` + `min-w-[600px]`).
5. `components/ranking/LeaguesLadder.tsx:96` (`grid-cols-4`) and `components/ranking/RankingConfigPanel.tsx:169,202,241` (`grid-cols-2` ×3) — add responsive variants (e.g. `grid-cols-2 sm:grid-cols-4` / `grid-cols-1 sm:grid-cols-2`) so these admin/ranking panels also reflow instead of squeezing at narrow widths.
**Verify-only, no code change expected** (check visually during QA, fix only if actually broken): `components/upsell/CopilotWidget.tsx` (`w-[380px] max-w-[calc(100vw-2rem)]` — already viewport-clamped) and `components/NavBar/StreakBell.tsx:107` (`w-64` popover anchored `right-0` inside the icon cluster).
## Implementation approach
### 1. `UpsellEvaluator.tsx` — convert inline styles to Tailwind, stack below `sm`
Replace the inline-styled shell (lines 183193) with:
```tsx
<div className="upsell-flow" style={{ minHeight: "calc(100vh - 57px)" }}>
<div className="mx-auto flex max-w-[1080px] flex-col items-stretch gap-4 p-4 sm:flex-row sm:items-start sm:gap-6 sm:p-6">
<aside aria-label="Flow navigation" className="w-full flex-shrink-0 sm:sticky sm:top-4 sm:w-[220px]">
<FlowStepper .../>
<div className="mt-3"><Button variant="ghost" onClick={newCall}>...</Button></div>
{blocked && <div className="mt-3"><Badge variant="danger">Hard stop active</Badge></div>}
</aside>
<main className="min-w-0 flex-1">...</main>
</div>
```
Below `sm` (640px): aside goes full-width and stacks above main content. At `sm:` and up: reverts to today's sticky 220px sidebar + flex-1 main — no visual change for normal desktop use. `FlowStepper` needs no changes (already `flex-direction: column; width: 100%`, no fixed-px widths); the narrowness problem was entirely the parent `<aside>`'s hardcoded width. Leave the `saveToast` inline style block (lines 160181) as-is unless touching it is free — not a responsiveness bug (centered fixed toast is width-agnostic).
### 2. `NavBar.tsx` — make the existing (orphaned) DS `AppHeader` component responsive and adopt it
Per user decision, the responsive collapse behavior itself belongs in the design system as a real component, not composed ad hoc in the app. `packages/design-system/src/components/AppHeader/index.tsx` already exists with almost exactly the right shape (`brand?: ReactNode`, `nav?: ...`, `actions?: ReactNode`) but is presentational, non-responsive (fixed `height: 64px` row, no collapse), and **unused anywhere in the app** (`grep` confirms zero imports outside its own Storybook/tests) — so there's no adoption-migration risk, and enhancing it in place is strictly additive.
**DS-side changes (`packages/design-system/src/components/AppHeader/`):**
- Widen the `nav` prop from a structured `{id,label,href,active}[]` array to `ReactNode` (a slot, same shape as `actions` already is). This is the key change the user is asking for: the DS component stops knowing about hrefs/active-state/routing and just arranges *whatever nav content the app hands it* — so `NavBar.tsx` keeps building its role-gated links with `next/link`'s `Link` (prefetch, client-side nav) exactly as it does today, and simply passes that finished JSX in as a prop instead of AppHeader re-rendering plain `<a>` tags.
- Add `"use client"` + internal `open` state (mobile menu). Reuse the sibling `Drawer` component from the same package (`import { Drawer } from '../Drawer'` or via the barrel) — no new slide-over logic needed, `Drawer` already has focus-trap, Tab-trap, Escape, backdrop-close, and focus-restore.
- `style.module.scss`: add a `≥768px` media query (matching Tailwind's `md`) that shows the existing full-width `.nav`/`.actions` row and hides a new hamburger `.trigger` button; below `768px`, hide `.nav`/`.actions` from the row and show `.trigger`, which opens the `Drawer` containing the same `nav` and `actions` nodes.
- Add the standard DS file set for this change: update `AppHeader.stories.tsx` (add a narrow-viewport/mobile story), `AppHeader.test.tsx` (Jest: trigger opens Drawer, Escape/backdrop closes it, content renders in both desktop and mobile paths), `AppHeader.test.cy.tsx` (Cypress: real viewport resize + click-through + focus-trap), per the repo's DS-first convention (every component ships with story + Jest + Cypress).
**App-side changes (`components/NavBar.tsx`):**
- Stays a Server Component — all existing responsibilities (role gating via `profile`, building `Link` elements, composing `StreakBell`/`AttendanceBell`/`ThemeToggle`/`LogoutButton`/`TourButton`) are unchanged. **Confirmed division of responsibility:** `NavBar.tsx` decides *what* to show (which links per role, which icons); `AppHeader` only decides *how* to arrange it responsively. `AppHeader` never sees `Profile`/Supabase types — this preserves the DS = UI / app = business-logic boundary already established in GFIBER-585 (Paso 3 — "DS = estilos/UI; app = lógica de negocio").
- Replace the current hand-rolled `<nav>`/`<div>` markup with `<AppHeader brand={...} nav={<>...existing Link elements...</>} actions={<>...existing icon cluster...</>} />` imported from `@gfiber/design-system`. Passing server-built JSX as props into a client component (`AppHeader`) is a standard, supported RSC pattern (the children/props aren't re-executed on the client) — no NavBar behavior changes.
- Considered and rejected: reusing `KebabMenu` (wrong semantics/shape for primary nav — actions-menu, not nav, no `next/link` support) and building a brand-new, separate DS component instead of enhancing `AppHeader` (would leave `AppHeader` as dead, non-representative Penpot-sourced code and create two overlapping "app header" components in the DS).
### 3. `TicketDetailView.tsx` / `SessionHistoryView.tsx` grids
`grid-cols-2``grid-cols-1 sm:grid-cols-2` (TicketDetailView:44); `grid-cols-3``grid-cols-1 sm:grid-cols-3` (SessionHistoryView:119) — adjust to `sm:grid-cols-2 lg:grid-cols-3` instead if 3-up reads cramped exactly at 640px during QA.
### 4. Dashboard tables
```diff
- <div className="max-h-[460px] overflow-y-auto">
+ <div className="max-h-[460px] overflow-x-auto overflow-y-auto">
```
Apply to both `app/(protected)/dashboard/page.tsx:607-608` and `components/dashboard/AllTeamsCard.tsx:321-322`.
## Verification
- **Vitest + Testing Library** (existing suites for `UpsellEvaluator.tsx`, `NavBar.tsx`, `TicketDetailView`/`SessionHistoryView`): keep existing behavioral tests green (stage transitions, role-gated nav links render correctly into the `nav`/`actions` props); don't add tests whose only assertion is "this className string exists" — low value, jsdom has no real layout engine.
- **Design-system (Jest + Testing Library + Cypress)** for the enhanced `AppHeader`: Jest test for open/close/focus-trap/Escape/backdrop-close and that `nav`/`actions` content renders identically in both the desktop row and the mobile Drawer; Cypress test with real viewport resize (e.g. 340/480/768px) verifying the trigger appears/disappears at the right breakpoint and the drawer is fully interactive. Update `AppHeader.stories.tsx` with a mobile/narrow story so it's visually reviewable in Storybook.
- **Cypress E2E** (app-level, existing Upsell Evaluator flow spec): add `cy.viewport(340, 720)` / `cy.viewport(480, 720)` passes, re-running the same GAID lookup → stages → close interactions to catch real click-target/overlap regressions, including opening the header's mobile menu and navigating via it.
- **qa-validator (real browser)** — the concrete check for the AC. Resize to **340px, 480px, and 640px** (not just one width) and confirm:
1. Upsell Evaluator: stepper stacks above stage content, no squeeze; every input/select/textarea fully visible, not clipped; all buttons (New Call, Continue/Back, ObjectionDrawer trigger) reachable without horizontal scroll.
2. NavBar: below `md:`, only brand + hamburger trigger show; opening the menu reveals all links + icon cluster in the Drawer with working focus-trap/Escape/backdrop-close; nothing clipped or unreachable.
3. `/my-metrics` and `/my-metrics/[id]`: grids stack to 1 column instead of squeezing.
4. `/dashboard`: the two fixed tables scroll horizontally within their own box instead of blowing out page width.
5. `/ranking` and admin config panels: grids reflow instead of squeezing.
6. Verify-only items: `CopilotWidget` panel and `StreakBell` popover (now inside the Drawer) don't clip at 320340px.
7. Screenshots at each width for the PR description, as evidence against the AC.
## Suggested commit/PR breakdown
Single PR (`feat/gfiber-603-responsive-upsell-evaluator`):
1. `UpsellEvaluator.tsx` shell → Tailwind + stacking.
2. `packages/design-system/src/components/AppHeader/` → responsive rework (`nav`/`actions` widened to `ReactNode`, `"use client"`, internal Drawer-based mobile menu, updated story/Jest/Cypress).
3. `components/NavBar.tsx` → adopt `AppHeader` from `@gfiber/design-system`, passing existing role-gated `Link` elements and icon cluster as `nav`/`actions` props (server-component responsibilities unchanged).
4. `TicketDetailView.tsx` + `SessionHistoryView.tsx` → responsive grids.
5. `dashboard/page.tsx` + `AllTeamsCard.tsx``overflow-x-auto`.
6. `ranking/LeaguesLadder.tsx` + `ranking/RankingConfigPanel.tsx` → responsive `grid-cols-N`.
7. DS Jest/Cypress/Storybook updates for `AppHeader`; app Cypress narrow-viewport passes (including mobile-menu navigation via the new header).
8. qa-validator pass at 340/480/640px across Upsell Evaluator, My Usage, Dashboard, and Ranking/Admin pages; screenshots attached to PR description.