Skip to content

feat: [TESIS-150] choose the category of a product when creating or editing it - #66

Merged
LauAubert merged 3 commits into
masterfrom
TESIS-999011-product-category-in-modals
Oct 5, 2026
Merged

LauAubert merged 3 commits into
masterfrom
TESIS-999011-product-category-in-modals

Conversation

@LauAubert

@LauAubert LauAubert commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Ticket de Jira

https://proyectofinalfrlp.atlassian.net/browse/TESIS-150

ID provisorio: se reemplaza por la clave real al cargar la card en Jira. Card: cards/011.md.


Descripción

La API guarda y valida la categoría de un producto desde TESIS-102 y expone el vocabulario en GET /api/v1/products/categories, pero los dos modales de producto seguían mostrando un campo deshabilitado con «Pendiente de backend: el producto todavía no tiene categoría». Resultado: ningún producto podía recibir una categoría desde la app, y la columna Categoría del catálogo sólo mostraba lo que venía de los seeds. Este PR habilita el campo en el alta y en la edición.

Decisiones que conviene mirar:

El vocabulario sale de la API. useCategories pide GET /products/categories una vez por sesión (staleTime infinito). Sumar una categoría sigue siendo una línea en Product::CATEGORIES y aparece en los selects sin tocar el front.

Un solo campo para los dos modales (Regla de Dos): CategoryField, controlado. El modal de edición se rellena con reset al abrir; con un select registrado (no controlado), al reabrir seguiría mostrando lo que tenía antes. Con Controller, lo que se ve es siempre el valor del formulario.

No borrar por accidente. Si el producto tiene una categoría que la lista no incluye (la lista todavía no respondió, o la categoría salió del vocabulario), se ofrece igual: sin eso el select la mostraría vacía y guardar la borraría sin que nadie lo haya pedido.

'' en el formulario, null en la API. Un <select> no tiene null: «Sin categoría» es '' en el schema y viaja como null, que es lo que la columna admite.

  • Agrega fetchCategories, inventoryKeys.categories() y useCategories.
  • Agrega components/CategoryField/ y lo usa en CreateProductModal y EditProductModal.
  • Suma category a los schemas, a CreateProductPayload/UpdateProductPayload y a Product/ApiProduct; buildCreatePayload y buildUpdatePayload la mandan.
  • InventoryPage y ProductDetailPage pasan las categorías a sus modales.
  • Cambia el helper del campo por «Se usa para agrupar y filtrar el catálogo».
  • Tests: el campo en el modal de edición (abre con la del producto, vocabulario, elegir, quitar) y los payloads.

Evidencia visual

Pendiente de captura con la API levantada.


Cómo probar

Precondición: API de master, bin/rails db:seed, login con un usuario de Norte.

  1. Inventario → «Nuevo producto» → el campo Categoría ofrece «Sin categoría», Electronics, Machinery, Cabling y Power.
  2. Crear un producto con «Power» → en el catálogo, su columna Categoría dice Power.
  3. Abrir su detalle → «Editar producto» → la categoría abre en Power. Cambiarla a «Sin categoría» y guardar → el catálogo muestra «Sin categoría».

Verificación: npm run test (683 tests, 0 fallas), npm run lint, npm run format:check y npm run build limpios.


Impacto y consideraciones

¿Introduce breaking changes?
No

¿Requiere nuevas variables de entorno?
No

¿Afecta la arquitectura o genera un nuevo patrón?
No.

Conflicto esperable con proyecto-web#60 (card 002): esa rama también agrega category a Product y ApiProduct (junto con otros campos del detalle) y toca los mismos fixtures de tests. Se resuelve quedándose con la unión de los dos. Los comentarios y el copy del detalle que todavía dicen que la categoría no existe (buildSpecs, detail.specs.pendingBackend) los corrige #60, por eso no se tocan acá.

🤖 Generated with Claude Code

…r editing it

The API stores and validates the category since TESIS-102 and exposes its
vocabulary at GET /products/categories, but both product modals still showed
a disabled field saying the backend had no category. No product could get a
category from the app, so the catalog column and filter had nothing to show.

Both modals now offer the categories of the API plus "no category", through a
shared controlled CategoryField. An empty choice travels as null. A category
the product already has but the list does not include is still offered, so
saving never clears it by accident.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TomasMartin2004

