Skip to content

Commit 1b76a32

Browse files
Alex Fuentesclaude
andcommitted
Add Mercadona logo SVG and ui-improvements plan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6087112 commit 1b76a32

File tree

2 files changed

+309
-0
lines changed

2 files changed

+309
-0
lines changed

.aiplan/ui-improvements-plan.md

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
# Plan: UI Improvements - Logo, NutriScore, Footer
2+
3+
## Contexto
4+
5+
El workshop está completo (5 iteraciones, 13 tests, visual-fixes-v2 aplicado). Este plan
6+
implementa un lote de mejoras UI: logo de Mercadona en cabecera, extracción del componente
7+
NutriScore a componente reutilizable, logo clicable con test dedicado, y footer disclaimer
8+
para demos.
9+
10+
**Decisiones tomadas:**
11+
- NutriScore: badge simple (extraer a componente, mantener visual actual de pill coloreada)
12+
- Logo clicable: test dedicado nuevo en iteration-3-solution
13+
- Footer: sin test, solo para demo
14+
15+
---
16+
17+
## F0: Cambios en master
18+
19+
### F0.1 Logo Mercadona en Navigation
20+
- [x] Reemplazar `<h1 className="navigation__title">TDD Workshop</h1>` por `<img>`
21+
- [x] Usar `<img src="/mercadona-logo.svg" alt="Mercadona" className="navigation__logo" />`
22+
- [x] Añadir CSS: `.navigation__logo { max-width: 230px; height: auto; }`
23+
- [x] Eliminar `.navigation__title` del CSS (ya no se usa)
24+
25+
**Archivos:**
26+
- `src/components/navigation/Navigation.tsx`
27+
- `src/components/navigation/Navigation.css`
28+
29+
### F0.2 Footer disclaimer en App
30+
- [x] Añadir `<footer>` después de `<main>` en `app.tsx`
31+
- [x] Texto: "La información de los productos ha sido generada con IA y no refleja datos reales. Solo con fines de testing."
32+
- [x] Crear estilos en `src/styles/globals.css` (estilo discreto, gris, centrado, font-size small)
33+
34+
**Archivos:**
35+
- `src/app.tsx`
36+
- `src/styles/globals.css` (añadir estilos de `.disclaimer`)
37+
38+
### F0.3 Commit y verificación
39+
- [x] `npm run typecheck && npm run lint && npm test` en master
40+
- [ ] Verificar visualmente que el logo se muestra correctamente
41+
- [x] Commit
42+
43+
---
44+
45+
## F1: Cascade master → todas las ramas
46+
47+
Merge en cadena. En cada paso resolver conflictos si los hay.
48+
49+
- [x] `master``iteration-1-start`
50+
- [x] `iteration-1-start``iteration-1-solution`
51+
- [x] `iteration-1-solution``iteration-2-start`
52+
- [x] `iteration-2-start``iteration-2-solution`
53+
- [x] `iteration-2-solution``iteration-3-start`
54+
- [x] `iteration-3-start``iteration-3-solution`
55+
- [x] `iteration-3-solution``iteration-4-start`
56+
- [x] `iteration-4-start``iteration-4-solution`
57+
- [x] `iteration-4-solution``iteration-5-start`
58+
- [x] `iteration-5-start``iteration-5-solution`
59+
60+
**Conflictos esperados:**
61+
- `app.tsx` en iter-3+ (master tiene `<>header+main+footer</>`, iter-3+ tiene `<AppRoutes />`).
62+
Resolución: mover footer a `RootLayout.tsx` en iter-3+.
63+
64+
**Verificación por rama:** `npm run typecheck && npm test` tras cada merge.
65+
66+
---
67+
68+
## F2: Componente NutriScore en iteration-2-solution
69+
70+
### F2.1 Crear componente
71+
- [x] Crear `src/components/nutri-score/NutriScore.tsx`
72+
- [x] Crear `src/components/nutri-score/NutriScore.css` (mover estilos de nutriscore desde ProductCard.css)
73+
- [x] Crear `src/components/nutri-score/index.ts` (barrel export)
74+
75+
**NutriScore.tsx:**
76+
```tsx
77+
import classNames from 'classnames'
78+
import './NutriScore.css'
79+
80+
interface NutriScoreProps {
81+
score: string
82+
showLabel?: boolean
83+
}
84+
85+
export const NutriScore = ({ score, showLabel = false }: NutriScoreProps) => (
86+
<span className={classNames('nutri-score', `nutri-score--${score.toLowerCase()}`)}>
87+
{showLabel && 'Nutriscore: '}{score}
88+
</span>
89+
)
90+
```
91+
92+
**NutriScore.css** (extraído de ProductCard.css):
93+
```css
94+
.nutri-score {
95+
display: inline-flex;
96+
align-items: center;
97+
justify-content: center;
98+
padding: var(--spacing-xs) var(--spacing-sm);
99+
border-radius: var(--border-radius-sm);
100+
font-size: var(--font-size-sm);
101+
font-weight: 700;
102+
color: var(--color-white);
103+
text-transform: uppercase;
104+
}
105+
106+
.nutri-score--a { background-color: #038141; }
107+
.nutri-score--b { background-color: #85bb2f; }
108+
.nutri-score--c { background-color: #fecb02; color: var(--color-text); }
109+
.nutri-score--d { background-color: #ee8100; }
110+
.nutri-score--e { background-color: #e63e11; }
111+
```
112+
113+
### F2.2 Refactor ProductCard
114+
- [x] Reemplazar el `<span>` de nutriscore por `<NutriScore score={nutriscore} />`
115+
- [x] Eliminar estilos `.product-card__nutriscore*` de `ProductCard.css`
116+
117+
**En iter-2 (badge siempre visible):**
118+
```tsx
119+
<NutriScore score={nutriscore} />
120+
```
121+
122+
**Archivos:**
123+
- `src/components/nutri-score/NutriScore.tsx` (nuevo)
124+
- `src/components/nutri-score/NutriScore.css` (nuevo)
125+
- `src/components/nutri-score/index.ts` (nuevo)
126+
- `src/components/product-card/ProductCard.tsx` (refactor)
127+
- `src/components/product-card/ProductCard.css` (eliminar estilos nutriscore)
128+
129+
### F2.3 Verificación y commit
130+
- [x] `npm run typecheck && npm test` en iteration-2-solution
131+
- [x] Commit
132+
133+
---
134+
135+
## F3: Cascade iteration-2-solution → ramas siguientes
136+
137+
- [x] `iteration-2-solution``iteration-3-start`
138+
- [x] `iteration-3-start``iteration-3-solution`
139+
- [x] `iteration-3-solution``iteration-4-start`
140+
- [x] `iteration-4-start``iteration-4-solution`
141+
- [x] `iteration-4-solution``iteration-5-start`
142+
- [x] `iteration-5-start``iteration-5-solution`
143+
144+
**Conflictos esperados:**
145+
- `ProductCard.tsx` en iter-4+ (iter-4 usa `showLabel` con `isListView`).
146+
Resolución: usar `<NutriScore score={nutriscore} showLabel />` en la versión condicional.
147+
- `ProductCard.css` en todas: los estilos nutriscore se eliminaron, resolver a favor de la eliminación.
148+
149+
**Verificación:** `npm run typecheck && npm test` tras cada merge.
150+
151+
---
152+
153+
## F4: Logo clicable + test en iteration-3-solution
154+
155+
### F4.1 Link en el logo
156+
- [x] En `Navigation.tsx` de iter-3-solution, envolver el logo en `<Link to="/">`
157+
- [x] Añadir CSS para que el link no tenga estilos de anchor
158+
159+
```tsx
160+
import { Link, NavLink } from 'react-router-dom'
161+
162+
// Dentro del JSX:
163+
<Link to="/" className="navigation__logo-link">
164+
<img src="/mercadona-logo.svg" alt="Mercadona" className="navigation__logo" />
165+
</Link>
166+
```
167+
168+
**CSS:**
169+
```css
170+
.navigation__logo-link {
171+
display: inline-flex;
172+
align-items: center;
173+
}
174+
```
175+
176+
### F4.2 Test dedicado
177+
- [x] Añadir test en `src/pages/category-detail/__tests__/CategoryDetail.test.tsx`
178+
(archivo donde viven los tests de navegación en iter-3)
179+
180+
```typescript
181+
it('should navigate to home when clicking the logo', async () => {
182+
const user = userEvent.setup()
183+
render(<App />)
184+
185+
await clickCategory(user, 'Fruta y verdura')
186+
await screen.findByRole('heading', { name: 'Fruta y verdura' })
187+
188+
const logo = screen.getByRole('link', { name: 'Mercadona' })
189+
await user.click(logo)
190+
191+
expect(screen.queryByRole('heading', { name: 'Fruta y verdura' })).not.toBeInTheDocument()
192+
expect(await screen.findByText('Fruta y verdura')).toBeVisible()
193+
})
194+
```
195+
196+
El test:
197+
1. Navega a una categoría (para estar fuera de home)
198+
2. Clickea el logo (link con alt="Mercadona")
199+
3. Verifica que ya no está en la página de categoría (no heading de categoría)
200+
4. Verifica que vuelve a home (categorías visibles como enlaces, no como heading)
201+
202+
**Nota:** En master el logo NO es clicable (Navigation está fuera del Router).
203+
Solo en iter-3+ donde RootLayout está dentro del Router se añade el `<Link>`.
204+
205+
### F4.3 Verificación y commit
206+
- [x] `npm run typecheck && npm test` en iteration-3-solution
207+
- [x] Commit
208+
209+
---
210+
211+
## F5: Cascade iteration-3-solution → ramas siguientes
212+
213+
- [x] `iteration-3-solution``iteration-4-start`
214+
- [x] `iteration-4-start``iteration-4-solution`
215+
- [x] `iteration-4-solution``iteration-5-start`
216+
- [x] `iteration-5-start``iteration-5-solution`
217+
218+
**Verificación:** `npm run typecheck && npm test` tras cada merge.
219+
220+
---
221+
222+
## F6: NutriScore en ProductDetail (iteration-5-solution)
223+
224+
### F6.1 Integrar NutriScore en ProductDetail
225+
- [x] Añadir `<NutriScore score={product.nutriscore} showLabel />` en `ProductDetail.tsx`
226+
- [x] Eliminar los estilos duplicados `.product-detail__nutriscore-badge--*` de `ProductDetail.css`
227+
(ya no se necesitan, el componente trae sus propios estilos)
228+
229+
```tsx
230+
import { NutriScore } from 'components/nutri-score'
231+
232+
// Dentro del JSX, después de price:
233+
{product.nutriscore && <NutriScore score={product.nutriscore} showLabel />}
234+
```
235+
236+
### F6.2 Verificación y commit
237+
- [x] `npm run typecheck && npm test` en iteration-5-solution
238+
- [x] Commit
239+
240+
---
241+
242+
## F7: Push y verificación final
243+
244+
- [x] Push de todas las ramas
245+
- [ ] Verificar que CI pasa (si hay)
246+
- [ ] Revisión visual rápida de:
247+
- master: logo + footer visible
248+
- iter-3: logo clicable → home
249+
- iter-2: NutriScore badge en ProductCard
250+
- iter-4: NutriScore con label en list view
251+
- iter-5: NutriScore en ProductDetail modal
252+
253+
---
254+
255+
## Resumen de archivos afectados
256+
257+
| Archivo | Rama origen | Cambio |
258+
|---------|-------------|--------|
259+
| `src/components/navigation/Navigation.tsx` | master | Logo `<img>` reemplaza `<h1>` |
260+
| `src/components/navigation/Navigation.css` | master | Estilos logo, eliminar title |
261+
| `src/app.tsx` | master | Footer disclaimer |
262+
| `src/styles/globals.css` | master | Estilos disclaimer |
263+
| `src/components/nutri-score/NutriScore.tsx` | iter-2-solution | Componente nuevo |
264+
| `src/components/nutri-score/NutriScore.css` | iter-2-solution | Estilos extraídos |
265+
| `src/components/nutri-score/index.ts` | iter-2-solution | Barrel export |
266+
| `src/components/product-card/ProductCard.tsx` | iter-2-solution | Usa `<NutriScore>` |
267+
| `src/components/product-card/ProductCard.css` | iter-2-solution | Eliminar estilos nutriscore |
268+
| `src/components/navigation/Navigation.tsx` | iter-3-solution | Link en logo |
269+
| `src/components/navigation/Navigation.css` | iter-3-solution | Estilos logo-link |
270+
| `CategoryDetail.test.tsx` | iter-3-solution | Test logo → home |
271+
| `src/pages/routes/RootLayout.tsx` | iter-3-solution+ | Footer (merge conflict) |
272+
| `src/components/product-detail/ProductDetail.tsx` | iter-5-solution | Integra `<NutriScore>` |
273+
| `src/components/product-detail/ProductDetail.css` | iter-5-solution | Eliminar estilos duplicados |
274+
275+
---
276+
277+
## Notas de contexto para implementación
278+
279+
**Estructura de routing (importante para el logo clicable):**
280+
- **master**: `app.tsx` tiene `<>header+main</>` con Navigation FUERA del Router
281+
- **iter-3+**: `app.tsx` tiene solo `<AppRoutes />`, con `RootLayout.tsx` DENTRO del Router
282+
- Por eso el `<Link>` en el logo solo es posible desde iter-3 en adelante
283+
284+
**NutriScore actual:**
285+
- iter-2: badge siempre visible (solo letra), usa template literal para clase CSS
286+
- iter-4: badge condicional solo en list view, muestra "Nutriscore: X"
287+
- iter-5 ProductDetail: NO tiene nutriscore (CSS skeleton existe pero no se usa)
288+
289+
---
290+
291+
## Retrospectiva
292+
293+
### Aprendizajes
294+
- Los merge cascades en cadena son mecánicos pero requieren atención: cada conflicto en Navigation.tsx o ProductCard.tsx tiene un patrón claro (combinar ambas versiones)
295+
- El footer en iter-3+ va en RootLayout, no en app.tsx (la app ya delegó todo al router)
296+
- El test del logo clicable necesita `await screen.findByText(...)` antes de `clickCategory` (mismo patrón que los otros tests de navegación — las categorías son async)
297+
- Import relativo `../../types` en ProductDetail.tsx: corregido de paso a import absoluto
298+
299+
### Fricciones
300+
- Al inicio de la sesión olvidé actualizar el plan mientras avanzaba — el usuario lo señaló y se corrigió el hábito durante la sesión
301+
- CLAUDE.md generó conflicto de merge en iter-1-start: resuelto a favor de master (versión más actualizada)
302+
303+
### Decisiones de código
304+
- `NutriScore` con `showLabel` prop cubre los tres casos: badge solo (iter-2), badge con label (iter-4 list view, iter-5 modal)
305+
- No se añadió lógica extra en iter-5 para el `../../types` — era un fix obvio de calidad
306+
307+
### CLAUDE.md
308+
- Sin cambios necesarios: las convenciones ya cubren todo lo implementado

public/mercadona-logo.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)