Copy link
Copy Markdown
Contributor

Revisado. Lo veo bien para implementar.

Es barato y saca un cartel que hoy es mentira: los dos modales de producto muestran un campo deshabilitado que dice «Pendiente de backend: el producto todavía no tiene categoría», cuando la API guarda y valida la categoría desde TESIS-102 y expone el vocabulario en GET /api/v1/products/categories justamente «para que el modal de alta y el filtro del listado no repitan la lista». Resultado actual: ninguna categoría se puede asignar desde la app, y la columna Categoría del catálogo sólo muestra lo que vino de los seeds.

Lo que verifiqué

  • categoryOf('') → null es el detalle que importa y está resuelto donde corresponde (en utils/payload.ts, no en el componente): un <select> no tiene null, y la API espera null para «sin categoría». Confirmé contra el modelo: validates :category, inclusion: { in: CATEGORIES }, allow_nil: true, así que el null es válido y el string vacío no lo sería.
  • Se aplica a los dos caminos, alta y edición, con el mismo helper. Era la forma obvia de que uno quedara distinto del otro.
  • CategoryField sale como componente propio con sus tipos, consumido por los dos modales, y el vocabulario se lee con useCategories en lugar de una lista repetida en el front.
  • architecture.md actualizado.

Antes de mergear

El PR está CONFLICTING contra master: lo dejó así el merge de TESIS-139 (pantalla de integraciones de sólo lectura), que también toca inventory. Hay que rebasar.

Nota aparte: proyecto-web#67 (filtro del catálogo por categoría) está apilado sobre esta rama. Ese no lo mergearía para esta entrega, así que si #67 queda afuera conviene que este deje de depender de aquel orden.

@LauAubert LauAubert changed the title feat: [TESIS-999011] choose the category of a product when creating or editing it feat: [TESIS-150] choose the category of a product when creating or editing it Oct 3, 2026
@LauAubert LauAubert closed this Oct 3, 2026
@LauAubert
LauAubert deleted the TESIS-999011-product-category-in-modals branch October 3, 2026 23:19
@LauAubert
LauAubert restored the TESIS-999011-product-category-in-modals branch October 3, 2026 23:22
@LauAubert LauAubert reopened this Oct 3, 2026
@LauAubert
LauAubert marked this pull request as ready for review October 3, 2026 23:26
@LauAubert
LauAubert requested a review from a team as a code owner October 3, 2026 23:26
@LauAubert
LauAubert requested review from TomasMartin2004 and removed request for a team October 3, 2026 23:26

@TomasMartin2004 TomasMartin2004 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revisión del diff completo. El código es idéntico al que leí cuando los PRs estaban en draft —ningún commit nuevo—, así que lo que sigue es el veredicto formal.

✅ Aprobado

Barato y saca un cartel que hoy es mentira: los dos modales de producto muestran un campo deshabilitado que dice «Pendiente de backend: el producto todavía no tiene categoría», cuando la API la guarda y valida desde TESIS-102 y expone el vocabulario en GET /api/v1/products/categories justamente «para que el modal de alta y el filtro del listado no repitan la lista».

Lo que verifiqué

  • categoryOf('') → null es el detalle que importa y está resuelto donde corresponde (en utils/payload.ts, no en el componente). Confirmé contra el modelo: validates :category, inclusion: { in: CATEGORIES }, allow_nil: true, así que el null es válido y el string vacío no lo sería.
  • Se aplica a los dos caminos, alta y edición, con el mismo helper. Era la forma obvia de que uno quedara distinto del otro.
  • CategoryField sale como componente propio, y el vocabulario se lee con useCategories en vez de una lista repetida en el front.

⚠️ Antes de mergear

  1. CONFLICTING por el merge de TESIS-139, que también toca inventory. Hay que rebasar.
  2. proyecto-web#67 está apilado sobre esta rama. Si #67 queda afuera, conviene desapilarlo para que no quede colgando de una rama ya mergeada.
  3. Roce menor con mi proyecto-web#79, que muestra la categoría en el detalle. Son archivos distintos, pero conviene mergear una y rebasar la otra.

@LauAubert
LauAubert merged commit 58dba98 into master Oct 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants